我有一个具有抽象类型属性的实体。这将创建一对一关系,该关系使用每层次表继承。一切似乎都正常。
我可以创建一个Item并将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)
所以它几乎是正确的,但我希望在update语句中看到[Discriminator] = 'ConcreteTwo'
。我的期望是没有根据的还是我做错了什么?
作为测试,我尝试使用table-per-type,并且该条目已从ConcreteOne
表中删除,并按照我的预期添加到ConcreteTwo
表中。所以它可以工作,但我的真实应用程序至少有七个子类型,并且用于检索Base
属性的SQL语句非常讨厌。所以如果可能的话,我当然希望使用TPH来实现这一目标。
更新 我已经确认EF5和EF6中存在问题。
答案 0 :(得分:0)
这个问题是基于对更新的期望,这似乎是一个值得商榷的期望。目前,如果TPH层次结构没有按预期运行,并且考虑到EF6目前处于测试阶段,那么最好的选择是开始讨论Codeplex forums。
答案 1 :(得分:0)
将此添加到您的模型中:
public enum BaseType
{
ConcreteOne = 1,
ConcreteTwo = 2
}
public abstract class Base
{
...
public BaseType BaseType { get; set; }
...
}
在OnModelCreating方法中:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Base>()
.ToTable("Base");
modelBuilder.Entity<ConcreteOne>()
.Map(t => t.Requires(m => m.BaseType).Equals(BaseType.ConcreteOne))
.ToTable("ConcreteOne");
modelBuilder.Entity<ConcreteTwo>()
.Map(t => t.Requires(m => m.BaseType).Equals(BaseType.ConcreteTwo))
.ToTable("ConcreteTwo");
}
答案 2 :(得分:-1)
我希望这能用ConcreteTwo鉴别器创建一个新的实例(记录)。
using (var context = new DataContext())
{
var item = context.Items.FirstOrDefault();
var newBase = new ConcreteTwo()
{
Name = "Item 3",
User = new User { Name = "Foo" }
};
item.Base = newBase;
context.SaveChanges();
}