2012-04-09 5 views
18

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

So che posso probabilmente scrivere un metodo di estensione per la classe DbMigration che registrare la chiamata di procedura sp_updateextendedproperty o sp_addextendedproperty come un'operazione di migrazione SQL all'interno della transazione migrazione e chiamare l'interno, dopo la creazione delle tabelle nel Up metodo di migrazione. 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.

+1

Non è necessario aggiungere un attributo DataAnnotations? –

risposta

10

Avevo bisogno anche questo. Così ho trascorso un giorno e qui è:

Il Codice

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

Come utilizzare

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

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

Quindi in Package Manager Console tipo update-database e premi Invio. Ecco fatto.

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

Si prega di segnalare eventuali bug o suggerire miglioramenti.

Grazie alla

ho usato questi codice da altre persone e voglio dire grazie:

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 piccola classe. Paio di raccomandazioni 1) Avvia con SetColumnDescription() con controllo delle proprietà virtuali. Aggiungi solo oggetti di scena che non sono virtuali. 2) Creare un attributo personalizzato anziché utilizzare Display. public class DbTableMetaAttribute: Attributo { stringa privata _description; stringa virtuale pubblica Descrizione { get {return _description; } set {_description = value; } } } – gnome

+0

È possibile trovare una soluzione più pulita per GetTableName all'indirizzo http://romiller.com/2014/04/08/ef6-1-mapping-between-types-tables/ –

2

Nota abbastanza soddisfatta della risposta corrente (ma 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ì!

In primo luogo, accendere file di documentazione XML: Progetto Proprietà-> Build-> documentazione XML file-> App_Data \ YourProjectName.XML

In secondo luogo, includere il file come una 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<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; 
    } 
} 

E la classe che ottiene il commento markup dallo studio visivo generato XML file di documentazione:

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

grazie Mr.Mahmoodvcs per la grande soluzione. mi permettono di modificarlo basta sostituire "DisplayAttribute" con "DescriptionAttribute" invece di utilizzare:

[Display(Name="Description here")] 

utilizzare:

[Description("Description here")] 

in modo che include la tabella pure.

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