.net 如果实体彼此相关,那么存储库模式如何工作?
有一个问题,以及它的用途,似乎有一个很好的答案 但我的问题是:我该如何干净地处理相互关联的实体,而IRepository不就是一个没有真正意义的层吗 假设我有这些业务对象:.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
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
- 存储库模式到底应该抽象出什么
- 在现实世界中,人们如何在不破坏存储库模式应该实现的抽象的情况下优化其数据库层
- 具体来说,现实世界如何处理SELECT N+1和数据完整性问题
- 当已经在使用ORM(如Linq To Sql)时,DataContext不是已经是我的存储库了吗?因此,DataContext之上的存储库只是再次抽象了同样的东西
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();
}
}
}