草庐IT

c# - 用自定义DbSet/IDbSet包裹DbSet <TEntity>吗?

coder 2023-07-13 原文

首先,我认为这样做有点荒谬,但是我团队的其他成员坚持这样做,除了“我认为这很愚蠢”之外,我无法提出反对它的好理由。

我们正在尝试做的是创建一个完全抽象的数据层,然后对该数据层进行各种实现。很简单,对不对?输入 Entity Framework 4.1 ...

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

我想实现以下目标:

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

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”:
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());
    }
}

这样就足够了……我希望这里的某个人能够为我指出正确的方向,或者以一个很好的论点帮助我,以便我可以回击开发团队。我在该网站上查看了其他一些有关EF无法使用接口(interface)的文章,并且one reply表示您无法实现IDbSet(我觉得有点奇怪,如果您不能实现它,为什么他们会提供它呢? ?),但到目前为止没有用。

在此先感谢您的帮助!
Ĵ

最佳答案

第一个论点是EF不适用于接口(interface)。 DbSet必须使用真实实体实现来定义。

第二个参数是您的实体不应包含DbSet-这是与上下文相关的类,并且您的实体应不依赖此类,除非您将要实现Active Record模式。即使在这种情况下,您也绝对不能访问另一个实体中另一个实体的DbSet。即使包装集,您仍然离EF太近,并且实体永远不会具有访问另一实体类型的所有实体(不仅是与当前实例相关的实体)的属性。

只是为了使EF中的DbSet清楚具有非常特殊的含义-它不是集合。它是数据库的入口点(例如,DbSet命中数据库上的每个LINQ查询),并且在正常情况下不公开给实体。

第三个参数是每个应用程序使用单个上下文-每个单例工厂只有一个私有(private)实例。除非您正在执行一些单运行批处理应用程序it is definitely wrong

最后一个论点很实际。为您提供功能而不是在抽象上浪费时间而向您付款,这不会给您(和您的客户)任何业务值(value)。这与证明为什么不应该创建此抽象无关。这是关于证明为什么要这样做的证明。使用它会带来什么值(value)?如果您的同事无法提出具有商业值(value)的论据,您可以直接去产品经理那里,让他使用自己的权力-他拥有预算。

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

  • 每种抽象都会使您的应用程序更加复杂,并且会增加开发成本和时间
  • 并非每个抽象都会使您的应用程序更好或更可维护-过多的抽象会产生相反的影响
  • 抽象EF很难。说您将以可以用另一种实现替换的方式抽象数据访问,这是数据访问专家的任务。首先,您必须对许多数据访问技术都有很好的经验,以便能够定义适用于所有这些技术的抽象(最后,您只能说您的抽象适用于您在设计时所考虑的技术) )。您的抽象只能与EF DbContext API一起使用,而不能与其他任何东西一起使用,因为它不是抽象。如果要构建通用抽象,则应该开始研究存储库模式,工作单元模式和规范模式-但是要使它们通用并实现它们需要大量工作。所需的第一步是将与数据访问相关的所有内容隐藏在该抽象之后-包括LINQ!
  • 仅当您现在需要时,抽象数据访问以支持多个API才有意义。如果您仅认为它比将来在业务驱动的项目中有用,那将是完全错误的决策,并且提出该想法的开发人员无权做出针对业务的决策。

  • 什么时候进行“大量”抽象有意义?
  • 您现在有这样的要求-将这种决定的负担移交给负责预算/项目范围/要求等的人员。
  • 您现在需要抽象以简化设计或解决一些问题
  • 您正在做开源或业余项目,您不受业务需求的驱动,而是受项目纯度和质量的驱动。
  • 您正在使用平台(可以长期使用的长生命周期零售产品)或公共(public)框架-通常返回到第一点,因为此类产品通常具有诸如
  • 需求之类的抽象概念

    如果仅使用目标应用程序(大多数是按需使用的单一目的应用程序或外包解决方案),则仅在必要时才使用抽象。这些应用受成本驱动-目标是以最小的成本和最短的时间交付工作解决方案。即使最终的应用程序内部效果不是很好,也必须实现此目标-唯一重要的是应用程序是否满足要求。任何基于“如果……发生什么情况”或“也许我们将需要……”的抽象都会增加虚拟(不存在)需求的成本,这种需求在99%的情况下永远不会发生,并且在大多数情况下,与客户的初始契约(Contract)不计在内这样的额外费用。

    顺便提一句。这种类型的应用程序是MS API和设计人员策略的目标-MS将使许多设计人员和代码生成器产生非最佳但便宜且快速的解决方案,这些解决方案可以由技能水平较低且非常便宜的人创建。最后一个示例是LightSwitch。

    关于c# - 用自定义DbSet/IDbSet包裹DbSet <TEntity>吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7431756/

    有关c# - 用自定义DbSet/IDbSet包裹DbSet <TEntity>吗?的更多相关文章

    1. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

      我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

    2. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

      我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

    3. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

      我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

    4. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

      我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

    5. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

      我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

    6. ruby-on-rails - rspec should have_select ('cars' , :options => ['volvo' , 'saab' ] 不工作 - 2

      关闭。这个问题需要detailsorclarity.它目前不接受答案。想改进这个问题吗?通过editingthispost添加细节并澄清问题.关闭8年前。Improvethisquestion在首页我有:汽车:VolvoSaabMercedesAudistatic_pages_spec.rb中的测试代码:it"shouldhavetherightselect"dovisithome_pathit{shouldhave_select('cars',:options=>['volvo','saab','mercedes','audi'])}end响应是rspec./spec/request

    7. ruby - 在 Ruby 中有条件地定义函数 - 2

      我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

    8. ruby - 定义方法参数的条件 - 2

      我有一个只接受一个参数的方法:defmy_method(number)end如果使用number调用方法,我该如何引发错误??通常,我如何定义方法参数的条件?比如我想在调用的时候报错:my_method(1) 最佳答案 您可以添加guard在函数的开头,如果参数无效则引发异常。例如:defmy_method(number)failArgumentError,"Inputshouldbegreaterthanorequalto2"ifnumbereputse.messageend#=>Inputshouldbegreaterthano

    9. ruby - 如何在 Grape 中定义哈希数组? - 2

      我使用Ember作为我的前端和GrapeAPI来为我的API提供服务。前端发送类似:{"service"=>{"name"=>"Name","duration"=>"30","user"=>nil,"organization"=>"org","category"=>nil,"description"=>"description","disabled"=>true,"color"=>nil,"availabilities"=>[{"day"=>"Saturday","enabled"=>false,"timeSlots"=>[{"startAt"=>"09:00AM","endAt"=>

    10. ruby - 获取模块中定义的所有常量的值 - 2

      我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

    随机推荐