人们!
假设我们有一组接口提出问题'domain: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上实现所有的基础设施......当得到简单的解决方案(受到Fredrik Kalseth的 article 的启发)并获得第一个例外时,我意识到所有的悲伤这个想法......这是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(); }
}
因为它打破了域接口的所有当前代码使用,而且由于宗教信仰,我无法用int
替换Expression<Func<int>>
:)
也许,我应该编写自定义Expression访问者来跳过查询'IUser.SomeProperty
的AST调用,并用它的内部子AST替换它......
你能提出你的想法吗?
更新。在这里,我应该写一些关于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&lt; =&gt; Domain)。
答案 0 :(得分:0)
使用Name
属性装饰Column
中的属性,与使用Linq2SQL的任何其他类型相同。
答案 1 :(得分:0)
这是一个众所周知的问题,每当您尝试使用不能转换为SQL字符串命令的逻辑执行IQueryable时,LINQ-to-SQL本身就会抛出此错误。
由于IQueryable的性质 - 延迟执行,也会出现此问题。
您可以按如下方式强制执行IQueryable:
public IUser GetUser(string userName)
{
return Warehouse.GetRepository<IUser>().GetAll()
.AsEnumerable()
.First(u => u.Name == userName);
}
这将确保您的域界面可以保持原样,并且在运行时也不会给您带来任何问题。
干杯!
答案 2 :(得分:0)
您是否尝试过使用泛型? Linq to SQL需要知道具体类型,以便它可以将表达式转换为sql。
public TUser GetUser<TUser>(string userName) where TUser : IUser
{
return Warehouse.GetRepository<TUser>().GetAll()
.First(u => u.Name == userName);
}
答案 3 :(得分:0)
看来,我找到了解决问题的方法。今天我发现很好的 series of post 名为“使用Linq的高级域模型查询”。作者提出了支持更方便(漂亮)Lambda语法以与实体一起工作的方法。他使用表达式树转换。所以我会遵循这种方法,如果我成功了,我会发布更详细的答案(也许可以通过我的博客)。
您如何看待这个想法?我应该花时间实现Expression provider吗?
UPDATE。我使用自定义Expression vistior失败了 - 它需要通过表达式树进行多次转换。所以我已经决定将所有Linq逻辑移到“存储”类中,它们包含了复杂的查询和持久性管理。