2010-11-03 30 views
5

Tôi đã tìm thấy một số thông tin liên quan đến điều này nhưng không đủ để tôi hiểu những gì thực hành tốt nhất cho là cho kịch bản này. Tôi có thiết lập TPH điển hình của bạn với một lớp cơ sở trừu tượng "Công ty". Tôi có một số trẻ em "Công ty nhỏ", "Công ty lớn" vv kế thừa từ Công ty. Trong thực tế tôi thực sự có phân loại thực tế khác nhau cho các công ty nhưng tôi đang cố gắng giữ nó đơn giản trong ví dụ này. Trong cơ sở dữ liệu theo TPH, tôi có một bảng Công ty với cột FirmTypeId (int) phân biệt giữa tất cả các loại này. Mọi thứ đều hoạt động tốt, ngoại trừ tôi có yêu cầu cho phép người dùng thay đổi một loại công ty thành một công ty khác. Ví dụ: người dùng có thể đã phạm sai lầm khi thêm công ty và muốn thay đổi nó từ Big Firm thành Small Firm. Vì khung thực thể không cho phép hiển thị cột cơ sở dữ liệu phân biệt đối xử như là một thuộc tính, tôi không tin rằng có một cách để thay đổi một kiểu này sang kiểu khác thông qua EF. Hãy sửa tôi nếu tôi sai. Cách tôi nhìn thấy tôi có hai tùy chọn:Entity Framework 4 TPH thừa kế, làm thế nào để thay đổi một loại khác?

  1. Không sử dụng TPH. Đơn giản chỉ cần có một Entity Firm và quay lại sử dụng .Where (FirmTypeId == something) để phân biệt giữa các loại.
  2. Thực hiện SQL trực tiếp bằng cách sử dụng context.ExecuteStoreCommand để cập nhật cột FirmTypeId của cơ sở dữ liệu.

Tôi đã nhìn thấy một bài đăng mà mọi người đề xuất rằng Một trong các nguyên lý của OOP là các trường hợp không thể thay đổi loại của chúng. Mặc dù điều đó có ý nghĩa hoàn hảo với tôi, tôi dường như không thể kết nối các dấu chấm. Nếu chúng ta tuân theo quy tắc này, thì thời gian duy nhất để sử dụng bất kỳ loại thừa kế nào (TPH/TPT) là khi người ta chắc chắn rằng một loại sẽ không bao giờ được chuyển đổi thành một loại khác. Vì vậy, một công ty nhỏ sẽ không bao giờ trở thành một công ty lớn. Tôi thấy đề xuất rằng thành phần nên được sử dụng thay thế. Mặc dù nó không có ý nghĩa với tôi (có nghĩa là tôi không thấy làm thế nào một công ty có một công ty lớn, với tôi một công ty lớn là một công ty), tôi có thể xem cách thành phần có thể được mô hình hóa trong EF nếu dữ liệu được trong nhiều bảng. Tuy nhiên trong một tình huống mà tôi có một bảng duy nhất trong cơ sở dữ liệu có vẻ như đó là TPH hoặc những gì tôi đã mô tả trong # 1 và # 2 ở trên.

+0

Đó "ai đó" có thể là tôi. Trong OO dựa trên lớp, các đối tượng không thể thay đổi kiểu. Không bao giờ. C# không cho phép điều này. EF chắc chắn không thêm tính năng này vào C#. Điều duy nhất khác với EF là tuổi thọ của vật thể dài hơn - hiệu quả, mãi mãi. –

+0

Craig, nó có thể là bạn. Tôi vừa chỉnh sửa bài đăng để bao gồm thêm một chút thông tin về vấn đề này. Nếu bạn không nhớ xem lại đoạn cuối trong bài viết của tôi và làm sáng tỏ một số cách tiếp cận đúng. – e36M3

+0

Sự khác biệt giữa "Công ty nhỏ" và "Công ty lớn" là gì? BTW Tôi không nhận được thông báo trả lời của bạn trừ khi bạn đặt @Craig vào họ. –

Trả lời

1

Có, bạn hoàn toàn ổn. Kế thừa EF không hỗ trợ kịch bản này. Cách tốt nhất để thay đổi một loại Công ty cho một số hiện tại Công ty là sử dụng quy trình được lưu trữ.

Xin hãy nhìn vào bài viết này để biết thêm:
Changing Inherited Types in Entity Framework

+1

Đó là kết luận mà tôi đã đến. Tuy nhiên, tại thời điểm này, tôi không chắc rằng đó là cách giải quyết vấn đề này, có vẻ như nếu ai đó gặp vấn đề này hơn là họ đã tiếp cận vấn đề không chính xác để bắt đầu (theo Craig) – e36M3

0

Trừ khi bạn rõ ràng muốn sử dụng các chức năng đa hình của thừa kế quan hệ, thì tại sao không nhìn vào một chiến lược tách?

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

+0

Tôi không chắc điều này thực sự giúp tôi như thế nào . Trong tình huống của tôi, có một cột Loại phân biệt đối xử xem một thực thể thuộc loại A, B hay C. Họ thực sự không có thuộc tính của riêng họ. Nó có mùi kế thừa với tôi, bởi vì tôi có thể có thể thêm một vài thuộc tính cho mỗi loại khác nhau để mô tả tốt hơn các chi tiết cụ thể của các loại của chúng. Tuy nhiên có vẻ như thừa kế không nên được sử dụng khi có yêu cầu kinh doanh để cho phép thay đổi loại này thành loại khác. – e36M3

+0

Vâng, bạn có thể chia kiểu trên một lá cờ bằng một trong những phương pháp này, sau đó bằng cách chuyển cờ về cơ bản bạn chuyển đổi loại. Và như tôi nói, thừa kế chỉ là bắt buộc nếu bạn muốn sử dụng đa hình. Vì vậy, về cơ bản bạn có 2 loại khác nhau với các thuộc tính tương tự, chỉ khác nhau bởi một hoặc hai thuộc tính. – Slappy

1

Tôi đã chạy vào vấn đề này trong dự án của chúng tôi, nơi chúng tôi có lõi DBContext và một số module "pluggable" với riêng DBContexts của họ, trong đó "mô-đun sử dụng" thừa hưởng "lõi (cơ sở) người sử dụng" . Hy vọng đó là dễ hiểu.

Chúng tôi cũng cần khả năng thay đổi (chúng ta hãy gọi nó) User để Customer (và nếu cần thiết cũng khác "thừa hưởng" Users cùng một lúc, vì vậy người dùng có thể sử dụng tất cả những mô-đun.

Do chúng tôi đã thử sử dụng TPT kế thừa, thay vì TPH - nhưng TPH cũng sẽ làm việc bằng cách nào đó.

Một cách khác mà tôi nghĩ đến là gửi truy vấn chèn/cập nhật tùy chỉnh tới DB. Trong TPT nó sẽ là:

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; 
     } 
    } 

Trong kịch bản này Customer thừa hưởng User và chỉ có string Email.

Khi sử dụng TPH truy vấn sẽ chỉ thay đổi từ INSERT ... VALUES ... thành UPDATE ... SET ... WHERE [Id] = .... Đừng quên thay đổi cột Discriminator.

Sau cuộc gọi tiếp theo dbcontext.Users.OfType<Customer>, người dùng ban đầu của chúng tôi đã "chuyển đổi" thành khách hàng.


bottomline: Tôi cũng đã cố gắng giải pháp từ một câu hỏi ở đây, trong đó bao gồm tách thực thể gốc (người dùng) từ ObjectStateManager và làm cho pháp nhân mới (khách hàng) nhà nước sửa đổi, sau đó lưu dbcontext.SaveChanges(). Điều đó không hiệu quả đối với tôi (cả TPH lẫn TPT). Hoặc là vì sử dụng DBContexts riêng cho mỗi mô-đun, hoặc vì EntityFramework 6 (.1) bỏ qua điều này. It can be found here.

+1

Thấy điều này sau một thời gian, nó sẽ sạch hơn khi sử dụng ** các tham số SQL ** trong truy vấn thay vì nối chuỗi. – podvlada

0

EDIT: Xin lỗi, đây là một EF 6.x ĐÁP

tôi gửi bài mã ví dụ cho đầy đủ. Trong kịch bản này, tôi có một lớp cơ sở Thing. Sau đó, tiểu học: ActiveThingDeletedThing

My OData ThingsController, có một chính GetThings mà tôi có ý định chỉ phơi bày ActiveThing s, nhưng, đó là GetThing (ThingId) vẫn có thể trở lại một trong hai loại đối tượng. Hành động Delete thực hiện chuyển đổi từ ActiveThing thành DeletedThing nhiều theo cách mà OP yêu cầu và nhiều cách được mô tả trong các câu trả lời khác. Tôi đang sử dụng SQL inline (tham số)

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")); 
    } 

} 

Dưới đây là ThingsController.cs cập nhật của tôi

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', [email protected] WHERE [email protected]", 
        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); 
    } 
} 
Các vấn đề liên quan