问题描述
我已经找到了一些关于这一点的信息,但是我不足以理解这种情况的最佳做法。我有一个典型的TPH设置与抽象基础类公司。我有几个孩子从公司继承的小公司,大公司等。实际上,我实际上对企业有不同的现实分类,但我在这个例子中试图保持简单。在数据库中,根据TPH,我有一个具有FirmTypeId列(int)的Firm表,可以区分所有这些类型。一切都很好,除了我有一个要求,允许用户将一种类型的公司变成另一种。例如,用户在添加公司时可能会犯错,并希望将其从大公司更改为小公司。因为实体框架不允许将歧视数据库列暴露为属性,我不认为有一种方式可以通过EF将一种类型更改为另一种类型。如果我错了,请纠正我我看到的方式我有两个选择:
- 不要使用TPH。简单地拥有一个公司,并返回使用。(FirmTypeId == something)区分类型。
- 直接使用context.ExecuteStoreCommand执行SQL来更新数据库的FirmTypeId列
我看到一个帖子,人们提出,OOP的一个原则是这种情况不能改变他们的类型。虽然这对我来说是完美的,但我似乎似乎无法连接点。如果我们遵循这个规则,那么使用任何种类的继承(TPH / TPT)的唯一时间就是当一个类型永远不会被转换为另一种类型时。所以一个小公司永远不会成为一个大公司。我看到建议应该使用组合。即使这对我来说没有意义(意思是我看不到公司如何有一个大公司,对我来说,一个大公司是一个公司),我可以看到,如果数据在多个表。然而,在数据库中有一个表的情况下,它似乎是TPH,或者我在上面#1和#2中描述的。
我在项目中遇到了这个问题,我们有核心的 DBContext
和一些可插拔模块,其自己的 DBContexts
,其中模块用户继承核心(基础)用户。希望这是可以理解的。
我们还需要改变的能力(让我们称之为)用户
到客户
(如果需要,还需要同时另一个继承的用户
,以便用户可以使用所有这些模块。 p>
因为我们尝试使用TPT继承,而不是TPH - 但是TPH也会工作。
单向是使用许多人建议的自定义存储过程 ...
我想到的另一种方法是发送自定义插入/更新查询到DB。在 TPT 中,它将是:
private static bool UserToCustomer(User u,Customer c)
{
try
{
string sqlcommand =INSERT INTO [dbo]。[Customers]([Id],[Email]) VALUES(+ u.Id +,'+ c.Email +');
var sqlconn = new SqlConnection(ConfigurationManager.ConnectionStrings [DBContext]。Conn ectionString);
sqlconn.Open();
var sql = new SqlCommand(sqlcommand,sqlconn);
var rows = sql.ExecuteNonQuery();
sqlconn.Close();
return rows == 1;
}
catch(异常)
{
return false;
}
}
在这种情况下 Customer
继承用户
,只有字符串电子邮件
。
当使用 TPH 时,查询只会从 INSERT ... VALUES ...
更改为更新... SET ... WHERE [Id] = ...
。 不要忘记更改判别器
列。
下次通话后 dbcontext.Users.OfType< Customer>
我们的原始用户转换给客户。
Bottomline:我也尝试过另一个问题的解决方案,其中包括从 ObjectStateManager
中分离原始实体(用户)并使新实体(客户)状态修改,然后保存 dbcontext.SaveChanges()
。这对我来说(TPH和TPT都没有)。因为每个模块使用单独的DBContexts,或者因为EntityFramework 6(.1)忽略这个。
I have found some information regarding this but not enough for me to understand what the best practice for is for this scenario. I have your typicaly TPH setup with an abstract base class "Firm". I have several children "Small Firm", "Big Firm" etc inheriting from Firm. In reality I actually have different realistic classifications for firms but I am trying to keep it simple in this example. In the database as per TPH I have a single Firm table with a FirmTypeId column (int) that differentiates between all these types. Everything works great except I have a requirement to allow a user to change one type of firm into another. For example a user might have made a mistake when adding the firm, and would like to change it from Big Firm to Small Firm. Because entity framework does not allow exposing the discriminating database column to be exposed as a property, I don't believe there is a way to change one type into another via EF. Please correct me if I am wrong. The way I see it I have two options:
- Don't use TPH. Simply have a Firm Entity and go back to using .Where(FirmTypeId == something) to differentiate between the types.
- Execute SQL directly using context.ExecuteStoreCommand to update the FirmTypeId column of the database.
I've seen a post where people suggest that One of the tenets of OOP is that instances cannot change their type. Although that makes perfect sense to me, I just don't seem to be able to connect the dots. If we were to follow this rule, then the only time to use any kind of inheritance (TPH/TPT) is when one is sure that one type would never be converted into another. So a Small Firm will never become a Big Firm. I see suggestions that composition should be used instead. Even though it doesn't make sense to me (meaning I don't see how a Firm has a Big Firm, to me a Big Firm is a Firm), I can see how composition can be modeled in EF if the data is in multiple tables. However in a situation where I have a single table in the database it seems it's TPH or what I've described in #1 and #2 above.
I've ran into this problem in our project, where we have core DBContext
and some "pluggable" modules with their own DBContexts
, in which "module user" inherits "core (base) user". Hope that's understandable.
We also needed the ability to change (let's call it) User
to Customer
(and if needed also to another "inherited" Users
at the same time, so that user can use all those modules.
Because of that we tried using TPT inheritance, instead of TPH - but TPH would work somehow too.
One way is to use custom stored procedure as suggested by many people...
Another way that came to my mind is to send custom insert/update query to DB. In TPT it would be:
private static bool UserToCustomer(User u, Customer c)
{
try
{
string sqlcommand = "INSERT INTO [dbo].[Customers] ([Id], [Email]) VALUES (" + u.Id + ", '" + c.Email + "')";
var sqlconn = new SqlConnection(ConfigurationManager.ConnectionStrings["DBContext"].ConnectionString);
sqlconn.Open();
var sql = new SqlCommand(sqlcommand, sqlconn);
var rows = sql.ExecuteNonQuery();
sqlconn.Close();
return rows == 1;
}
catch (Exception)
{
return false;
}
}
In this scenario Customer
inherits User
and has only string Email
.
When using TPH the query would only change from INSERT ... VALUES ...
to UPDATE ... SET ... WHERE [Id] = ...
. Dont forget to change Discriminator
column too.
After next call dbcontext.Users.OfType<Customer>
there is our original user, "converted" to customer.
Bottomline: I also tried solution from another question here, which included detaching original entity (user) from ObjectStateManager
and making new entity (customer) state modified, then saving dbcontext.SaveChanges()
. That didn't work for me (neither TPH nor TPT). Either because using separate DBContexts per module, or because EntityFramework 6(.1) ignores this.It can be found here.
这篇关于实体框架4 TPH继承,如何将一个类型更改为另一个?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!