2011-12-21 41 views
31

Tôi đã thêm EntityFramework.Migrations (Beta 1) vào ứng dụng Code-First hiện tại đang trải qua một số thay đổi (cho cả khả năng di chuyển và tinh chỉnh nhiều hơn các bảng tôi tạo từ API đầu tiên của mã) và chạy vào kịch bản GETDATE().Có thể đặt trường DateTime mặc định thành GETDATE() với Di chuyển khung thực thể?

Tôi đã sử dụng lớp khởi tạo tùy chỉnh trong DbContext của mình để chạy tập lệnh SQL để đặt một số trường và tạo chỉ mục trong cơ sở dữ liệu của tôi. Một số ít các kịch bản lệnh AlterTable của tôi là chính để thiết lập các trường có giá trị mặc định (chẳng hạn như các trường DateTime nhất định được đặt thành GETDATE()). Tôi đã thực sự hy vọng EntityFramework.Migrations sẽ có một câu trả lời cho điều này vì bạn có thể dễ dàng chỉ định defaultValue, nhưng cho đến nay tôi không nhìn thấy một.

Bất kỳ ý tưởng nào? Tôi đã thực sự hy vọng rằng làm như sau sẽ làm việc kỳ diệu. (Đó là 'kỳ lân ma thuật', sau khi tất cả)

DateCreated = c.DateTime(nullable: false, defaultValue: DateTime.Now) 

Thật không may, và logic, nó thiết lập giá trị mặc định của tôi đến thời điểm khi lệnh Update-Database được thực thi.

+0

Tôi không tìm kiếm vào đi vào các lý thuyết xoay quanh' ... bằng cách sử dụng ORM, bạn không nên đặt logic vào cơ sở dữ liệu ... '. Tôi hiểu điều đó. Chỉ muốn chắc chắn rằng không ai ném lại một ví dụ POCO với DateTime.Now trong hàm tạo :) – adammokan

+0

chỉ có một tương tự có thể, và tìm thấy một giải pháp tốt. Hy vọng điều này sẽ giúp: http://stackoverflow.com/questions/9830216/ef-4-3-1-migration-exception-altercolumn-defaultvaluesql-creates-same-default/ –

Trả lời

14

Bạn phải sử dụng kịch bản tùy chỉnh SQL trong Up phương pháp để thiết lập giá trị mặc định:

Sql("ALTER TABLE TableName ADD CONSTRAINT ConstraintName DEFAULT GETDATE() FOR ColumnName"); 

Setting giá trị mặc định trong mã cho phép chỉ có giá trị tĩnh - không có chức năng cấp cơ sở dữ liệu.

Dù sao, đặt nó trong hàm dựng POCO là cách chính xác nếu bạn định sử dụng mã trước. Ngoài ra nếu bạn muốn đặt giá trị trong ứng dụng cho một số trường hợp đặc biệt, bạn không thể sử dụng giá trị mặc định trong cơ sở dữ liệu vì giá trị mặc định trong cơ sở dữ liệu yêu cầu DatabaseGeneratedOption.Identity hoặc DatabaseGeneratedOption.Computed. Cả hai tùy chọn này đều cho phép thiết lập thuộc tính chỉ trong cơ sở dữ liệu.

Edit:

Kể từ khi sản phẩm vẫn đang được phát triển câu trả lời của tôi là không còn giá trị. Kiểm tra câu trả lời @gius cho cách thực tế để đạt được yêu cầu này bằng cách sử dụng defaultValueSql (nó không có trong EF Migrations Beta 1 nhưng đã được thêm vào trong EF 4.3 Beta 1 đã bao gồm di chuyển).

+0

Cảm ơn. Tôi đã có các chức năng SQL Script tại chỗ trước khi chuyển sang EF.Migrations vì vậy điều này không thực sự giúp tôi bất kỳ. – adammokan

+0

Tôi đã đánh dấu phần này là câu trả lời. Không phải là câu trả lời tôi đã hy vọng, nhưng tôi đã nhận ra điều này là đúng. – adammokan

1

Ngoài ra nếu tổ chức của bạn kế thừa từ một giao diện chung, bạn có thể ghi đè lên các phương pháp SaveChanges trên DbContext và thiết lập hoặc cập nhật tính tại thời điểm đó (tuyệt vời cho Ngày tạo và cuối đã thay đổi ngày)

79

Bạn có thể sử dụng

DateCreated = c.DateTime(nullable: false, defaultValueSql: "GETDATE()") 

Cách sử dụng:

public partial class MyMigration : DbMigration 
{ 
    public override void Up() 
    { 
     CreateTable(
      "dbo.Users", 
      c => new 
       { 
        Created = c.DateTime(nullable: false, defaultValueSql: "GETDATE()"), 
       }) 
      .PrimaryKey(t => t.ID); 
... 

cập nhật 2012/10/10:

Theo yêu cầu của Thiago trong bình luận của anh ấy, tôi thêm một chút ngữ cảnh.

Mã ở trên là tệp di chuyển được tạo bởi di chuyển EF bằng cách chạy Add-Migration MyMigration làm lệnh trong bảng điều khiển trình quản lý gói. Mã được tạo dựa trên các mô hình trong DbContext được kết hợp với di chuyển. Câu trả lời gợi ý rằng bạn sửa đổi kịch bản được tạo ra một chút, để một giá trị mặc định được thêm vào khi cơ sở dữ liệu được tạo ra.

Bạn có thể đọc thêm về Mã khung thực thể đầu tiên Di chuyển đầu tiên here.

+1

mã này ở đâu? biến "c" là gì ?? Bạn có thể cung cấp thêm một chút ngữ cảnh xung quanh dòng mã này không? –

+1

@ThiagoSilva Đã thêm một chút ngữ cảnh bổ sung vào câu trả lời, như bạn đã đề xuất. –

19

Gần đây tôi đã gặp sự cố này trong EF6 (vì họ vẫn chưa sửa lỗi này). Cách dễ nhất tôi tìm thấy để làm điều đó mà không phải sửa đổi thủ công lớp Migration là ghi đè lên CodeGenerator trong lớp Configuration của bạn.

Bằng cách tạo một lớp thực hiện MigrationCodeGenerator và sau đó ghi đè phương thức tạo, bạn có thể lặp qua tất cả các thao tác và áp dụng những thay đổi mà bạn muốn.

Khi sửa đổi của bạn đã được thực hiện, bạn có thể khởi tạo CSharpMigrationCodeGenerator và trả về giá trị mặc định của nó.

public class ExtendedMigrationCodeGenerator : MigrationCodeGenerator 
{ 
    public override ScaffoldedMigration Generate(string migrationId, IEnumerable<MigrationOperation> operations, string sourceModel, string targetModel, string @namespace, string className) 
    { 
     foreach (MigrationOperation operation in operations) 
     { 
      if (operation is CreateTableOperation) 
      { 
       foreach (var column in ((CreateTableOperation)operation).Columns) 
        if (column.ClrType == typeof(DateTime) && column.IsNullable.HasValue && !column.IsNullable.Value && string.IsNullOrEmpty(column.DefaultValueSql)) 
         column.DefaultValueSql = "GETDATE()"; 
      } 
      else if (operation is AddColumnOperation) 
      { 
       ColumnModel column = ((AddColumnOperation)operation).Column; 

       if (column.ClrType == typeof(DateTime) && column.IsNullable.HasValue && !column.IsNullable.Value && string.IsNullOrEmpty(column.DefaultValueSql)) 
        column.DefaultValueSql = "GETDATE()"; 
      } 
     } 

     CSharpMigrationCodeGenerator generator = new CSharpMigrationCodeGenerator(); 

     return generator.Generate(migrationId, operations, sourceModel, targetModel, @namespace, className); 
    } 
} 

internal sealed class Configuration : DbMigrationsConfiguration<Project.Models.Context.DatabaseContext> 
{ 
    public Configuration() 
    { 
     AutomaticMigrationsEnabled = false; 
     MigrationsDirectory = @"Migrations"; 
     this.CodeGenerator = new ExtendedMigrationCodeGenerator(); 
    } 
} 

Tôi hy vọng điều này sẽ giúp

+0

Đối với EF6, điều này làm việc hoàn hảo. tốt đẹp nhất. – tony

+1

Tôi ước tôi có thể trao một khoản tiền thưởng đại diện cho bạn. Tôi đang tham gia một dự án hiện tại đã bắt đầu cuộc sống của mình như là một dự án nHibernate mà một người nào đó sau đó đã thay đổi thành EF6. Họ đã không đi thêm dặm để có được tất cả mọi thứ về di cư do thời gian hạn chế, mà tôi dự định làm bây giờ. Điều này làm cho nó có thể xóa nhiều tập lệnh một lần mà chúng chạy trong trình tạo DB được cuộn bằng tay. Cảm ơn bạn! –

0

Đây là cách đơn giản nhất.

Đầu ThêmDatabaseGeneratedOption.ComputedDataAnnotion đến tài sản

và bây giờ bạn có thể sửa đổi de SqlServerMigrationSqlGenarator, ghi đè phương pháp Genarate và thiết lập DefaultValueSql = "GETDATE()" or "GETUTCDATE()";

+1

Câu trả lời này có lẽ nên bao gồm SqlServerMigrationSqlGenerator tùy chỉnh cụ thể. Câu trả lời của JonnySchnittger là một bàn chải rộng và không bao gồm hỗ trợ chú thích thuộc tính cho mỗi thuộc tính hoặc định nghĩa cấu hình thông thạo. (Nếu tôi kết thúc triển khai thay vì sửa đổi di chuyển, tôi sẽ chỉnh sửa nó) – JoeBrockhaus

0

Một cải tiến: kiểm tra nếu các hạn chế tồn tại:

Sql(@" 
if not exists (
    select * 
     from sys.all_columns c 
     join sys.tables t on t.object_id = c.object_id 
     join sys.schemas s on s.schema_id = t.schema_id 
     join sys.default_constraints d on c.default_object_id = d.object_id 
    where 
     d.name = 'DF_ThubOutputEmail_Created' 
) 
begin 
    ALTER TABLE dbo.ThubOutputEmails ADD CONSTRAINT DF_ThubOutputEmail_Created default getdate() for Created; 
end"); 
4

Tạo di chuyển:

public partial class Table_Alter : DbMigration 
{ 
    public override void Up() 
    { 
     AddColumn("dbo.tableName", "columnName", 
      c => c.DateTime(nullable: false, defaultValueSql: "GETDATE()")); 
    } 

    public override void Down() 
    { 
     DropColumn("dbo.tableName", "columnName"); 
    } 
} 

Đối với hồ sơ hiện có, nó sẽ thiết lập các datetime khi bạn sẽ chạy Update-Database lệnh, cho kỷ lục mới sẽ được thiết lập datetime tạo

Các vấn đề liên quan