9

Tôi đang bắt đầu một dự án mới sử dụng Khuôn khổ thực thể. Tôi đã nghiên cứu các lựa chọn của tôi về cách tạo cơ sở dữ liệu và tìm thấy Code-First Migrations có ý nghĩa nhất (xem phần dưới nếu bạn cần biết tại sao). Code-First Migrations cho phép tôi thả xuống SQL tùy ý nghĩa là tôi vẫn có toàn quyền kiểm soát. Trong thực tế tôi thấy rằng vấn đề là thả xuống SQL có vẻ lặp đi lặp lại khủng khiếp cho một số nhiệm vụ phổ biến.Có cách nào tốt để mở rộng việc di chuyển mã đầu tiên

Vì mục đích của mình, tôi không quan tâm rằng các tiện ích mở rộng trong di chuyển là nhà cung cấp bất khả tri (không phải là SQL trong lớp lót). Tuy nhiên, tôi không thực sự tìm thấy một đường may hay điểm mở rộng tốt trong khung di trú để thêm những thứ như vậy.

Để đưa ra một ví dụ cụ thể, giả sử tôi muốn chỉ định cột RowGuid để sao chép MS-SQL. Mỗi sự xuất hiện có dạng

Sql(
    string.Format(
     "Alter Table {0} Alter Column {1} Add ROWGUIDCOL", 
     table, 
     column)); 

Vì vậy, tôi viết phương pháp tĩnh để thoát khỏi một số dư thừa

Sql(MigrationHelper.SetRowGuid(table, column); 

-hoặc-

MigrationHelper.SetRowGuid(Sql, table, column); //passing the Sql method 

Có thể có thể làm cho một trong những phần mở rộng phương pháp trên DbMigration và truy cập chúng bằng this. Nhưng vẫn trông như thế này:

CreateTable(
    "dbo.CustomerDirectory", 
    c => new 
     { 
      Uid = c.Int(nullable: false), 
      CustomerUid = c.Int(nullable: false), 
      Description = c.String(nullable: false, maxLength: 50, unicode: false), 
      RowGuid = c.Guid(nullable: false), 
     }) 
    .PrimaryKey(t => t.Uid) 
    .ForeignKey("dbo.Customer", t => t.CustomerUid); 

this.SetRowGuid(Sql, "dbo.CustomerDirectory", "RowGuid"); 
//Custom method here because of desired naming convention of Constraint 
this.SetDefaultConstraint(Sql, "dbo.CustomerDirectory", "''"): 

Nó không phải là khủng khiếp, nhưng nó vẫn cảm thấy như một hack với tôi. Tôi phải lặp lại tên bảng, và tôi cần đảm bảo rằng tôi có được tên cột được tạo đúng. Tôi tìm thấy tên bảng cần phải được lặp đi lặp lại rất nhiều, nhưng do đó, làm cột. Tuy nhiên, những gì tôi thực sự cố gắng làm như thêm vào bảng khai báo chỉ xảy ra nơi cả tên bảng và tên cột đều được biết đến.

Tôi, tuy nhiên, không thể tìm thấy điểm mở rộng tốt để mở rộng giao diện thông thạo hoặc mở rộng mã di chuyển đầu tiên theo cách cảm thấy nhất quán. Tui bỏ lỡ điều gì vậy? Có ai tìm thấy một cách tốt để làm điều này?

Một số biện minh là tại sao tôi đang ở tình trạng này:

Tôi không thích những gì dường như là một giải pháp chung của việc sử dụng tùy chỉnh chung giải pháp thuộc tính để chỉ ra cơ sở dữ liệu không lập bản đồ cho một vài lý do, nhưng mạnh mẽ nhất bởi vì chúng không tự động được chọn bởi việc di chuyển có nghĩa là bảo trì thêm. Các giải pháp mẫu đầu tiên được đưa ra bởi vì chúng không kiểm soát hoàn toàn cơ sở dữ liệu. Cơ sở dữ liệu-Đầu tiên là hấp dẫn vì sự kiểm soát; tuy nhiên, nó không có chức năng quản lý thay đổi ngoài hộp-Code-First Migrations cung cấp. Do đó, di chuyển Code-First dường như là một người chiến thắng vì các thay đổi theo định dạng mã đầu tiên là tự động và điều đó có nghĩa là sẽ chỉ có một thứ duy trì.

+0

+1 để làm nổi bật EFCF như cách tiếp cận Entity Framework duy nhất mà thực sự tạo điều kiện kiểm soát thay đổi. – bwerks

Trả lời

5

Tôi đã tìm thấy giải pháp mặc dù tôi không chắc chắn liệu nó có tốt hay không. Tôi phải đi xa hơn một chút so với hố thỏ hơn là tôi muốn lấy nó, và nó không thực sự là một điểm mở rộng.

Nó cho phép tôi viết những câu như:

CreateTable(
    "dbo.CustomerDirectory", 
    c => new 
     { 
      Uid = c.Int(nullable: false), 
      CustomerUid = c.Int(nullable: false), 
      Description = c.String(nullable: false, maxLength: 50, unicode: false), 
      RowGuid = c.Guid(nullable: false), 
     }) 
    .PrimaryKey(t => t.Uid) 
    .ForeignKey("dbo.Customer", t => t.CustomerUid) 
     //SqlValue is a custom static helper class 
    .DefaultConstraint(t => t.Description, SqlValue.EmptyString) 
     //This is a convention in the project 
     //Equivalent to 
     // .DefaultConstraint(t => t.RowGuid, SqlValue.EmptyString) 
     // .RowGuid(t => t.RowGuid) 
    .StandardRowGuid() 
     //For one-offs 
    .Sql(tableName => string.Format("ALTER TABLE {0} ...", tableName"); 

tôi không thích:

  • Thực tế là tôi đang suy nghĩ về các thành viên tư nhân, và thông thường sẽ không sử dụng như một giải pháp
  • Đó là lambda để chọn một cột có thể trả về tên cột sai nếu tham số tùy chọn "tên" của định nghĩa cột được sử dụng.

Tôi chỉ xem xét sử dụng nó ở đây vì:

  • Chúng tôi tàu lắp ráp EF nên chúng tôi chắc chắn một trong những sử dụng sẽ có các thành viên.
  • Một bài kiểm tra đơn vị đôi sẽ cho chúng tôi biết nếu một phiên bản mới sẽ phá vỡ các.
  • Nó được phân lập để di chuyển.
  • Chúng tôi có tất cả thông tin chúng tôi đang phản ánh để có được, vì vậy nếu một phiên bản mới phá vỡ điều này, chúng tôi có thể đặt một bản hack để thay thế chức năng này.
internal static class TableBuilderExtentions 
{ 
    internal static TableBuilder<TColumns> Sql<TColumns>(
     this TableBuilder<TColumns> tableBuilder, 
     Func<string, string> sql, 
     bool suppressTransaction = false, 
     object anonymousArguments = null) 
    { 
     string sqlStatement = sql(tableBuilder.GetTableName()); 

     DbMigration dbMigration = tableBuilder.GetDbMigration(); 
     Action<string, bool, object> executeSql = dbMigration.GetSqlMethod(); 

     executeSql(sqlStatement, suppressTransaction, anonymousArguments); 

     return tableBuilder; 
    } 

    [Pure] 
    private static DbMigration GetDbMigration<TColumns>(this TableBuilder<TColumns> tableBuilder) 
    { 
     var field = tableBuilder.GetType().GetField(
      "_migration", BindingFlags.NonPublic | BindingFlags.Instance); 
     return (DbMigration)field.GetValue(tableBuilder); 
    } 

    /// <summary> 
    /// Caution: This implementation only works on single properties. 
    /// Also, coder may have specified the 'name' parameter which would make this invalid. 
    /// </summary> 
    private static string GetPropertyName<TColumns>(Expression<Func<TColumns, object>> someObject) 
    { 
     MemberExpression e = (MemberExpression)someObject.Body; 

     return e.Member.Name; 
    } 

    [Pure] 
    private static Action<string, bool, object> GetSqlMethod(this DbMigration migration) 
    { 
     MethodInfo methodInfo = typeof(DbMigration).GetMethod(
      "Sql", BindingFlags.NonPublic | BindingFlags.Instance); 
     return (s, b, arg3) => methodInfo.Invoke(migration, new[] { s, b, arg3 }); 
    } 

    [Pure] 
    private static string GetTableName<TColumns>(this TableBuilder<TColumns> tableBuilder) 
    { 
     var field = tableBuilder.GetType().GetField(
      "_createTableOperation", BindingFlags.NonPublic | BindingFlags.Instance); 

     var createTableOperation = (CreateTableOperation)field.GetValue(tableBuilder); 
     return createTableOperation.Name; 
    } 
} 
+2

+1 để sử dụng '[Pure]' – nicodemus13

0

Không phải là giải pháp chung, nhưng bạn có thể kế thừa từ lớp trừu tượng/giao diện. Cho điều này sẽ cần một số thay đổi mã nhưng nó hợp lý sạch sẽ.

Tôi đã sử dụng mẫu này để xác định các cột kiểm tra của mình (UpdateBy, UpdateDate, v.v.) cho tất cả các bảng.