Entity Framework 和 Repository Pattern 的概念难点

3

我正在使用ASP.NET MVC和SQL Server 2012制作一个内部网站。我正在使用Onion Architecture创建存储库和架构。我的问题是,我所在的公司已经有几个服务器数据库,其中表之间没有关系,而是有一些用于映射这些关系的表。例如,一个用户表和一个文档表有一个名为User_joint_Document的表来建立关系,包含两个ID(IDDocument和IDUser)。现在当我写我的通用存储库时:

class Repository<T> : IRepository<T> where T : class 

问题在于通用类型T没有意义,我无法使用EF查询影响模型中的值,这是正常的。如果有一个父类BaseEntity来为每个表定义ID,那就太好了,然后我可以这样写:

class Repository<T> : IRepository<T> where T : BaseEntity

我所有的表模型都将继承自BaseEntity。但这也意味着需要以关系方式重写整个数据库,并手动映射每个DB POCO(请纠正我如果我错了),而我没有这样做的技能(不同服务器DB中有超过300个表,我缺乏正确的知识和经验来进行此类操作)。
有没有办法保持原始的数据库结构,并编写一个通用存储库?如何去实现?
编辑为澄清我的问题,因为@saeb部分回答了我的问题。我是否可以拥有一个通用存储库,而不必为我的DB POCOs设置父类?还是我需要它,以便只有一个存储库来掌管它们?例如:
class Repository<T>:IRepository<T> where T : class
{
  private readonly ApplicationContext context;
  private DbSet<T> entities;
  public Repository(PrincipalServerContext context)
  {
        this.context = context;
        entities = context.Set<T>();
  }
  public T Get(long id)
  {
     return entities.SingleOrDefault(s => s.IDUser == id);
     //This does not work, IDUser isn't recognized

  }

感谢您的帮助!

1
但是,如果您正在使用洋葱架构,则有一个包会自动为您创建存储库,您只需在DataOnion中注册它们即可。 - Mihail Stancescu
@melkisadek 哎呀!我在处理数据库方面真的很差。我本以为你会在两个表之间建立一对一的关系或者类似的东西?也许这个关系是一个实际的表格?这真的不是我的强项。如果我创建一个带有ID的父类,那么这个关系表格会变成什么样子?因为它之前有IDDocument和IDUser,但现在我只会从父类中得到ID。 - Flexabust Bergson
@MihailStancescu 什么?这个存在吗?这是你所指的吗?https://sswdataonion.com - Flexabust Bergson
1
是的,我正在使用他们的包并且使用工作单元和存储库。您只需声明域类及其正确的 EF 配置即可。 - Mihail Stancescu
是的,检查他们的软件包,稍加定制即可实现您所需的功能。 - Mihail Stancescu
显示剩余5条评论
2个回答

3

...有几个服务器数据库,其中表之间没有关系...

但是它们确实存在一种关系,即多对多关系,该关系通过第三个映射表定义(这是否是一个正确定义的关系是另一个话题)

...问题是通用类型T毫无意义,我无法使用EF查询影响我的模型中的值...

为什么不能?考虑你的表示例,你会有两个实体:UserDocument,它们看起来像这样:

public class User
{
    public int IDUser { get; set; }

    public virtual ICollection<Document> Documents { get; set; }

    ...
}

public class Document
{
    public int IDDocument { get; set; }

    public virtual ICollection<User> Users { get; set; }

    ...
}

您可以在上下文的OnModelCreating中使用流畅的API通过第三张表来建立关系:

public class YourContext: DbContext
{
    ...

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<User>()
            .HasMany<Document>(u => u.Documents)
            .WithMany(d => d.Users)
            .Map(userJointDocument =>
                {
                    userJointDocument.MapLeftKey("IDUser");
                    userJointDocument.MapRightKey("IDDocument");
                    userJointDocument.ToTable("User_joint_Document");
                });
    }

    ...
}

然后,您可以像直接关联一样在存储库中查询UserDocument。如果您想了解更多信息,请单击这里此处这个源查看更多好的资料。


谢谢你提供精确的答案和好文档。我已经尝试过了,但是在我的代码库中仍然遇到了问题,我无法识别这些ID。我已经编辑了我的问题以展示我的代码库长什么样子。 - Flexabust Bergson
不能奇怪地标记你。 - Flexabust Bergson

1
据我所见,如果不给实体/POCO至少添加一个基类或接口,就无法实现此目标。您可以尝试使用表达式来实现通用存储库。
public interface IEntity<T> where T : class
{
    Expression<Func<T, bool>> GetByIdPredicate(long id);
}        

public partial class User : IEntity<User>
{
   public int UserID { get; set; }

   public Expression<Func<User, bool>> GetByIdPredicate(long id)
   {
      return (User entity) => entity.UserID == id;
   }
}

class Repository<T>:IRepository<T> where T : class, IEntity, new()
{
  private readonly ApplicationContext context;
  private DbSet<T> entities;
  T dummyEntity;

  public Repository(PrincipalServerContext context)
  {
        this.context = context;
        entities = context.Set<T>();
        dummyEntity = new T();
  }
  public T Get(long id)
  {
     return entities.SingleOrDefault(dummyEntity.GetByIdPredicate(id));
  }

可能有更简洁的方法来清除dummyEntity字段


哦,这是一个非常有趣的看法。我实际上已经重新制定了我的问题,并在这里得到了另一个答案,如果你感兴趣的话,它都打包在一个使用表达式方法的方法中:https://stackoverflow.com/questions/44591796/does-a-generic-repository-need-a-base-entity-class-to-be-applied-everywhere/44592318#44592318 - Flexabust Bergson

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