2012-04-09 16 views
18

Estoy utilizando primero el código de Entity Framework 4.3.1 con migraciones explícitas. ¿Cómo agrego descripciones para las columnas en las clases de configuración de entidades o en las migraciones, de modo que termine como la descripción de una columna en el servidor SQL (por ejemplo, 2008 R2)?¿Cómo agregar la descripción a las columnas en el código de Entity Framework 4.3 primero usando migraciones?

sé Es probable que pueda escribir un método de extensión para la clase DbMigration que registrar el sp_updateextendedproperty o sp_addextendedproperty llamada a procedimiento como una operación de migración de SQL dentro de la transacción de la migración y llamar a esa extensión después de la creación de tablas en el Up método de migración. Pero, ¿hay una forma elegante construida que aún no he descubierto? Sería bueno tener un atributo que la lógica de detección de cambios de las migraciones pueda captar y generar llamadas de método apropiadas en la migración con scaffolded.

+1

¿No necesita agregar un Atributo de DataAnnotaciones? –

Respuesta

10

Necesitaba esto también. Así que pasé un día y aquí está:

El Código

public class DbDescriptionUpdater<TContext> 
     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+\]\.)?\[(?<table>.*)\]"); 
      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<T>(this DbContext context) where T : class 
     { 
      ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext; 

      return objectContext.GetTableName<T>(); 
     } 

     public static string GetTableName<T>(this ObjectContext context) where T : class 
     { 
      string sql = context.CreateObjectSet<T>().ToTraceString(); 
      Regex regex = new Regex("FROM (?<table>.*) AS"); 
      Match match = regex.Match(sql); 

      string table = match.Groups["table"].Value; 
      return table; 
     } 
    } 

Cómo utilizar

En el archivo de Migrations/Configuration.cs, añadir esto al final del método Seed:

DbDescriptionUpdater<ContextClass> updater = new DbDescriptionUpdater<ContextClass>(context); 
updater.UpdateDatabaseDescriptions(); 

Luego, en Package Manager Console escriba update-database y presiona Enter. Eso es todo.

El código usa el atributo [Display(Name="Description here")] en las propiedades de clase de su entidad para establecer la descripción.

Informe cualquier error o sugiera mejoras.

Gracias a

He usado estos códigos de otras personas y quiero decir gracias:

adding a column description

Check if a class is derived from a generic class

Get Database Table Name from Entity Framework MetaData

Generics in C#, using type of a variable as parameter

+1

Crea una pequeña clase. Un par de recomendaciones. 1) Ajustar con SetColumnDescription() con la verificación de propiedades virtuales. Solo agregue accesorios que no sean virtuales. 2) Cree un atributo personalizado en lugar de usar la Pantalla. public class DbTableMetaAttribute: Attribute { private string _description; cadena virtual pública Descripción { get {return _description; } set {_description = value; } } } – gnome

+0

Puede encontrar una solución más limpia para GetTableName en http://romiller.com/2014/04/08/ef6-1-mapping-between-types-tables/ –

2

Nota bastante satisfecho con la respuesta actual (¡pero con los accesorios para el trabajo!), Quería una manera de obtener el marcado de comentario existente en mis clases en lugar de usar atributos. Y en mi opinión, no sé por qué diablos Microsoft no apoyó esto ya que parece obvio que debería estar allí.

primer lugar, encienda el archivo de documentación XML: Proyecto Properties-> Generar-> documentación XML Archivo-> App_Data \ YourProjectName.XML

En segundo lugar, incluye el archivo como un recurso incrustado.Cree su proyecto, vaya a App_Data, muestre los archivos ocultos e incluya el archivo XML que se generó. Seleccione el recurso incrustado y Copie si es más nuevo (esto es opcional, podría especificar la ruta explícitamente, pero en mi opinión esto es más limpio). Tenga en cuenta que debe usar este método, ya que el marcado no está presente en el ensamblaje y le evitará ubicar dónde está almacenado su XML.

Aquí está la implementación del código, que es una versión modificada de la respuesta aceptada:

public class SchemaDescriptionUpdater<TContext> 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+\]\.)?\[(?<table>.*)\]"); 
     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<SqlParameter> 
     { 
      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<T>(this DbContext context) where T : class 
    { 
     ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext; 

     return objectContext.GetTableName<T>(); 
    } 

    public static string GetTableName<T>(this ObjectContext context) where T : class 
    { 
     string sql = context.CreateObjectSet<T>().ToTraceString(); 
     Regex regex = new Regex("FROM (?<table>.*) AS"); 
     Match match = regex.Match(sql); 

     string table = match.Groups["table"].Value; 
     return table; 
    } 
} 

Y la clase que obtiene la marca de comentario de la Visual Studio genera XML archivo de documentación:

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

    /// <summary> 
    /// Retrievethe XML comments documentation for a given resource 
    /// Eg. ITN.Data.Models.Entity.TestObject.MethodName 
    /// </summary> 
    /// <returns></returns> 
    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; 
    } 

    /// <summary> 
    /// Retrievethe XML comments documentation for a given resource 
    /// Eg. ITN.Data.Models.Entity.TestObject.MethodName 
    /// </summary> 
    /// <returns></returns> 
    public ObjectDocumentation[] GetCommentsForResource(Type objectType) 
    { 
     List<ObjectDocumentation> comments = new List<ObjectDocumentation>(); 
     string resourcePath = objectType.FullName; 

     PropertyInfo[] properties = objectType.GetProperties(); 
     FieldInfo[] fields = objectType.GetFields(); 
     List<ObjectDocumentation> objectNames = new List<ObjectDocumentation>(); 
     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(); 
    } 

    /// <summary> 
    /// Retrievethe XML comments documentation for a given resource 
    /// </summary> 
    /// <param name="objectType">The type of class to retrieve documenation on</param> 
    /// <param name="propertyName">The name of the property in the specified class</param> 
    /// <param name="resourceType"></param> 
    /// <returns></returns> 
    public string GetCommentsForResource(Type objectType, string propertyName, XmlResourceType resourceType) 
    { 
     List<ObjectDocumentation> comments = new List<ObjectDocumentation>(); 
     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 
} 
0

gracias Mr.Mahmoodvcs por la gran solución. me permite modificarlo basta con sustituir "DisplayAttribute" con "DescriptionAttribute" insted de utilizar:

[Display(Name="Description here")] 

que va a utilizar:

[Description("Description here")] 

lo que incluye la mesa también.

public class DbDescriptionUpdater<TContext> 
    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+\]\.)?\[(?<table>.*)\]"); 
     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<T>(this DbContext context) where T : class 
    { 
     ObjectContext objectContext = ((IObjectContextAdapter)context).ObjectContext; 

     return objectContext.GetTableName<T>(); 
    } 

    public static string GetTableName<T>(this ObjectContext context) where T : class 
    { 
     string sql = context.CreateObjectSet<T>().ToTraceString(); 
     Regex regex = new Regex("FROM (?<table>.*) AS"); 
     Match match = regex.Match(sql); 

     string table = match.Groups["table"].Value; 
     return table; 
    } 
} 
Cuestiones relacionadas