.net 如果实体彼此相关,那么存储库模式如何工作?

.net 如果实体彼此相关,那么存储库模式如何工作?,.net,linq-to-sql,repository-pattern,.net,Linq To Sql,Repository Pattern,有一个问题,以及它的用途,似乎有一个很好的答案 但我的问题是:我该如何干净地处理相互关联的实体,而IRepository不就是一个没有真正意义的层吗 假设我有这些业务对象: public class Region { public Guid InternalId {get; set;} public string Name {get; set;} public ICollection<Location> Locations {get; set;} pu

有一个问题,以及它的用途,似乎有一个很好的答案

但我的问题是:我该如何干净地处理相互关联的实体,而IRepository不就是一个没有真正意义的层吗

假设我有这些业务对象:

public class Region {
    public Guid InternalId {get; set;}
    public string Name {get; set;}
    public ICollection<Location> Locations {get; set;}
    public Location DefaultLocation {get; set;}
}

public class Location {
    public Guid InternalId {get; set;}
    public string Name {get; set;}
    public Guid RegionId {get; set;}
}
看起来不错。一开始在第二次检查中,我有单独的业务和L2S对象,所以我仍然不知道这如何避免选择N+1,因为查询不能只返回
GetTable

问题似乎是有两组不同的对象。但是,如果我用所有System.Data.LINQ属性([Table]、[Column]等)装饰业务对象,这会破坏抽象并破坏IRepository的用途。因为我可能还想使用一些其他的ORM,此时我必须用其他属性装饰我的业务实体(同样,如果业务实体在一个单独的.Business程序集中,it的使用者现在也需要引用所有的ORM来解析属性-恶心!)

在我看来,IRepository应该是iSeries,上面的类应该是这样的:

public IQueryable<T> Query()
public class RegionService : IRegionService {
      private Func<DataContext> _l2sfactory;

      public void Create(Region newRegion) {
        // Responsibility 1: Business Validation
        // This could of course move into the Region class as
        // a bool IsValid(), but that doesn't change the fact that
        // the service concerns itself with validation
        if(newRegion.Locations == null || newRegion.Locations.Count == 0){
           throw new Exception("...");
        }

        if(newRegion.DefaultLocation == null){
          newRegion.DefaultLocation = newRegion.Locations.First();
        }

        // Responsibility 2: Data Insertion, incl. Foreign Keys
        using(var db = _l2sfactory()){
            var dbRegion = new DbRegion {
                ...
            }

            // Use EntitySet to insert Locations as well
            foreach(var location in newRegion.Locations){
                var dbLocation = new DbLocation {

                }
                dbRegion.Locations.Add(dbLocation);
            }

            // Insert Region AND all Locations
            db.InsertOnSubmit(dbRegion);
            db.SubmitChanges();
        }
      }
}
公共类区域服务:IRegionService{
私营Func_L2工厂;
公共无效创建(区域新建区域){
//职责1:业务验证
//当然,这可以作为一个例子进入Region类
//bool是valid(),但这并不能改变以下事实:
//服务本身与验证有关
if(newRegion.Locations==null | | newRegion.Locations.Count==0){
抛出新异常(“…”);
}
if(newRegion.DefaultLocation==null){
newRegion.DefaultLocation=newRegion.Locations.First();
}
//职责2:数据插入,包括外键
使用(var db=_l2sfactory()){
var dbRegion=新的dbRegion{
...
}
//还可以使用EntitySet插入位置
foreach(newRegion.Locations中的变量位置){
var dbLocation=新的dbLocation{
}
dbRegion.Locations.Add(dbLocation);
}
//插入区域和所有位置
db.InsertOnSubmit(dbRegion);
db.SubmitChanges();
}
}
}
这也解决了一个鸡蛋问题:

  • DbRegion.ID由数据库生成(作为newid()),并设置IsDbGenerated=true
  • DbRegion.DefaultLocationId是不可为空的GUID
  • DbRegion.DefaultLocationId是指向Location.ID的FK
  • DbLocation.RegionId是不可为空的GUID和FK到Region.ID
在没有EntitySet的情况下实现这一点几乎是不可能的,因此除非您牺牲数据库上的数据完整性并将其转移到业务逻辑中,否则就不可能在区域提供者之外保留位置的责任

我明白这篇帖子怎么会被视为一个不真实的问题,主观的和有争议的,所以请允许我提出一个客观的问题:

  • 存储库模式到底应该抽象出什么
  • 在现实世界中,人们如何在不破坏存储库模式应该实现的抽象的情况下优化其数据库层
  • 具体来说,现实世界如何处理SELECT N+1和数据完整性问题
我想我真正的问题是:

  • 当已经在使用ORM(如Linq To Sql)时,DataContext不是已经是我的存储库了吗?因此,DataContext之上的存储库只是再次抽象了同样的东西

关于这一切,我有几点想法: 1.AFAIK存储库模式的发明比ORM早一点。在以前的纯SQL查询中,实现存储库并从实际使用的数据库中购买抽象代码是一个不错的主意。 2.我可以说现在完全不需要存储库,但不幸的是,根据我的经验,我不能说任何ORM都可以从所有数据库细节中真正提取您。例如,我不能创建一个ORM映射,并将其与ORM声称支持的任何其他DB服务器一起使用(特别是我说的是Microsoft EF)。所以,如果您确实希望能够使用不同的数据库服务器,那么您可能仍然需要使用存储库。
3.另一个问题非常简单:代码重复。当然,有些查询您经常将其称为代码。如果您只保留ORM作为存储库,那么您将复制这些查询,因此最好对ORM容器进行某种程度的抽象,以保存这些常用的查询。

在设计存储库时,您应该考虑所谓的聚合根。本质上这意味着,如果一个实体可以单独存在于域中,那么它将不仅仅拥有自己的存储库。在你的情况下,这将是该地区

考虑典型的客户/订单场景。客户存储库将提供对订单的访问,因为没有客户订单就无法存在,因此除非您有有效的业务案例,否则您不太可能需要单独的订单存储库

在一个简单的应用程序中,您的假设可能是正确的,但请记住,除非您提供L2S上下文的抽象,否则您将难以执行有效的单元测试。根据接口编码,无论是IServiceX、IRepositoryX还是任何能提供这种分离级别的接口


关于服务接口是否进入设计的决策通常再次与业务逻辑的复杂性以及对该逻辑的可扩展Api的需要有关,该逻辑可能会被多个不同的客户端使用。

我看到的问题是订单示例的连锁反应:订单有OrderItems,没有秩序就不可能存在。但是,我可能有一个函数
public IQueryable<T> Query()
         return new Region {
            InternalId = dbRegion.ID,
            Name = dbRegion.Name,
            // Now, LocationRepo needs to concern itself with Regions...
            Locations = _locationRepo.Query()
                        .Select(loc => loc.RegionId == dbRegion.ID)
                        .ToList(),
            // DefaultLocation is a uniqueidentifier
            DefaultLocation = _locationRepo.Get(dbRegion.DefaultLocationId)
         }  
public class RegionService : IRegionService {
      private Func<DataContext> _l2sfactory;

      public void Create(Region newRegion) {
        // Responsibility 1: Business Validation
        // This could of course move into the Region class as
        // a bool IsValid(), but that doesn't change the fact that
        // the service concerns itself with validation
        if(newRegion.Locations == null || newRegion.Locations.Count == 0){
           throw new Exception("...");
        }

        if(newRegion.DefaultLocation == null){
          newRegion.DefaultLocation = newRegion.Locations.First();
        }

        // Responsibility 2: Data Insertion, incl. Foreign Keys
        using(var db = _l2sfactory()){
            var dbRegion = new DbRegion {
                ...
            }

            // Use EntitySet to insert Locations as well
            foreach(var location in newRegion.Locations){
                var dbLocation = new DbLocation {

                }
                dbRegion.Locations.Add(dbLocation);
            }

            // Insert Region AND all Locations
            db.InsertOnSubmit(dbRegion);
            db.SubmitChanges();
        }
      }
}