簡體   English   中英

實體框架4 TPH繼承,如何將一種類型轉換為另一種?

[英]Entity Framework 4 TPH inheritance, how to change one type into another?

我已經找到了一些有關這方面的信息,但還不足以讓我理解這種情況的最佳實踐。 我有你的典型TPH設置與抽象基類“公司”。 我有幾個孩子“小公司”,“大公司”等繼承自公司。 實際上,我實際上對公司有不同的現實分類,但我試圖在這個例子中保持簡單。 在根據TPH的數據庫中,我有一個具有FirmTypeId列(int)的Firm表,用於區分所有這些類型。 一切都很好,除了我要求允許用戶將一種類型的公司改為另一種。 例如,用戶在添加公司時可能會犯錯,並希望將其從Big Firm更改為Small Firm。 因為實體框架不允許將區分數據庫列暴露為屬性,所以我不相信有一種方法可以通過EF將一種類型更改為另一種類型。 如果我錯了,請糾正我。 我看到它的方式我有兩個選擇:

  1. 不要使用TPH。 只需擁有一個公司實體並返回使用.Where(FirmTypeId == something)來區分類型。
  2. 使用context.ExecuteStoreCommand直接執行SQL以更新數據庫的FirmTypeId列。

我看過一篇文章,人們認為OOP的一個原則是實例不能改變它們的類型。 雖然這對我來說很有意義,但我似乎無法連接點。 如果我們遵循這個規則,那么唯一一次使用任何類型的繼承(TPH / TPT)就是確定一種類型永遠不會轉換為另一種類型。 因此,小公司永遠不會成為一家大公司。 我看到應該使用構圖的建議。 即使它對我來說沒有意義(意思是我沒有看到公司如何擁有一家大公司,對我來說,一家大公司就是一家公司),我可以看到如果數據存在,如何在EF中建模組合。多個表。 但是,在我在數據庫中有一個表的情況下,它似乎是TPH或我在上面#1和#2中描述的內容。

我在我們的項目中遇到了這個問題,我們有核心DBContext和一些帶有自己DBContexts “可插拔”模塊,其中“模塊用戶”繼承了“核心(基礎)用戶”。 希望這是可以理解的。

我們還需要改變(我們稱之為)的能力, UserCustomer (如果需要還的另一個“繼承” Users在同一時間,使用戶可以使用所有這些模塊。

因為我們嘗試使用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"].ConnectionString);
            sqlconn.Open();
            var sql = new SqlCommand(sqlcommand, sqlconn);
            var rows = sql.ExecuteNonQuery();
            sqlconn.Close();

            return rows == 1;
        }
        catch (Exception)
        {
            return false;
        }
    }

在此方案中, Customer繼承User並且只有string Email

使用TPH時 ,查詢只會從INSERT ... VALUES ...更改為UPDATE ... SET ... WHERE [Id] = ... 別忘了改變Discriminator專欄。

下次調用dbcontext.Users.OfType<Customer> ,我們的原始用戶“轉換”為客戶。


底線:我還嘗試了另一個問題的解決方案,其中包括從ObjectStateManager 分離原始實體(用戶)並修改新的實體(客戶)狀態,然后保存dbcontext.SaveChanges() 這對我沒用(TPH和TPT都沒有)。 因為每個模塊使用單獨的DBContexts,或者因為EntityFramework 6(.1)忽略了這一點。 在這里能找到它。

是的,你做得很好。 EF繼承不支持此方案。 更改現有公司公司類型的最佳方法是使用存儲過程。

請查看這篇文章了解更多信息:
在實體框架中更改繼承的類型

除非您明確要使用關系繼承的多態功能,否則為什么不查看拆分策略?

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<Thing> Things { get; set; }  //db table

    public DbSet<ActiveThing> ActiveThings { get; set; } // now my ThingsController 'GetThings' pulls from this

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
       //TPH (table-per-hierarchy):
      modelBuilder.Entity<Ross.Biz.ThingStatusLocation.Thing>()
        .Map<Ross.Biz.ThingStatusLocation.ActiveThing>(thg => thg.Requires("Discriminator").HasValue("A"))
        .Map<Ross.Biz.ThingStatusLocation.DeletedThing>(thg => thg.Requires("Discriminator").HasValue("D"));
    }

}

這是我更新的ThingsController.cs

public class ThingsController : ODataController
{
    private myDbModel db = new myDbModel();

    /// <summary>
    /// Only exposes ActiveThings (not DeletedThings)
    /// </summary>
    /// <returns></returns>
    [EnableQuery]
    public IQueryable<Thing> GetThings()
    {
        return db.ActiveThings;
    }

    public async Task<IHttpActionResult> 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);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM