2011-10-26 25 views
1

伙计!如何让Linq2Sql理解自定义类型?

假设我们已经设置的界面呈现问题”域:IUser, IAddressBook, IComment等。该假设IUser定义如下:

public interface IUser : IAmIdentifiedEntity<int>, IHaveName 
{ 
    string FullName { get; set; } 
    string Email { get; set; } 
    bool ReceiveNotification { get; set; } 
} 

public interface IHaveName 
{ 
    string Name { get; set; } 
} 

在我的应用程序只用提到的合同,例如:

public IUser GetUser(string userName) 
{ 
    return Warehouse.GetRepository<IUser>().GetAll() 
     .First(u => u.Name == userName); 
} 

正如你所看到的,我使用一些网关获取数据。库”方法GetAll()返回IQueryable<TEntity>,所以可以建立一些复杂的查询,并使用延迟加载的所有优势。当我介绍它时,我想到了未来Linq2Sql的应用。

一段时间以来,在开发客户端代码,我们使用“内存”实现数据存储。所以一切正常。但现在是将域绑定到SQL Server的时候了。所以我开始在LINQ2SQL实现所有的基础设施......当制作简单的解决方案(由弗雷德里克Kalseth的article启发),得到了第一个例外,我已经意识到这个想法的所有悲伤...这里是implmentation的IUser

public partial class USER : IUser 
{ 
    int IHaveID<int>.ID 
    { 
     get { return USERID; } 
    } 

    string IHaveName.Name 
    { 
     get { return USERNAME; } 
     set { USERNAME = value; } 
    } 

    string IUser.FullName 
    { 
     get { return USERFULLNAME; } 
     set { USERFULLNAME = value; } 
    } 

    // ... same approach for other properties 
} 

在这里 - 例外:

Exception: System.NotSupportedException: 
    The member 'Data.IHaveName.Name' has no supported translation to SQL. 

这是明显的例外 - LINQ2SQL提供商并不了解外部接口,但我应该怎么解决呢?我不能改变域接口返回Linq.Expression,如:

Expression<Func<string>> IHaveName.Name 
{ 
    get { return (() => USERNAME); } 
    set { USERNAME = value(); } 
} 

,因为它打破了所有的域接口当前的代码使用,而且我不能Expression<Func<int>>取代int因为宗教信仰的:)

也许,我应该写自定义表达式访问者查询与它的内部子AST跳过” AST主叫IUser.SomeProperty和替换它...

你能提供上你的想法?

UPDATE。在这里,我应该写一些关于Repository的实现。看看GetAll()来源:

public IQueryable<TEntity> GetAll() 
{ 
    ITable table = GetTable(); 
    return table.Cast<TEntity>(); 
} 

protected ITable GetTable() 
{ 
    return _dataContext.GetTable(_implType); 
} 

在我的例子,TEntity <=> IUser_implType <=> typeof(USER)

更新2我发现related question,其中OP有一个相似的问题:需要具体的ORM实体和它之间的一些桥梁域实体。我发现有趣的the answer:作者建议创建表达式访问者,它将执行实体之间的转换(ORM < => Domain)。

+0

有什么新建议? – ajukraine

回答

0

看来,我已经找到了一种方法来解决我的问题。今天我发现很好series of post名为“使用Linq的高级域模型查询”。作者提出了他的方式来支持更方便(漂亮)的Lambda语法来处理实体。他使用表达式树转换。所以我会遵循这个方法,如果我成功了,我会发布更详细的答案(也许通过我的博客)。

你觉得这个主意怎么样?我应该花时间实现Expression提供程序吗?

UPDATE。我用自定义表达式vistior失败 - 它需要通过表达式树进行很多转换。所以我决定将所有Linq逻辑转移到“存储”类中,这些类包含复杂的查询和持久性管理。

0

Column属性装饰Name中的属性,这与您使用Linq2SQL的任何其他类型的属性相同。

+0

您是否在编写'Name'时指'IHaveName'? – ajukraine

+0

不,我的意思是名称,实际的类Linq2SQL将在上找到属性。 –

+0

...虽然它可能位于基类中的某个属性上,而不是一个接口,这会在* some *个案例中解决此问题。 –

0

这是一个众所周知的问题,只要你尝试用它不能铸造成一个SQL字符串命令逻辑来执行一个IQueryable LINQ到SQL本身抛出这个错误。

这个问题也出现由于IQueryable的本质 - 延迟执行。

您可以强制的IQueryable的执行如下:

public IUser GetUser(string userName) 
{ 
    return Warehouse.GetRepository<IUser>().GetAll() 
     .AsEnumerable() 
     .First(u => u.Name == userName); 
} 

这将确保您的域名接口可以保持原样,要么在运行时不会给你任何问题。

干杯!

+2

这不是IQueryable的延迟执行问题,(你也可以使用AsEnumerable())延迟执行,但是查询引擎如何执行它是不同的。 Linq2SQL需要关于如何根据数据库进行计算的信息,这不是对象定义固有的。这可以解决这个问题,但需要扫描整个表中的大部分甚至全部,以满足不断的请求。 –

+0

@JoHanna:是的,完全正确。我使用.NET MVC工作了很多,因此使用这种方法可以更简单,并且可以在应用程序级别使用SQL依赖性缓存有问题的表。列属性不能解决这个问题,我知道的唯一另一种替代方法是IQueryable 中的扩展,它将附加参数作为字段插入到通过数据库生成的LINQ-to-SQL类中。 –

+0

因此,如果有30000个用户,并且匹配用户是您要从数据库中检索29999个用户的最后一个,那么您不使用该用户?当然,您可以将表缓存在内存中,甚至可以通过关于用户名的字典进行缓存,以便可以在几乎恒定的时间内检索,但这会导致缓存失效的所有问题。 –

0

您是否尝试过使用泛型? Linq to SQL需要知道具体类型,以便它可以将表达式转换为sql。

public TUser GetUser<TUser>(string userName) where TUser : IUser 
{ 
    return Warehouse.GetRepository<TUser>().GetAll() 
     .First(u => u.Name == userName); 
} 
+0

首先,应用程序对Linq2Sql类一无所知,所以不可能看到像'GetRepository ()'...... – ajukraine

+0

这样的表达式,那么你必须做一些你的映射应用程序类到你的Linq2Sql类型,我不知道,但我知道,但像AutoMapper这样的工具可以让生活更轻松。 – jrummell

相关问题