entity framework4 TPHinheritance,如何将一种类型转换为另一种?

我已经找到了一些有关这方面的信息,但还不足以让我理解这种情况的最佳实践。 我有你的典型TPH设置与抽象基类“公司”。 我有几个孩子“小公司”,“大公司”等inheritance自公司。 实际上,我实际上对公司有不同的现实分类,但我试图在这个例子中保持简单。 在根据TPH的数据库中,我有一个具有FirmTypeId列(int)的Firm表,用于区分所有这些类型。 一切都很好,除了我要求允许用户将一种类型的公司改为另一种公司。 例如,用户在添加公司时可能会犯错,并希望将其从Big Firm更改为Small Firm。 因为entity framework不允许将区分数据库列暴露为属性,所以我不相信有一种方法可以通过EF将一种类型更改为另一种类型。 如果我错了,请纠正我。 我看到它的方式我有两个选择:

  1. 不要使用TPH。 只需拥有一个公司实体并返回使用.Where(FirmTypeId == something)来区分类型。
  2. 使用context.ExecuteStoreCommand直接执行SQL以更新数据库的FirmTypeId列。

我看过一篇post,人们认为OOP的一个原则是实例不能改变它们的类型。 虽然这对我来说很有意义,但我似乎无法连接点。 如果我们遵循这个规则,那么唯一一次使用任何类型的inheritance(TPH / TPT)就是确定一种类型永远不会被转换为另一种类型。 因此,小公司永远不会成为一家大公司。 我看到应该使用构图的建议。 即使它对我没有意义(意思是我没有看到一家公司如何拥有一家大公司,对我来说,一家大公司是一家公司),我可以看到如果数据在哪里,可以如何在EF中建模组合。多个表格。 但是,在我在数据库中有一个表的情况下,它似乎是TPH或我在上面#1和#2中描述的内容。

是的,你做得很好。 EFinheritance不支持此方案。 更改现有公司公司类型的最佳方法是使用存储过程。

请查看这篇文章了解更多信息:
在entity framework中更改inheritance的类型

我在我们的项目中遇到了这个问题,我们的核心DBContext和一些带有自己DBContexts “可插拔”模块,其中“模块用户”inheritance了“核心(基础)用户”。 希望这是可以理解的。

我们还需要能够将用户更改(让我们称之为User )(如果需要,还可以同时更改另一个“inheritance的” Users ,以便用户可以使用所有这些模块。

因此我们尝试使用TPTinheritance而不是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"].ConnectionString); sqlconn.Open(); var sql = new SqlCommand(sqlcommand, sqlconn); var rows = sql.ExecuteNonQuery(); sqlconn.Close(); return rows == 1; } catch (Exception) { return false; } } 

在此方案中, CustomerinheritanceUser并且只有string Email

使用TPH时 ,查询只会从INSERT ... VALUES ...更改为UPDATE ... SET ... WHERE [Id] = ... 别忘了改变Discriminator专栏。

下次调用dbcontext.Users.OfType ,我们的原始用户“转换”为客户。


底线:我还尝试了另一个问题的解决方案,其中包括从ObjectStateManager 分离原始实体(用户)并修改新实体(客户)状态,然后保存dbcontext.SaveChanges() 。 这对我没用(TPH和TPT都没有)。 因为每个模块使用单独的DBContexts,或者因为EntityFramework 6(.1)忽略了这一点。 在这里能找到它。

除非您明确要使用关系inheritance的多态function,否则为什么不查看拆分策略?

http://msdn.microsoft.com/en-us/data/ff657841.aspx

编辑:APOLOGIES,这是一个EF 6.x答案

我发布了完整性的示例代码。 在这种情况下,我有一个基础Thing类。 然后,子类: ActiveThingDeletedThing

我的OData ThingsController有一个主要的GetThings ,我打算只公开ActiveThing ,但是,它的GetThing(ThingId)仍然可以返回任何类型的对象。 Delete操作按照OP请求的方式执行从ActiveThingDeletedThing的转换,其方式与其他答案中描述的方式非常相似。 我正在使用内联SQL(参数化)

 public class myDbModel:DbContext { public myDbModel(): base("name=ThingDb"){} public DbSet Things { get; set; } //db table public DbSet ActiveThings { get; set; } // now my ThingsController 'GetThings' pulls from this protected override void OnModelCreating(DbModelBuilder modelBuilder) { //TPH (table-per-hierarchy): modelBuilder.Entity() .Map(thg => thg.Requires("Discriminator").HasValue("A")) .Map(thg => thg.Requires("Discriminator").HasValue("D")); } } 

这是我更新的ThingsController.cs

 public class ThingsController : ODataController { private myDbModel db = new myDbModel(); ///  /// Only exposes ActiveThings (not DeletedThings) ///  ///  [EnableQuery] public IQueryable GetThings() { return db.ActiveThings; } public async Task Delete([FromODataUri] long key) { using (var context = new myDbModel()) { using (var transaction = context.Database.BeginTransaction()) { Thing thing = await db.Things.FindAsync(key); if (thing == null || thing is DeletedThing) // love the simple expressiveness here { return NotFound();//was already deleted previously, so return NotFound status code } //soft delete: converts ActiveThing to DeletedThing via direct query to DB context.Database.ExecuteSqlCommand( "UPDATE Things SET Discriminator='D', DeletedOn=@NowDate WHERE Id=@ThingId", new SqlParameter("@ThingId", key), new SqlParameter("@NowDate", DateTimeOffset.Now) ); context.ThingTransactionHistory.Add(new Ross.Biz.ThingStatusLocation.ThingTransactionHistory { ThingId = thing.Id, TransactionTime = DateTimeOffset.Now, TransactionCode = "DEL", UpdateUser = User.Identity.Name, UpdateValue = "MARKED DELETED" }); context.SaveChanges(); transaction.Commit(); } } return StatusCode(HttpStatusCode.NoContent); } }