risposta

15

È possibile ignorare il MigrationSqlGenerator che viene utilizzata da Entity Framework chiamando il metodo DbMigrationsConfiguration.SetSqlGenerator() nel costruttore della classe DbMigrationsConfiguration, passando il nome del provider di database (ad esempio "System.Data.SqlClient" per SQL Server), e l'istanza MigrationSqlGenerator da utilizzare per il database fornitore.

Si consideri l'esempio da the work item che si è collegato a:

public class MyEntity 
{ 
    public int Id { get; set; } 

    [Required] 
    [MinLength(5)] 
    public string Name { get; set; } 
} 

Supponiamo che la tabella per MyEntity era già stata prodotta e il comando Add-Migration è stato utilizzato per aggiungere il campo Name.

Per impostazione predefinita, la migrazione è ponteggi:

public partial class AddMyEntity_Name : DbMigration 
{ 
    public override void Up() 
    { 
     AddColumn("dbo.MyEntity", "Name", c => c.String(nullable: false)); 
    } 

    public override void Down() 
    { 
     DropColumn("dbo.MyEntity", "Name"); 
    } 
} 

Si noti che lo scaffolder non ha generato nulla per il MinLengthAttribute.

Per far convocare EF il requisito di lunghezza minima, è possibile specify an attribute-to-column annotation convention. Come menzionato in quella pagina di documentazione, qualsiasi AnnotationValues viene ignorato dai generatori SQL predefiniti.

All'interno della vostra di DbContext OnModelCreating() override, aggiungere il seguente:

modelBuilder.Conventions.Add(new AttributeToColumnAnnotationConvention<MinLengthAttribute, Int32>("minLength", (property, attributes) => attributes.Single().Length)); 

Dopo aver aggiunto che, è possibile rigenerare la migrazione ponteggi eseguendo Add-Migration -Force AddMyEntity_Name. Ora la migrazione ponteggi è:

public partial class AddMyEntity_Name : DbMigration 
{ 
    public override void Up() 
    { 
     AddColumn("dbo.MyEntity", "Name", c => c.String(nullable: false, 
      annotations: new Dictionary<string, AnnotationValues> 
      { 
       { 
        "minLength", 
        new AnnotationValues(oldValue: null, newValue: "5") 
       }, 
      })); 
    } 

    public override void Down() 
    { 
     DropColumn("dbo.MyEntity", "Name", 
      removedAnnotations: new Dictionary<string, object> 
      { 
       { "minLength", "5" }, 
      }); 
    } 
} 

Supponiamo che, come in l'elemento di lavoro collegato, si desidera generare un vincolo per verificare che il valore di Name rifilato è maggiore del minLength (5 in questo caso).

Si può iniziare con la creazione di un custom MigrationSqlGenerator che si estende SqlServerMigrationSqlGenerator e chiamare SetSqlGenerator() per installare il costume MigrationSqlGenerator:

internal class CustomSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator 
{ 
    protected override void Generate(AddColumnOperation addColumnOperation) 
    { 
     base.Generate(addColumnOperation); 
    } 
} 

internal sealed class Configuration : DbMigrationsConfiguration<DataContext> 
{ 
    public Configuration() 
    { 
     AutomaticMigrationsEnabled = false; 

     SetSqlGenerator("System.Data.SqlClient", new CustomSqlServerMigrationSqlGenerator()); 
    } 

    protected override void Seed(DataContext context) 
    { 
     //... 
    } 
} 

In questo momento, questo CustomSqlServerMigrationSqlGenerator l'override del metodo Generate (AddColumnOperation), ma semplicemente chiama il implementazione di base.

Se si guarda the documentation of AddColumnOperation, verranno visualizzate due proprietà importanti, Column e Table. Column è il ColumnModel creato dal lambda in Up(), c => c.String(nullable: false, annotations: ...).

Nel metodo Genera(), è possibile accedere all'animazione AnnotationValues tramite la proprietà Annotations dello ColumnModel.

Per generare il DDL che aggiunge il vincolo, è necessario generare l'SQL e chiamare il metodo Statement(). Per esempio:

internal class CustomSqlServerMigrationSqlGenerator : SqlServerMigrationSqlGenerator 
{ 
    protected override void Generate(AddColumnOperation addColumnOperation) 
    { 
     base.Generate(addColumnOperation); 

     var column = addColumnOperation.Column; 
     if (column.Type == System.Data.Entity.Core.Metadata.Edm.PrimitiveTypeKind.String) 
     { 
      var annotations = column.Annotations; 
      AnnotationValues minLengthValues; 
      if (annotations.TryGetValue("minLength", out minLengthValues)) 
      { 
       var minLength = Convert.ToInt32(minLengthValues.NewValue); 
       if (minLength > 0) 
       { 
        if (Convert.ToString(column.DefaultValue).Trim().Length < minLength) 
        { 
         throw new ArgumentException(String.Format("minLength {0} specified for {1}.{2}, but the default value, '{3}', does not satisfy this requirement.", minLength, addColumnOperation.Table, column.Name, column.DefaultValue)); 
        } 

        using (var writer = new StringWriter()) 
        { 
         writer.Write("ALTER TABLE "); 
         writer.Write(Name(addColumnOperation.Table)); 
         writer.Write(" ADD CONSTRAINT "); 
         writer.Write(Quote("ML_" + addColumnOperation.Table + "_" + column.Name)); 
         writer.Write(" CHECK (LEN(LTRIM(RTRIM({0}))) > {1})", Quote(column.Name), minLength); 
         Statement(writer.ToString()); 
        } 
       } 
      } 
     } 
    } 
} 

Se si esegue Update-Database -Verbose, si vedrà un eccezione generata da CustomSqlServerMigrationSqlGenerator:

 
minLength 5 specified for dbo.MyEntity.Name, but the default value, '', does not satisfy this requirement. 

Per risolvere questo problema, specificare un defaultValue nella Up() che è più lungo del lunghezza minima (ad es "unknown"):

public override void Up() 
    { 
     AddColumn("dbo.MyEntity", "Name", c => c.String(nullable: false, defaultValue: "unknown", 
      annotations: new Dictionary<string, AnnotationValues> 
      { 
       { 
        "minLength", 
        new AnnotationValues(oldValue: null, newValue: "5") 
       }, 
      })); 
    } 

Ora, se si ri-run Update-Database -Verbose, si vedrà la ALTER TABLE dichiarazione che aggiunge la colonna e la dichiarazione ALTER TABLE che aggiunge il vincolo:

 
ALTER TABLE [dbo].[MyEntity] ADD [Name] [nvarchar](max) NOT NULL DEFAULT 'unknown' 
ALTER TABLE [dbo].[MyEntity] ADD CONSTRAINT [ML_dbo.MyEntity_Name] CHECK (LEN(LTRIM(RTRIM([Name]))) > 5) 

Consulta anche: EF6: Writing Your Own Code First Migration Operations, che mostra come implementare un'operazione di migrazione personalizzata.