Come aggiungere la descrizione alle colonne nel codice Entity Framework 4.3 prima utilizzando le migrazioni?

Sto usando il codice Entity Framework 4.3.1 prima con le migrazioni esplicite. Come posso aggiungere descrizioni per le colonne nelle classi di configurazione delle quadro o nelle migrazioni, in modo che finiscano come descrizione di una colonna nel server SQL (ad es. 2008 R2)?

So che probabilmente posso scrivere un metodo di estensione per la class DbMigration che registra la chiamata di procedura sp_updateextendedproperty o sp_addextendedproperty come un’operazione di migrazione sql all’interno della transazione di migrazione e chiama tale estensione dopo la creazione della tabella nel metodo di migrazione Up . Ma c’è un modo elegante che devo ancora scoprire? Sarebbe utile disporre di un attributo che la logica di rilevamento delle modifiche delle migrazioni possa rilevare e generare chiamate di metodo appropritate nella migrazione dello scaffold.

Anch’io avevo bisogno di questo. Così ho passato un giorno ed eccolo qui:

Il codice

  public class DbDescriptionUpdater where TContext : System.Data.Entity.DbContext { public DbDescriptionUpdater(TContext context) { this.context = context; } Type contextType; TContext context; DbTransaction transaction; public void UpdateDatabaseDescriptions() { contextType = typeof(TContext); this.context = context; var props = contextType.GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); transaction = null; try { context.Database.Connection.Open(); transaction = context.Database.Connection.BeginTransaction(); foreach (var prop in props) { if (prop.PropertyType.InheritsOrImplements((typeof(DbSet<>)))) { var tableType = prop.PropertyType.GetGenericArguments()[0]; SetTableDescriptions(tableType); } } transaction.Commit(); } catch { if (transaction != null) transaction.Rollback(); throw; } finally { if (context.Database.Connection.State == System.Data.ConnectionState.Open) context.Database.Connection.Close(); } } private void SetTableDescriptions(Type tableType) { string fullTableName = context.GetTableName(tableType); Regex regex = new Regex(@"(\[\w+\]\.)?\[(?.*)\]"); Match match = regex.Match(fullTableName); string tableName; if (match.Success) tableName = match.Groups["table"].Value; else tableName = fullTableName; var tableAttrs = tableType.GetCustomAttributes(typeof(TableAttribute), false); if (tableAttrs.Length > 0) tableName = ((TableAttribute)tableAttrs[0]).Name; foreach (var prop in tableType.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)) { if (prop.PropertyType.IsClass && prop.PropertyType != typeof(string)) continue; var attrs = prop.GetCustomAttributes(typeof(DisplayAttribute), false); if (attrs.Length > 0) SetColumnDescription(tableName, prop.Name, ((DisplayAttribute)attrs[0]).Name); } } private void SetColumnDescription(string tableName, string columnName, string description) { string strGetDesc = "select [value] from fn_listextendedproperty('MS_Description','schema','dbo','table',N'" + tableName + "','column',null) where objname = N'" + columnName + "';"; var prevDesc = RunSqlScalar(strGetDesc); if (prevDesc == null) { RunSql(@"EXEC sp_addextendedproperty @name = N'MS_Description', @value = @desc, @level0type = N'Schema', @level0name = 'dbo', @level1type = N'Table', @level1name = @table, @level2type = N'Column', @level2name = @column;", new SqlParameter("@table", tableName), new SqlParameter("@column", columnName), new SqlParameter("@desc", description)); } else { RunSql(@"EXEC sp_updateextendedproperty @name = N'MS_Description', @value = @desc, @level0type = N'Schema', @level0name = 'dbo', @level1type = N'Table', @level1name = @table, @level2type = N'Column', @level2name = @column;", new SqlParameter("@table", tableName), new SqlParameter("@column", columnName), new SqlParameter("@desc", description)); } } DbCommand CreateCommand(string cmdText, params SqlParameter[] parameters) { var cmd = context.Database.Connection.CreateCommand(); cmd.CommandText = cmdText; cmd.Transaction = transaction; foreach (var p in parameters) cmd.Parameters.Add(p); return cmd; } void RunSql(string cmdText, params SqlParameter[] parameters) { var cmd = CreateCommand(cmdText, parameters); cmd.ExecuteNonQuery(); } object RunSqlScalar(string cmdText, params SqlParameter[] parameters) { var cmd = CreateCommand(cmdText, parameters); return cmd.ExecuteScalar(); } } public static class ReflectionUtil { public static bool InheritsOrImplements(this Type child, Type parent) { parent = ResolveGenericTypeDefinition(parent); var currentChild = child.IsGenericType ? child.GetGenericTypeDefinition() : child; while (currentChild != typeof(object)) { if (parent == currentChild || HasAnyInterfaces(parent, currentChild)) return true; currentChild = currentChild.BaseType != null && currentChild.BaseType.IsGenericType ? currentChild.BaseType.GetGenericTypeDefinition() : currentChild.BaseType; if (currentChild == null) return false; } return false; } private static bool HasAnyInterfaces(Type parent, Type child) { return child.GetInterfaces() .Any(childInterface => { var currentInterface = childInterface.IsGenericType ? childInterface.GetGenericTypeDefinition() : childInterface; return currentInterface == parent; }); } private static Type ResolveGenericTypeDefinition(Type parent) { var shouldUseGenericType = true; if (parent.IsGenericType && parent.GetGenericTypeDefinition() != parent) shouldUseGenericType = false; if (parent.IsGenericType && shouldUseGenericType) parent = parent.GetGenericTypeDefinition(); return parent; } } public static class ContextExtensions { public static string GetTableName(this DbContext context, Type tableType) { MethodInfo method = typeof(ContextExtensions).GetMethod("GetTableName", new Type[] { typeof(DbContext) }) .MakeGenericMethod(new Type[] { tableType }); return (string)method.Invoke(context, new object[] { context }); } public static string GetTableName(this DbContext context) where T : class { ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext; return objectContext.GetTableName(); } public static string GetTableName(this ObjectContext context) where T : class { string sql = context.CreateObjectSet().ToTraceString(); Regex regex = new Regex("FROM (?
.*) AS"); Match match = regex.Match(sql); string table = match.Groups["table"].Value; return table; } }

Come usare

Nel tuo file Migrations/Configuration.cs , aggiungi questo alla fine del metodo Seed :

 DbDescriptionUpdater updater = new DbDescriptionUpdater(context); updater.UpdateDatabaseDescriptions(); 

Quindi, in Package Manager Console, digitare update-database e premere Invio. Questo è tutto.

Il codice utilizza l’attributo [Display(Name="Description here")] sulle proprietà della class di quadro per impostare la descrizione.

Si prega di segnalare eventuali bug o suggerire miglioramenti.

Grazie a

Ho usato questi codici da altre persone e voglio ringraziare:

aggiungendo una descrizione di colonna

Controlla se una class è derivata da una class generica

Ottieni il nome della tabella del database da MetaData di Entity Framework

Generici in C #, usando il tipo di una variabile come parametro

Nota abbastanza soddisfatto della risposta corrente (ma gli oggetti di scena per il lavoro!), Volevo un modo per tirare il markup del commento esistente nelle mie classi invece di usare gli attributi. E a mio parere, non so perché diavolo Microsoft non ha supportato questo come sembra ovvio che dovrebbe essere lì!

Innanzitutto, triggers il file di documentazione XML: Proprietà progetto-> Crea-> File di documentazione XML-> App_Data \ YourProjectName.XML

In secondo luogo, includere il file come risorsa incorporata. Costruisci il tuo progetto, vai su App_Data, mostra i file nascosti e includi il file XML che è stato generato. Seleziona la risorsa incorporata e Copia se più recente (questo è facoltativo, puoi specificare esplicitamente il percorso ma a mio parere è più pulito). Nota, è necessario utilizzare questo metodo poiché il markup non è presente nell’assembly e ti eviterà di individuare il punto in cui è memorizzato il tuo XML.

Ecco l’implementazione del codice che è una versione modificata della risposta accettata:

 public class SchemaDescriptionUpdater where TContext : DbContext { Type contextType; TContext context; DbTransaction transaction; XmlAnnotationReader reader; public SchemaDescriptionUpdater(TContext context) { this.context = context; reader = new XmlAnnotationReader(); } public SchemaDescriptionUpdater(TContext context, string xmlDocumentationPath) { this.context = context; reader = new XmlAnnotationReader(xmlDocumentationPath); } public void UpdateDatabaseDescriptions() { contextType = typeof(TContext); var props = contextType.GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); transaction = null; try { context.Database.Connection.Open(); transaction = context.Database.Connection.BeginTransaction(); foreach (var prop in props) { if (prop.PropertyType.InheritsOrImplements((typeof(DbSet<>)))) { var tableType = prop.PropertyType.GetGenericArguments()[0]; SetTableDescriptions(tableType); } } transaction.Commit(); } catch { if (transaction != null) transaction.Rollback(); throw; } finally { if (context.Database.Connection.State == System.Data.ConnectionState.Open) context.Database.Connection.Close(); } } private void SetTableDescriptions(Type tableType) { string fullTableName = context.GetTableName(tableType); Regex regex = new Regex(@"(\[\w+\]\.)?\[(?.*)\]"); Match match = regex.Match(fullTableName); string tableName; if (match.Success) tableName = match.Groups["table"].Value; else tableName = fullTableName; var tableAttrs = tableType.GetCustomAttributes(typeof(TableAttribute), false); if (tableAttrs.Length > 0) tableName = ((TableAttribute)tableAttrs[0]).Name; // set the description for the table string tableComment = reader.GetCommentsForResource(tableType, null, XmlResourceType.Type); if (!string.IsNullOrEmpty(tableComment)) SetDescriptionForObject(tableName, null, tableComment); // get all of the documentation for each property/column ObjectDocumentation[] columnComments = reader.GetCommentsForResource(tableType); foreach (var column in columnComments) { SetDescriptionForObject(tableName, column.PropertyName, column.Documentation); } } private void SetDescriptionForObject(string tableName, string columnName, string description) { string strGetDesc = ""; // determine if there is already an extended description if(string.IsNullOrEmpty(columnName)) strGetDesc = "select [value] from fn_listextendedproperty('MS_Description','schema','dbo','table',N'" + tableName + "',null,null);"; else strGetDesc = "select [value] from fn_listextendedproperty('MS_Description','schema','dbo','table',N'" + tableName + "','column',null) where objname = N'" + columnName + "';"; var prevDesc = (string)RunSqlScalar(strGetDesc); var parameters = new List { new SqlParameter("@table", tableName), new SqlParameter("@desc", description) }; // is it an update, or new? string funcName = "sp_addextendedproperty"; if (!string.IsNullOrEmpty(prevDesc)) funcName = "sp_updateextendedproperty"; string query = @"EXEC " + funcName + @" @name = N'MS_Description', @value = @desc,@level0type = N'Schema', @level0name = 'dbo',@level1type = N'Table', @level1name = @table"; // if a column is specified, add a column description if (!string.IsNullOrEmpty(columnName)) { parameters.Add(new SqlParameter("@column", columnName)); query += ", @level2type = N'Column', @level2name = @column"; } RunSql(query, parameters.ToArray()); } DbCommand CreateCommand(string cmdText, params SqlParameter[] parameters) { var cmd = context.Database.Connection.CreateCommand(); cmd.CommandText = cmdText; cmd.Transaction = transaction; foreach (var p in parameters) cmd.Parameters.Add(p); return cmd; } void RunSql(string cmdText, params SqlParameter[] parameters) { var cmd = CreateCommand(cmdText, parameters); cmd.ExecuteNonQuery(); } object RunSqlScalar(string cmdText, params SqlParameter[] parameters) { var cmd = CreateCommand(cmdText, parameters); return cmd.ExecuteScalar(); } } public static class ReflectionUtil { public static bool InheritsOrImplements(this Type child, Type parent) { parent = ResolveGenericTypeDefinition(parent); var currentChild = child.IsGenericType ? child.GetGenericTypeDefinition() : child; while (currentChild != typeof(object)) { if (parent == currentChild || HasAnyInterfaces(parent, currentChild)) return true; currentChild = currentChild.BaseType != null && currentChild.BaseType.IsGenericType ? currentChild.BaseType.GetGenericTypeDefinition() : currentChild.BaseType; if (currentChild == null) return false; } return false; } private static bool HasAnyInterfaces(Type parent, Type child) { return child.GetInterfaces() .Any(childInterface => { var currentInterface = childInterface.IsGenericType ? childInterface.GetGenericTypeDefinition() : childInterface; return currentInterface == parent; }); } private static Type ResolveGenericTypeDefinition(Type parent) { var shouldUseGenericType = true; if (parent.IsGenericType && parent.GetGenericTypeDefinition() != parent) shouldUseGenericType = false; if (parent.IsGenericType && shouldUseGenericType) parent = parent.GetGenericTypeDefinition(); return parent; } } public static class ContextExtensions { public static string GetTableName(this DbContext context, Type tableType) { MethodInfo method = typeof(ContextExtensions).GetMethod("GetTableName", new Type[] { typeof(DbContext) }) .MakeGenericMethod(new Type[] { tableType }); return (string)method.Invoke(context, new object[] { context }); } public static string GetTableName(this DbContext context) where T : class { ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext; return objectContext.GetTableName(); } public static string GetTableName(this ObjectContext context) where T : class { string sql = context.CreateObjectSet().ToTraceString(); Regex regex = new Regex("FROM (?
.*) AS"); Match match = regex.Match(sql); string table = match.Groups["table"].Value; return table; } }

E la class che ottiene il markup del commento dallo studio di visualizzazione ha generato un file di documentazione XML:

 public class XmlAnnotationReader { public string XmlPath { get; protected internal set; } public XmlDocument Document { get; protected internal set; } public XmlAnnotationReader() { var assembly = Assembly.GetExecutingAssembly(); string resourceName = String.Format("{0}.App_Data.{0}.XML", assembly.GetName().Name); this.XmlPath = resourceName; using (Stream stream = assembly.GetManifestResourceStream(resourceName)) { using (StreamReader reader = new StreamReader(stream)) { XmlDocument doc = new XmlDocument(); //string result = reader.ReadToEnd(); doc.Load(reader); this.Document = doc; } } } public XmlAnnotationReader(string xmlPath) { this.XmlPath = xmlPath; if (File.Exists(xmlPath)) { XmlDocument doc = new XmlDocument(); doc.Load(this.XmlPath); this.Document = doc; } else throw new FileNotFoundException(String.Format("Could not find the XmlDocument at the specified path: {0}\r\nCurrent Path: {1}", xmlPath, Assembly.GetExecutingAssembly().Location)); } ///  /// Retrievethe XML comments documentation for a given resource /// Eg. ITN.Data.Models.Entity.TestObject.MethodName ///  ///  public string GetCommentsForResource(string resourcePath, XmlResourceType type) { XmlNode node = Document.SelectSingleNode(String.Format("//member[starts-with(@name, '{0}:{1}')]/summary", GetObjectTypeChar(type), resourcePath)); if (node != null) { string xmlResult = node.InnerText; string trimmedResult = Regex.Replace(xmlResult, @"\s+", " "); return trimmedResult; } return string.Empty; } ///  /// Retrievethe XML comments documentation for a given resource /// Eg. ITN.Data.Models.Entity.TestObject.MethodName ///  ///  public ObjectDocumentation[] GetCommentsForResource(Type objectType) { List comments = new List(); string resourcePath = objectType.FullName; PropertyInfo[] properties = objectType.GetProperties(); FieldInfo[] fields = objectType.GetFields(); List objectNames = new List(); objectNames.AddRange(properties.Select(x => new ObjectDocumentation() { PropertyName = x.Name, Type = XmlResourceType.Property }).ToList()); objectNames.AddRange(properties.Select(x => new ObjectDocumentation() { PropertyName = x.Name, Type = XmlResourceType.Field }).ToList()); foreach (var property in objectNames) { XmlNode node = Document.SelectSingleNode(String.Format("//member[starts-with(@name, '{0}:{1}.{2}')]/summary", GetObjectTypeChar(property.Type), resourcePath, property.PropertyName )); if (node != null) { string xmlResult = node.InnerText; string trimmedResult = Regex.Replace(xmlResult, @"\s+", " "); property.Documentation = trimmedResult; comments.Add(property); } } return comments.ToArray(); } ///  /// Retrievethe XML comments documentation for a given resource ///  /// The type of class to retrieve documenation on /// The name of the property in the specified class ///  ///  public string GetCommentsForResource(Type objectType, string propertyName, XmlResourceType resourceType) { List comments = new List(); string resourcePath = objectType.FullName; string scopedElement = resourcePath; if (propertyName != null && resourceType != XmlResourceType.Type) scopedElement += "." + propertyName; XmlNode node = Document.SelectSingleNode(String.Format("//member[starts-with(@name, '{0}:{1}')]/summary", GetObjectTypeChar(resourceType), scopedElement)); if (node != null) { string xmlResult = node.InnerText; string trimmedResult = Regex.Replace(xmlResult, @"\s+", " "); return trimmedResult; } return string.Empty; } private string GetObjectTypeChar(XmlResourceType type) { switch (type) { case XmlResourceType.Field: return "F"; case XmlResourceType.Method: return "M"; case XmlResourceType.Property: return "P"; case XmlResourceType.Type: return "T"; } return string.Empty; } } public class ObjectDocumentation { public string PropertyName { get; set; } public string Documentation { get; set; } public XmlResourceType Type { get; set; } } public enum XmlResourceType { Method, Property, Field, Type } 

non puoi usare il metodo ExceuteSqlCommand . Qui puoi definire in modo esplicito qualunque meta proprietà vuoi aggiungere nella tua tabella.

http://msdn.microsoft.com/en-us/library/system.data.entity.database.executesqlcommand(v=vs.103).aspx

grazie Mr.Mahmoodvcs per la grande soluzione. permettimi di modificarlo basta sostituire “DisplayAttribute” con “DescriptionAttribute” per utilizzare:

 [Display(Name="Description here")] 

userai:

 [Description("Description here")] 

quindi include anche il tavolo.

  public class DbDescriptionUpdater where TContext : System.Data.Entity.DbContext { public DbDescriptionUpdater(TContext context) { this.context = context; } Type contextType; TContext context; DbTransaction transaction; public void UpdateDatabaseDescriptions() { contextType = typeof(TContext); this.context = context; var props = contextType.GetProperties(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public); transaction = null; try { context.Database.Connection.Open(); transaction = context.Database.Connection.BeginTransaction(); foreach (var prop in props) { if (prop.PropertyType.InheritsOrImplements((typeof(DbSet<>)))) { var tableType = prop.PropertyType.GetGenericArguments()[0]; SetTableDescriptions(tableType); } } transaction.Commit(); } catch { if (transaction != null) transaction.Rollback(); throw; } finally { if (context.Database.Connection.State == System.Data.ConnectionState.Open) context.Database.Connection.Close(); } } private void SetTableDescriptions(Type tableType) { string fullTableName = context.GetTableName(tableType); Regex regex = new Regex(@"(\[\w+\]\.)?\[(?.*)\]"); Match match = regex.Match(fullTableName); string tableName; if (match.Success) tableName = match.Groups["table"].Value; else tableName = fullTableName; var tableAttrs = tableType.GetCustomAttributes(typeof(TableAttribute), false); if (tableAttrs.Length > 0) tableName = ((TableAttribute)tableAttrs[0]).Name; var table_attrs = tableType.GetCustomAttributes(typeof(DescriptionAttribute), false); if (table_attrs != null && table_attrs.Length > 0) SetTableDescription(tableName, ((DescriptionAttribute)table_attrs[0]).Description); foreach (var prop in tableType.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)) { if (prop.PropertyType.IsClass && prop.PropertyType != typeof(string)) continue; var attrs = prop.GetCustomAttributes(typeof(DescriptionAttribute), false); if (attrs != null && attrs.Length > 0) SetColumnDescription(tableName, prop.Name, ((DescriptionAttribute)attrs[0]).Description); } } private void SetColumnDescription(string tableName, string columnName, string description) { string strGetDesc = "select [value] from fn_listextendedproperty('MS_Description','schema','dbo','table',N'" + tableName + "','column',null) where objname = N'" + columnName + "';"; var prevDesc = RunSqlScalar(strGetDesc); if (prevDesc == null) { RunSql(@"EXEC sp_addextendedproperty @name = N'MS_Description', @value = @desc, @level0type = N'Schema', @level0name = 'dbo', @level1type = N'Table', @level1name = @table, @level2type = N'Column', @level2name = @column;", new SqlParameter("@table", tableName), new SqlParameter("@column", columnName), new SqlParameter("@desc", description)); } else { RunSql(@"EXEC sp_updateextendedproperty @name = N'MS_Description', @value = @desc, @level0type = N'Schema', @level0name = 'dbo', @level1type = N'Table', @level1name = @table, @level2type = N'Column', @level2name = @column;", new SqlParameter("@table", tableName), new SqlParameter("@column", columnName), new SqlParameter("@desc", description)); } } private void SetTableDescription(string tableName, string description) { string strGetDesc = "select [value] from fn_listextendedproperty('MS_Description','schema','dbo','table',N'" + tableName + "',null,null);"; var prevDesc = RunSqlScalar(strGetDesc); if (prevDesc == null) { RunSql(@"EXEC sp_addextendedproperty @name = N'MS_Description', @value = @desc, @level0type = N'Schema', @level0name = 'dbo', @level1type = N'Table', @level1name = @table;", new SqlParameter("@table", tableName), new SqlParameter("@desc", description)); } else { RunSql(@"EXEC sp_updateextendedproperty @name = N'MS_Description', @value = @desc, @level0type = N'Schema', @level0name = 'dbo', @level1type = N'Table', @level1name = @table;", new SqlParameter("@table", tableName), new SqlParameter("@desc", description)); } } DbCommand CreateCommand(string cmdText, params SqlParameter[] parameters) { var cmd = context.Database.Connection.CreateCommand(); cmd.CommandText = cmdText; cmd.Transaction = transaction; foreach (var p in parameters) cmd.Parameters.Add(p); return cmd; } void RunSql(string cmdText, params SqlParameter[] parameters) { var cmd = CreateCommand(cmdText, parameters); cmd.ExecuteNonQuery(); } object RunSqlScalar(string cmdText, params SqlParameter[] parameters) { var cmd = CreateCommand(cmdText, parameters); return cmd.ExecuteScalar(); } } public static class ReflectionUtil { public static bool InheritsOrImplements(this Type child, Type parent) { parent = ResolveGenericTypeDefinition(parent); var currentChild = child.IsGenericType ? child.GetGenericTypeDefinition() : child; while (currentChild != typeof(object)) { if (parent == currentChild || HasAnyInterfaces(parent, currentChild)) return true; currentChild = currentChild.BaseType != null && currentChild.BaseType.IsGenericType ? currentChild.BaseType.GetGenericTypeDefinition() : currentChild.BaseType; if (currentChild == null) return false; } return false; } private static bool HasAnyInterfaces(Type parent, Type child) { return child.GetInterfaces() .Any(childInterface => { var currentInterface = childInterface.IsGenericType ? childInterface.GetGenericTypeDefinition() : childInterface; return currentInterface == parent; }); } private static Type ResolveGenericTypeDefinition(Type parent) { var shouldUseGenericType = true; if (parent.IsGenericType && parent.GetGenericTypeDefinition() != parent) shouldUseGenericType = false; if (parent.IsGenericType && shouldUseGenericType) parent = parent.GetGenericTypeDefinition(); return parent; } } public static class ContextExtensions { public static string GetTableName(this DbContext context, Type tableType) { MethodInfo method = typeof(ContextExtensions).GetMethod("GetTableName", new Type[] { typeof(DbContext) }) .MakeGenericMethod(new Type[] { tableType }); return (string)method.Invoke(context, new object[] { context }); } public static string GetTableName(this DbContext context) where T : class { ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext; return objectContext.GetTableName(); } public static string GetTableName(this ObjectContext context) where T : class { string sql = context.CreateObjectSet().ToTraceString(); Regex regex = new Regex("FROM (?
.*) AS"); Match match = regex.Match(sql); string table = match.Groups["table"].Value; return table; } }

Mentre la domanda riguarda l’EF4, questa risposta si rivolge all’EF6, che dovrebbe essere appropriato dato il tempo trascorso da quando è stata posta la domanda.

Penso che i commenti appartengano ai metodi di migrazione Up e Down piuttosto che a un metodo di Seed .

Quindi, come suggerito da @MichaelBrown, inizia con l’abilitazione dell’output della documentazione XML e includi il file di documentazione come risorsa incorporata nel tuo progetto.

Quindi, consente di trasformare i commenti in un’annotazione di tabella / colonna utilizzando una Convention . Ci sono alcune modifiche da fare per cose come commenti multilinea e sbarazzarsi di spazi vuoti eccessivi.

 public class CommentConvention : Convention { public const string NewLinePlaceholder = "<>"; public CommentConvention() { var docuXml = new XmlDocument(); // Read the documentation xml using (var commentStream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Namespace.Documentation.xml")) { docuXml.Load(commentStream); } // configure class/table comment Types() .Having(pi => docuXml.SelectSingleNode($"//member[starts-with(@name, 'T:{pi?.FullName}')]/summary")) .Configure((c, a) => { c.HasTableAnnotation("Comment", GetCommentTextWithNewlineReplacement(a)); }); // configure property/column comments Properties() .Having(pi => docuXml.SelectSingleNode( $"//member[starts-with(@name, 'P:{pi?.DeclaringType?.FullName}.{pi?.Name}')]/summary")) .Configure((c, a) => { c.HasColumnAnnotation("Comment", GetCommentTextWithNewlineReplacement(a)); }); } // adjust the documentation text to handle newline and whitespace private static string GetCommentTextWithNewlineReplacement(XmlNode a) { if (string.IsNullOrWhiteSpace(a.InnerText)) { return null; } return string.Join( NewLinePlaceholder, a.InnerText.Trim() .Split(new string[] {"\r\n", "\r", "\n"}, StringSplitOptions.None) .Select(line => line.Trim())); } } 

Registrare la convenzione nel metodo OnModelCreating .

Risultato previsto: quando viene creata una nuova migrazione, i commenti saranno inclusi come annotazioni come

 CreateTable( "schema.Table", c => new { Id = c.Decimal(nullable: false, precision: 10, scale: 0, identity: true, annotations: new Dictionary { { "Comment", new AnnotationValues(oldValue: null, newValue: "Commenting the Id Column") }, }), // ... 

Passare alla seconda parte: regolare il generatore SQL per creare commenti dalle annotazioni.

Questo è per Oracle, ma MS Sql dovrebbe essere molto simile

 class CustomOracleSqlCodeGen : MigrationSqlGenerator { // the actual SQL generator private readonly MigrationSqlGenerator _innerSqlGenerator; public CustomOracleSqlCodeGen(MigrationSqlGenerator innerSqlGenerator) { _innerSqlGenerator = innerSqlGenerator; } public override IEnumerable Generate(IEnumerable migrationOperations, string providerManifestToken) { var ms = _innerSqlGenerator.Generate(AddCommentSqlStatements(migrationOperations), providerManifestToken); return ms; } // generate additional SQL operations to produce comments IEnumerable AddCommentSqlStatements(IEnumerable migrationOperations) { foreach (var migrationOperation in migrationOperations) { // the original inputted operation yield return migrationOperation; // create additional operations to produce comments if (migrationOperation is CreateTableOperation cto) { foreach (var ctoAnnotation in cto.Annotations.Where(x => x.Key == "Comment")) { if (ctoAnnotation.Value is string annotation) { var commentString = annotation.Replace( CommentConvention.NewLinePlaceholder, Environment.NewLine); yield return new SqlOperation($"COMMENT ON TABLE {cto.Name} IS '{commentString}'"); } } foreach (var columnModel in cto.Columns) { foreach (var columnModelAnnotation in columnModel.Annotations.Where(x => x.Key == "Comment")) { if (columnModelAnnotation.Value is AnnotationValues annotation) { var commentString = (annotation.NewValue as string)?.Replace( CommentConvention.NewLinePlaceholder, Environment.NewLine); yield return new SqlOperation( $"COMMENT ON COLUMN {cto.Name}.{columnModel.Name} IS '{commentString}'"); } } } } } } } 

Nel costruttore DbMigrationsConfiguration , registrare il nuovo generatore di codice (di nuovo, questo è specifico di Oracle ma sarà simile per altri provider SQL)

 internal sealed class Configuration : DbMigrationsConfiguration { public Configuration() { AutomaticMigrationsEnabled = false; var cg = GetSqlGenerator("Oracle.ManagedDataAccess.Client"); SetSqlGenerator("Oracle.ManagedDataAccess.Client", new CustomOracleSqlCodeGen(cg)); } // ... 

Risultato previsto: le annotazioni dei commenti dei metodi Up e Down vengono tradotte in istruzioni SQL che modificano i commenti nel database.