我有一个实体,其中一个属性是抽象类型。这会创建一个使用表继承的一对一关系。看起来一切都正常。
我可以创建一个项目并将“Base”属性设置为“ConcreteOne”,一切都保存正常。但是,当我尝试更新“Base”为“ConcreteTwo”时,EF会在数据库中更新“Base”记录与新用户值,它不会更新类型的鉴别器。因此,“ConcreteTwo”的额外数据得到了持久化,但鉴别器仍然显示“ConcreteOne”。
以下是一个简单的示例,展示了这个问题。
所以它几乎是正确的,但我希望在更新语句中看到
作为一个测试,我尝试使用表分离策略,然后这个条目从
更新:我已经验证了这个问题存在于EF5和EF6中。
我可以创建一个项目并将“Base”属性设置为“ConcreteOne”,一切都保存正常。但是,当我尝试更新“Base”为“ConcreteTwo”时,EF会在数据库中更新“Base”记录与新用户值,它不会更新类型的鉴别器。因此,“ConcreteTwo”的额外数据得到了持久化,但鉴别器仍然显示“ConcreteOne”。
以下是一个简单的示例,展示了这个问题。
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
App_Start.EntityFrameworkProfilerBootstrapper.PreStart();
Database.SetInitializer(new DropCreateDatabaseAlways<DataContext>());
// Create our item with ConcreteOne for Base
using (var context = new DataContext())
{
var item = new Item
{
Base = new ConcreteOne { Name = "Item", Data = 3 }
};
context.Items.Add(item);
context.SaveChanges();
}
// Update Base with a new ConcreteTwo
using (var context = new DataContext())
{
var item = context.Items.FirstOrDefault();
var newBase = new ConcreteTwo()
{
Item = item,
Name = "Item 3",
User = new User { Name = "Foo" }
};
// If I don't set this to null, EF tries to create a new record in the DB which causes a PK exception
item.Base.Item = null;
item.Base = newBase;
// EF doesn't save the discriminator, but DOES save the User reference
context.SaveChanges();
}
// Retrieve the item -- EF thinks Base is still ConcreteOne
using (var context = new DataContext())
{
var item = context.Items.FirstOrDefault();
Console.WriteLine("{0}: {1}", item.Name, item.Base.Name);
}
Console.WriteLine("Done.");
Console.ReadLine();
}
}
public class DataContext : DbContext
{
public DbSet<Item> Items { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public virtual Base Base { get; set; }
}
public abstract class Base
{
public int Id { get; set; }
public string Name { get; set; }
[Required]
public virtual Item Item { get; set; }
}
public class ConcreteOne : Base
{
public int Data { get; set; }
}
public class ConcreteTwo : Base
{
public virtual User User { get; set; }
}
}
当更改被保存时,EF会生成以下SQL:
update [dbo].[Bases]
set [Name] = 'Item 3' /* @0 */,
[User_Id] = 1 /* @1 */
where (([Id] = 1 /* @2 */)
and [User_Id] is null)
所以它几乎是正确的,但我希望在更新语句中看到
[Discriminator] = 'ConcreteTwo'
。我的期望是不是没有根据或者我做错了什么?作为一个测试,我尝试使用表分离策略,然后这个条目从
ConcreteOne
表中删除并添加到ConcreteTwo
表中,正如我所期望的那样。所以它可以工作,但我的真实应用程序至少有七个子类型,并且检索Base
属性的SQL语句变得非常恶心。因此,如果可能的话,我肯定想使用TPH来完成这个任务。更新:我已经验证了这个问题存在于EF5和EF6中。
PrimaryTransportation
作为类型,那么这是一个糟糕的设计。 - PhillPrimaryTransportation
作为属性,它将是Vehicle
类型。我不在乎EF是否想删除ConcreteOne记录并创建ConcreteTwo记录;那没关系。我的观点是,它既不使用TPH进行前者,也不使用TPT进行后者。 - Andorbal