C# 包装DbSet<;张力>;使用自定义DbSet/idset?

C# 包装DbSet<;张力>;使用自定义DbSet/idset?,c#,design-patterns,architecture,entity-framework-4.1,abstraction,C#,Design Patterns,Architecture,Entity Framework 4.1,Abstraction,首先,我认为这样做有点荒谬,但我团队的其他成员坚持这样做,我除了“我认为这很愚蠢”之外,找不出一个好的理由来反对它 我们要做的是创建一个完全抽象的数据层,然后实现该数据层的各种实现。很简单,对吧?进入实体框架4.1 我们在这里的最终目标是程序员(我尽量只停留在数据层上)永远不想暴露于具体的类。他们只想在代码中使用接口,当然不需要实例化工厂 我想实现以下目标: 首先,我们有所有接口的“公共”库,我们将其称为“Common.Data”: 所以差不多就是这样。。。我希望这里有人能给我指出正确的方向,或

首先,我认为这样做有点荒谬,但我团队的其他成员坚持这样做,我除了“我认为这很愚蠢”之外,找不出一个好的理由来反对它

我们要做的是创建一个完全抽象的数据层,然后实现该数据层的各种实现。很简单,对吧?进入实体框架4.1

我们在这里的最终目标是程序员(我尽量只停留在数据层上)永远不想暴露于具体的类。他们只想在代码中使用接口,当然不需要实例化工厂

我想实现以下目标:

首先,我们有所有接口的“公共”库,我们将其称为“Common.Data”:

所以差不多就是这样。。。我希望这里有人能给我指出正确的方向,或者帮我提出一个好的论点,我可以回击开发团队。我在这个网站上看过一些关于EF不能使用接口的文章,说你不能实现
IDbSet
(我觉得有点奇怪,如果你不能实现它,为什么他们会提供它?)

提前感谢您的帮助!
第一个论点是EF不适用于接口<代码>数据库集必须使用实际实体实现定义

第二个论点是您的实体不应该包含
DbSet
——这是上下文相关的类,并且您的实体应该完全没有这种依赖关系,除非您要实现活动记录模式。即使在这种情况下,您也绝对无法访问另一个实体中不同实体的
DbSet
。即使您换行集合,您仍然离EF太近,并且实体永远不会有属性访问其他实体类型的所有实体(不仅仅是与当前实例相关的实体)

只是想说明EF中的
DbSet
具有非常特殊的意义-它不是一个集合。它是数据库的入口点(例如,
DbSet
hits数据库上的每个LINQ查询),在正常情况下,它不会暴露在实体上

第三个参数是,您正在为每个应用程序使用一个上下文-每个单例工厂有一个私有实例。除非您正在执行某个单运行批处理应用程序

最后一个论点很实用。你是为提供特性而付费的,而不是因为在抽象上浪费时间,因为抽象并没有给你(和你的客户)任何商业价值。这不是为了证明为什么不应该创建这个抽象。这是关于证明你为什么应该这样做。你会从使用它中得到什么价值?如果你的同事不能提出有商业价值的论点,你可以去找你的产品经理,让他运用他的权力——他掌握预算

一般来说,抽象是设计良好的面向对象应用程序的一部分——这是正确的。但是:

  • 每一个抽象都会使您的应用程序更加复杂,并且会增加开发成本和时间
  • 并不是每个抽象都能使您的应用程序更好或更易于维护——太多的抽象会产生相反的效果
  • 提取EF很困难。说您将以另一种实现取代它的方式抽象数据访问是数据访问大师的任务。首先,您必须对许多数据访问技术有很好的经验,才能定义这样的抽象,它将与所有这些技术一起工作(最后,您只能知道您的抽象与您在设计时所考虑的技术一起工作)。您的抽象将只与EF DbContext API一起工作,而不与其他任何东西一起工作,因为它不是一个抽象。如果您想构建通用抽象,您应该开始研究存储库模式、工作单元模式和规范模式——但要使它们具有通用性并实现它们,这是一项艰巨的工作。需要做的第一步是将所有与数据访问相关的内容隐藏在抽象背后——包括LINQ
  • 只有当您现在需要时,抽象数据访问以支持多个API才有意义。如果您只认为它在未来比在业务驱动的项目中更有用,那么这完全是一个错误的决策,而产生这种想法的开发人员无法做出业务目标决策
什么时候进行“大量”抽象才有意义

  • 您现在有了这样的要求-这将此类决策的负担转移到负责预算/项目范围/要求等的人员身上
  • 您现在需要抽象来简化设计或解决一些问题
  • 你们正在做开源或爱好项目,你们的动力不是业务需求,而是项目的纯度和质量
  • 您正在平台(长寿命零售产品,将长期存在)或公共框架上工作-这通常返回到第一点,因为这类产品通常具有需求之类的抽象
如果您只使用目标应用程序(主要是按需单用途应用程序或外包解决方案),那么只有在必要时才应该使用抽象。这些应用程序是由成本驱动的-目标是以最小的成本和最短的时间提供有效的解决方案。即使最终的应用程序在内部不是很好,这个目标也必须实现——唯一重要的是应用程序是否满足要求。任何基于“如果……发生了什么”或“也许我们需要……”的抽象都会增加虚拟(不存在的)需求的成本,这些需求在99%的情况下永远不会发生,而且在大多数情况下,与客户的初始合同不计算这些额外成本

顺便说一句,这类应用程序是MS API和设计师策略的目标-MS将使
public interface IEntity
{
    int ID { get; set; }
}

public interface IUser : IEntity
{
    int AccountID { get; set; }
    string Username { get; set; }
    string EmailAddress { get; set; }
    IAccount Account { get; set; }
}

public interface IAccount : IEntity
{
    string FirstName { get; set; }
    string LastName { get; set; }
    DbSet<IUser> Users { get; set; } // OR IDbSet<IUser> OR [IDbSet implementation]?
}

public interface IEntityFactory
{
    DbSet<IUser> Users { get; }
    DbSet<IAccount> Accounts { get; }
}
internal class User : IUser
{
    public int ID { get; set; }
    public string Username { get; set; }
    public string EmailAddress { get; set; }
    public IAccount Account { get; set; }

    public class Configuration : EntityTypeConfiguration<User>
    {
        public Configuration() : base()
        {
             ...
        }
    }
}

internal class Account : IAccount
{
    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DbSet<IUser> Users { get; set; } // OR IDbSet<IUser> OR [IDbSet implementation]?

    public class Configuration : EntityTypeConfiguration<Account>
    {
        public Configuration() : base()
        {
             ...
        }
    }
}
public class ImplEntityFactory : IEntityFactory
{
    private ImplEntityFactory(string connectionString) 
    {
        this.dataContext = new MyEfDbContext(connectionString);
    }
    private MyEfDbContext dataContext;

    public static ImplEntityFactory Instance(string connectionString)
    {
        if(ImplEntityFactory._instance == null)
            ImplEntityFactory._instance = new ImplEntityFactory(connectionString);

        return ImplEntityFactory._instance;
    }
    private static ImplEntityFactory _instance;

    public DbSet<IUser> Users // OR IDbSet<IUser> OR [IDbSet implementation]?
    { 
        get { return dataContext.Users; }
    }

    public DbSet<IAccount> Accounts // OR IDbSet<IUser> OR [IDbSet implementation]?
    {
        get { return dataContext.Accounts; }
    }
}
public class MyEfDataContext : DbContext
{
    public MyEfDataContext(string connectionString)
        : base(connectionString)
    {
        Database.SetInitializer<MyEfDataContext>(null);
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Configurations.Add(new User.Configuration());
        modelBuilder.Configurations.Add(new Account.Configuration());
        base.OnModelCreating(modelBuilder);
    }

    public DbSet<User> Users { get; set; }
    public DbSet<Account> Accounts { get; set; }
}
public class UsingIt
{
    public static void Main(string[] args)
    {
        IEntityFactory factory = new ImplEntityFactory("SQLConnectionString");
        IUser user = factory.Users.Find(5);
        IAccount usersAccount = user.Account;

        IAccount account = factory.Accounts.Find(3);
        Console.Write(account.Users.Count());
    }
}