Linq to sql 仓储模式,一些问题

3
我正在使用基于linq to sql的仓储模式,每个表格都有一个对应的仓储类。我想知道,我这样做是否符合良好/标准的方式。
联系人仓储类
  Contact GetByID()
  Contact GetAll()

COntactTagRepository

 List<ContactTag> Get(long contactID)
 List<ContactTag> GetAll()
 List<ContactTagDetail> GetAllDetails()

class ContactTagDetail
{
  public Contact Contact {get;set;}
  public ContactTag COntactTag {get;set;}
}

当我需要联系人时,我调用ContactRepository中的方法,标签也是同样的道理。但是当我需要联系人和标签一起时,我调用ContactTag存储库中的GetDetails()方法,它不会返回ORM生成的ContactTag实体,而是返回包含ORM生成的Contact和ContactTag的ContactTagDetail实体。我知道我可以简单地在ContactTag存储库中调用GetAll并访问Contact.ContactTag,但由于它是Linq to SQL,没有在查询级别上进行延迟加载的选项,因此每当我需要一个带有相关实体的实体时,我就创建一个投影类。
另一个疑问是我真正需要编写方法的地方,我可以在ContactRepository中编写GetAllWithTags()或类似的方法,但我选择在ContactTag存储库中编写。
你有什么建议?
3个回答

5
我没有时间详细讲解,但基本上不要在表格和存储库之间建立一对一的关系。我犯过这个错误,它行不通。相反,让您的存储库包含所有概念相关的表格。在您的情况下,只需拥有一个联系人存储库即可处理联系人和联系人标签(以及其他相关表格)。没有什么规则可以说明“相关”意味着什么,只有您能够决定-但是根据您对应用程序元素如何自然地分组的理解来管理您的决策。
此外,请阅读以下两个SO线程,它们处理类似的问题: 使用LINQ的多个或单个存储库如何正确使用存储库模式?

这很好,但当我有许多相关实体的存储库时,我将在哪里编写插入/删除等方法? - MindlessProgrammer
只要它们作为一个“工作单元”相关联,你可以将它们全部放在一个代码库中。 - Dan Diplo

1

这不是仓储模式。仓储模式基本上是通过创建一个对象来抽象持久性,从而(概念上)作为域中某种类型的所有对象的集合。

因此,联系人仓储库可能会像这样工作:

IContacts contacts = GetContactRepository();
using(var uow = UnitOfWork.Create()) {
  var contact1 = contacts.Get(new FindByNameSpecification("Fred"));
  contact1.LastName = "Rubble";
  var contact2 = new Contact("Barney", "Flistone");
  contacts.Add(contact2);
  // both contact1 and contact 2 are implicitly saved at the end of the unit of work
}  

关于存储库应该如何工作的稍微宽松的定义可能是这样的:

IContactRepository contacts = GetContactRepository();
using(var uow = UnitOfWork.Create()) {
  var contact1 = contacts.GetByName("Fred");
  contact1.LastName = "Rubble";
  contacts.Save(contact1);
  var contact2 = new Contact("Barney", "Flistone");
  contacts.Save(contact2);
}  //The save is explicit but the commit is implicit

你所拥有的是一个表数据网关
问题在于你正在为每个表创建一个网关。我建议不要这样做。
标记是否属于您的联系领域?如果是,则应具有这些属性。
public class Contact {
  public IEnumerable<Tag> Tags { get; }
  public void TagWith(Tag tag) { .... }
  public void UnTag(Tag tag) { ... }
}

此外,问问自己是否会添加一个下面没有联系人的标签?如果不会,那就更容易了 - 您根本不需要网关来管理标签,只需让您的联系人对象处理它即可。
public interface IContactRepository {
  IEnumerable<Contact> GetAll(); // returns Contacts along with their tags
  void Save(Contact); // saves Contact along with any tags
} 

顺便提一下,如果标记不是您领域的一部分(例如它是一个应用程序关注的问题),那么您应该有一个单独的服务,可能甚至在一个单独的项目中。
public interface IAssociateTags {
  IEnumerable<Tag> GetTagFor(Contact contact);
  void TagContact(Contact contact, Tag tag);
  void UnTagContact(Contact contact, Tag tag);
}

-1

我相信你正在正确的道路上。

Linq to SQL会遇到一个问题,那就是与表之间的一对一映射。因此,你不得不暴露你的多对多关系表。

我解决这个问题的方法是使用一个数据层,该层可以访问Linq to SQL,但在返回调用之前将所有内容映射回POCO对象。

 public IQueryable<ArtGalleryData.Image> GetImagesByImageGroup(int id)
    {

        return SqlDataContext.Image_ImageGroups
            .Where(iig => iig.ImageGroupID == id)
            .Join(
            SqlDataContext.Images,
            iig => iig.ImageID,
            i => i.ImageID,
            (iig, i) => new ArtGalleryData.Image 
            { ImageID = i.ImageID, 
              Description = i.Description, 
              Title = i.Title, 
              Height = i.Height, 
              Width = i.Width }).AsQueryable();
    }

基本上,我正在将linq对象Image映射回POCO对象Image。 (我知道这有点乱,但这是我手头上最好的例子。)

为了实现延迟加载的效果,您可以返回一个IQueryable<T>对象。这将允许您推迟查询的执行,直到您需要它。

此外,对于其中包含依赖集合的对象,我发现了这个很棒的辅助类:LazyList,它允许通过IQueryable<T>进行集合的惰性加载(延迟执行)。

我所知道的大部分内容都来自阅读Rob Connery的博客MVC Store Front

阅读他的博文并下载Store front项目,我认为它会回答你很多问题。如果您不使用asp.net MVC,仅查看他的数据访问层应该会非常有帮助。

希望能帮到你。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接