Zac*_*ott 12 domain-driven-design iqueryable repository linq-to-sql
我想创建一个DDD存储库,它返回与Linq到SQL基础类匹配的IQueryable实体,减去任何关系.我可以轻松地返回实体减去与Linq选择新{field,field,...}投影的关系.如何编写Repository Entity类?如何从具有存储库类而不是Linq to SQL类的存储库中返回一个对象,并且仍然使用Linq选择中的多个实体填充它?我如何在ViewModel中引用此返回的类?
我对此很新,因此显而易见的错误.我是否错过了船,只应从存储库返回完整的实体,而不是投影?在从存储库发送之前,我仍然需要删除Linq to SQL关系.我完全不在基地吗?我真的想保留IQueryable数据类型.
例如,我的存储库中的Linq to SQL代码:
public class MiniProduct
{
public MiniProduct( string ProductNo, string Name, string Description, double Price)
{ this.ProductNo = ProductNo;
this.Name = Name;
this.Description = Description;
this.Price = Price;
}
}
public IQueryable<MiniProduct> GetProductsByCategory( string productCategory)
{
return ( from p in db.Products
from c in db.Categories
from pc in db.ProductCategories
where c.CategoryName == productCategory &&
c.CatID == pc.CatID &&
pc.ProductID == p.ProductID
select new { p.ProductNo, p.Name, p.Description, p.Price } );
// how to return IQueryable<MiniProduct> instead of IQueryable<AnonymousType>???
}
Run Code Online (Sandbox Code Playgroud)
在View(尝试强类型ViewModel)中,我的模型数据类型是什么以及如何从视图中引用?
<% Page Inherits="System.Web.Mvc.ViewPage<MyStore.Models.MiniProduct>" %>
Run Code Online (Sandbox Code Playgroud)
编辑:
Cottsak授权代码并使其工作,因此他获得了复选框.然而,Mark Seemann指出这种技术会引起副作用.他在POCO糟糕的投射或子设置中是正确的.在使代码工作之后,我最终制造了一个更多的实体对象,这导致了不必要的复杂化.最终我改变了代码以反映马克的建议.
添加到Cottsak的建议:我的存储库返回值是IQueryable.页面指令模型引用类型是
Inherits="System.Web.Mvc.ViewPage<IQueryable<MyStore.Models.MiniProduct>>"
Run Code Online (Sandbox Code Playgroud)
模型字段通过以下方式访问:
Model.SingleOrDefault().ProductNo
Model.SingleOrDefault().Name
...
Run Code Online (Sandbox Code Playgroud)
这导致了一个
foreach (MyStore.Models.MiniProduct myproduct in Model) {}
Run Code Online (Sandbox Code Playgroud)
谢谢你们的答案.
Mar*_*ann 29
假设您的LINQ to SQL(L2S)类是自动生成的并且反映了您的底层数据库,简短的答案是:不要公开任何L2S类的IQueryable - 它将是一个漏洞抽象.
答案稍长:
存储库的重点是隐藏抽象背后的数据访问,以便您可以独立于域模型替换或更改数据访问代码.当您将Repository接口基于特定实现(基于L2S的数据访问组件(DAC))中定义的类型时,这是不可能的 - 即使您可以提供Repository接口的新实现,您也需要参考你的L2S DAC.如果您突然决定切换到LINQ to Entities或Azure Table Storage Service,那么这将不会特别好.
域对象应以技术中立的方式定义.这最好是作为Plain Old C#Objects(POCO)完成的.
此外,暴露IQueryable使您有机会执行预测.这可能听起来很有吸引力,但在DDD背景下实际上相当危险.
在DDD中,我们应该设计域对象,以便它们封装域逻辑并确保所有不变量.
例如,考虑实体的概念(不是LINQ to Entitities Entity,而是DDD实体).实体几乎总是由持久性ID标识,因此一个常见的不变量是必须定义ID .我们可以编写一个这样的基类实体类:
public abstract class Entity
{
private readonly int id;
protected Entity(int id)
{
if(id <= 0)
{
throw new ArgumentOutOfRangeException();
}
this.id = id;
}
public int Id
{
get { return this.id; }
}
}
Run Code Online (Sandbox Code Playgroud)
这样的类很好地强制执行其不变量(在这种情况下,ID必须是正数).在特定的类中可能存在许多其他更多特定于域的不变量,但是其中许多很可能与投影的概念不一致:您可能能够定义省略某些属性的投影(例如ID),但它会在运行时崩溃,因为类型的默认值(例如0表示int)将抛出异常.
换句话说,即使您只需要域对象的某些属性,也只有将其整体水合才有意义,因为这是您可以满足其不变量的唯一方法.
在任何情况下,如果您经常发现只需要选择域对象的某些部分,则可能是因为它们违反了单一责任原则.将Domain Class分为两个或更多类可能是一个更好的主意.
总之,暴露IQueryable听起来像一个非常有吸引力的策略,但随着L2S的当前实现,它将导致Leaky抽象和贫血领域模型.
使用下一版本的实体框架,我们应该获得POCO支持,这样可能会使我们更接近这一目标.但是,就我而言,陪审团仍然在那个帐户上.
有关非常相似主题的更全面讨论,请参阅IQueryable是紧耦合.
尝试这样的事情:
public class AnnouncementCategory : //...
{
public int ID { get; set; }
public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
..并在你的回购中:
public IQueryable<AnnouncementCategory> GetAnnouncementCategories()
{
return from ac in this._dc.AnnouncementCategories
let announcements = this.GetAnnouncementsByCategory(ac.ID)
select new AnnouncementCategory
{
ID = ac.ID,
Name = ac.Text,
Announcements = new LazyList<Announcement>(announcements)
};
}
private IQueryable<Announcement> GetAnnouncementsByCategory(int categoryID)
{
return GetAnnouncements().Where(a => a.Category.ID == categoryID);
}
Run Code Online (Sandbox Code Playgroud)
这样,我不是投射到匿名类型,而是投射到我AnnouncementCategory班级的新实例中.您可以忽略的GetAnnouncementsByCategory功能,如果你喜欢,这是用来链相关的集合Announcement对象为每个类别,但在某种程度上,使他们可以偷懒满载的IQueryable(即.所以,当我做这个属性最终调用,我不必调用整个集合.我可以做更多的LINQ来过滤).
| 归档时间: |
|
| 查看次数: |
5241 次 |
| 最近记录: |