包装 DbSet使用自定义 DbSet/IDbSet?
首先,我认为这样做有点荒谬,但我团队的其他成员坚持这样做,除了“我认为这很愚蠢”之外,我无法提出一个好的论据来反对它……
我们正在尝试的要做的就是创建一个完全抽象的数据层,然后对该数据层进行各种实现。很简单,对吧?进入 Entity Framework 4.1...
我们的最终目标是程序员(我尽力只停留在数据层)永远不想暴露于具体的类。除了明显需要实例化工厂之外,他们只想在代码中使用接口。
我想实现如下目标:
首先我们有所有接口的“通用”库,我们将其称为“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()
{
...
}
}
}
Factory:
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; }
}
}
Context:
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 无法使用接口的其他一些文章,并一个回复< /a> 说你无法实现 IDbSet
(我觉得有点好奇,如果你无法实现的话他们为什么要提供它?)但到目前为止还没有结果。
预先感谢您的任何帮助! J
First off, I think this is somewhat ridiculous to do but the other members of my team insist upon it and I can't come up with a good argument against it other than "I think it's dumb"...
What we're trying to do is create a completely abstract data layer and then have various implementations of that data layer. Simple enough, right? Enter Entity Framework 4.1...
Our end goal here is that the programmers (I do my best to stay only on the data layer) never want to have to be exposed to the concrete classes. They only ever want to have to use interfaces in their code, aside from obviously needing to instantiate the factory.
I want to achieve something like the following:
First we have our "Common" library of all of the interfaces, we'll call it "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; }
}
From that we then have an implementation library, we'll call it "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()
{
...
}
}
}
Factory:
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; }
}
}
Context:
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; }
}
Then the front-end programmers would be using it such as:
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());
}
}
So that's pretty much it... I'm hoping someone on here might be able to either point me in the right direction or help me out with a good argument that I can fire back at the development team. I've looked at some other articles on this site about EF not being able to work with interfaces and one reply saying that you can't implement IDbSet
(which I find kind of curious, why would they provide it if you couldn't implement it?) but so far to no avail.
Thanks in advance for any help!
J
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
第一个论点是 EF 不适用于接口。
DbSet
必须使用真实的实体实现来定义。第二个参数是您的实体不应包含 DbSet - 这是与上下文相关的类,并且您的实体应该纯粹是这种依赖关系,除非您要实现活动记录模式。即使在这种情况下,您也绝对无法访问另一个实体中不同实体的
DbSet
。即使您包装集,您仍然离 EF 太近,并且实体永远不会具有访问其他实体类型的所有实体(不仅是与当前实例相关的实体)的属性。只是为了明确 EF 中的
DbSet
具有非常特殊的含义 - 它不是一个集合。它是数据库的入口点(例如DbSet
上的每个 LINQ 查询都会命中数据库),并且在正常情况下不会在实体上公开。第三个参数是每个应用程序使用单个上下文 - 每个单例工厂有一个私有实例。除非您正在执行一些单次运行的批处理应用程序这绝对是错误的。
最后一个论点很实用。您获得报酬是为了提供功能,而不是为了在抽象上浪费时间,因为抽象不会给您(和您的客户)带来任何商业价值。这并不是要证明为什么你不应该创建这个抽象。这是为了证明为什么你应该这样做。使用它你会获得什么价值?如果你的同事无法提出具有商业价值的论点,你可以直接去找你的产品经理,让他使用他的权力——他掌握着预算。
一般来说,抽象是设计良好的面向对象应用程序的一部分——这是正确的。但是:
什么时候进行“大量”抽象才有意义?
如果您只工作目标应用程序(大多数是单一用途的应用程序)需求或外包解决方案)仅在必要时才应使用抽象。这些应用程序由成本驱动 - 目标是在最短的时间内以最低的成本提供可行的解决方案。即使最终的应用程序在内部效果不是很好,这个目标也必须实现——唯一重要的是应用程序是否满足要求。任何基于“如果......发生会怎样”或“也许我们需要......”的抽象都会增加虚拟(不存在)需求的成本,而这些需求 99% 永远不会发生,并且在大多数情况下与客户的初始合同不计在内其中此类额外费用。
顺便提一句。此类应用程序是 MS API 和设计器策略的目标 - MS 将制造大量设计器和代码生成器,它们将创建非最佳但廉价且快速的解决方案,这些解决方案可以由技能较少的人创建,而且非常便宜。最后一个例子是LightSwitch。
The first argument is that EF doesn't work with interfaces.
DbSet
must be defined with a real entity implementation.The second argument is that your entities should not contain
DbSet
- that is context related class and your entities should be pure of such dependency unless you are going to implement Active record pattern. Even in such case you will definitely not have access toDbSet
of different entity in another entity. Even if you wrap set you are still too close to EF and entity never have property accessing all entities of another entity type (not only those related to current instance).Just to make it clear
DbSet
in EF has very special meaning - it is not a collection. It is entry point to database (for example each LINQ query onDbSet
hits database) and it is in normal scenarios not exposed on entities.The third argument is that you are using a single context per application - you have a single private instance per singleton factory. Unless you are doing some single run batch application it is definitely wrong.
The last argument is simply practical. You are paid for delivering features not for wasting time on abstraction which doesn't give you (and your customer) any business value. It is not about proving why you should not create this abstraction. It is about proving why you should do it. What value will you get from using it? If your colleagues are not able to come with arguments which have business value you can simply go to your product manager and let him use his power - he holds the budget.
Generally abstraction is part of well designed object oriented application - that is correct. BUT:
When it make sense to do "a lot of" abstraction?
If you are working only targeted application (mostly single purpose applications on demand or outsourced solutions) the abstraction should be used only if necessary. These applications are driven by costs - the target is delivering working solution for minimal costs and in the shortest time. This target must be achieved even if resulting application will not be very good internally - the only thing which matters is if application meets requirements. Any abstraction based on "what if ... happens" or "perhaps we will need ..." increases costs by virtual (non existing) requirements which will in 99% never happen and in most cases initial contract with customer didn't count which such additional costs.
Btw. this type of applications is targeted by MS APIs and designer strategy - MS will make a lot of designers and code generators which will create non optimal but cheap and quick solutions which can be created by people with smaller skill set and are very cheap. The last example is LightSwitch.