首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用定制的DbSet/IDbSet包装DbSet<TEntity>?

用定制的DbSet/IDbSet包装DbSet<TEntity>?
EN

Stack Overflow用户
提问于 2011-09-15 21:37:36
回答 1查看 15.7K关注 0票数 17

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

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

我们在这里的最终目标是程序员(我尽我最大的努力只停留在数据层)永远不想暴露在具体的类中。除了显然需要实例化工厂之外,他们只希望在代码中使用接口。

我想要实现如下所示:

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

代码语言:javascript
复制
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; }
}

这样我们就有了一个实现库,我们将其命名为"Something.Data.Imp":

代码语言:javascript
复制
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()
        {
             ...
        }
    }
}

工厂:

代码语言:javascript
复制
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; }
    }
}

上下文:

代码语言:javascript
复制
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; }
}

然后前端程序员将使用它,如下所示:

代码语言:javascript
复制
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());
    }
}

所以差不多就是这样了。我希望这里有人能够给我指出正确的方向,或者帮助我提出一个很好的论点,让我可以还击开发团队。我看过这个网站上的其他一些文章,关于EF不能使用接口和one reply,说你不能实现IDbSet (我觉得很奇怪,如果你不能实现它,为什么他们还要提供它呢?)但到目前为止,这一切都没有用。

提前感谢您的帮助!J

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2011-09-16 02:28:36

第一个论点是EF不能与接口一起工作。必须使用真实的实体实现来定义DbSet

第二个论点是你的实体不应该包含DbSet --也就是上下文相关的类,除非你要实现活动记录模式,否则你的实体应该是纯依赖的。即使在这种情况下,您也肯定无法访问另一个实体中不同实体的DbSet。即使你包装了set,你仍然离EF太近了,实体永远不会有属性访问另一个实体类型的所有实体(不仅仅是那些与当前实例相关的实体)。

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

第三个参数是每个应用程序都使用一个上下文--每个单例工厂都有一个私有实例。除非你正在做一些单次运行的批处理应用程序it is definitely wrong

最后一个论点是非常实用的。你得到的报酬是交付功能,而不是浪费时间在抽象上,这不会给你(和你的客户)带来任何商业价值。这并不是为了证明为什么你不应该创建这个抽象。这是为了证明你为什么应该这么做。使用它你会得到什么价值?如果你的同事不能提出有商业价值的论点,你可以简单地去找你的产品经理,让他使用他的权力-他持有预算。

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

  • 每一个抽象都会让你的应用程序变得更复杂,也会增加development
  • Not的成本和时间。每一个抽象都会让你的应用变得更好或更易维护--太多的抽象会产生相反的效果
  • 抽象EF是很难的。说你将抽象数据访问的方式,你可以用另一个实现替换它,这是数据访问大师的任务。首先,您必须对许多数据访问技术有非常好的经验,以便能够定义这样的抽象,这些抽象将与所有这些技术一起工作(最后,您只能告诉您,您的抽象与您在设计时考虑的技术一起工作)。您的抽象只能与EF DbContext API一起使用,而不能与其他任何东西一起使用,因为它不是一个抽象。如果你想构建通用抽象,你应该开始学习Repository模式、Unit of Work模式和Specification模式--但要使它们具有通用性,并实现它们,需要做大量的工作。所需的第一步是将与数据访问相关的所有内容隐藏在该抽象背后--包括支持多个API的LINQ!
  • Abstracting数据访问,只有当您现在需要它时才有意义。如果你只认为它在未来会比在业务驱动的项目中有用,那就完全错误的决策,而带着这个想法的开发人员没有能力做出业务目标决策。

什么时候做“大量”抽象是有意义的?

  • 您现在有这样的需求-这将这种决策的负担转移到负责预算/项目范围/需求等的人员身上。
  • 您现在需要抽象来简化设计或解决一些问题
  • 您正在做开源或爱好项目,并且您不是由业务需求驱动的,而是由您正在工作的项目的纯度和质量驱动的
  • 您正在工作的平台(长寿的零售产品,将存在很长一段时间)或公共框架-这通常返回到第一点,因为这种类型的产品通常具有诸如requirements

<>F225这样的抽象

如果您只使用目标应用程序(主要是单一用途的按需应用程序或外包解决方案),则应仅在必要时使用抽象。这些应用程序受成本驱动-目标是以最低的成本和最短的时间交付有效的解决方案。即使产生的应用程序在内部不是很好,也必须实现这个目标-唯一重要的是应用程序是否满足要求。任何基于“如果……发生了什么”或“也许我们需要……”的抽象通过虚拟(不存在)需求增加成本,这在99%的情况下永远不会发生,而且在大多数情况下,与客户的初始合同不会计入这些额外的成本。

顺便说一句。这种类型的应用程序是MS API和设计器策略的目标- MS将制作大量的设计器和代码生成器,这些设计器和代码生成器将创建非最佳但廉价和快速的解决方案,这些解决方案可以由技能集较小的人创建,并且非常便宜。最后一个例子是LightSwitch。

票数 46
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/7431756

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档