找了很多资料,终于找到一篇真正能解决ef生成字段说明,注释的文章,收藏不了,于是转载
本文章为转载,原文地址
项目中使用了EF框架,使用的是Database-First方式,因为数据库已经存在,所以采用Database-First方式,ef生成实体类的时候,发现微软没有自动生成表字段和表说明,在网上找了些资料,由于都不太全,倒腾了近2个小时,所以根据网上的帖子为基础,写得更详细一点,让初学者更容易明白和少走弯路。网上也有一些自动生成的软件,可以自动生成,但是更新数据库需要重新生成,有点麻烦,所有根据T4模板解决
第一步:
下载GetSummery.ttinclude文件,链接失效,现在贴出文件内容,自己重命名为这个文件就好啦,GetSummery.ttinclude是模板文件,为了实现EF框架添加数据库自动生成实体注释。
<#@ template language="C#v3.5" hostspecific="True" #> <#@ assembly name="EnvDTE" #> <#@ assembly name="System.Data" #> <#@ assembly name="System.Xml" #> <#@ assembly name="System.Configuration" #> <#@ assembly name="System.Windows.Forms" #> <#@ import namespace="System.Collections.Generic" #> <#@ import namespace="System.Data" #> <#@ import namespace="System.Data.SqlClient" #> <#@ import namespace="System.Data.Common" #> <#@ import namespace="System.Diagnostics" #> <#@ import namespace="System.Globalization" #> <#@ import namespace="System.IO" #> <#@ import namespace="System.Linq" #> <#@ import namespace="System.Text" #> <#@ import namespace="System.Text.RegularExpressions" #> <#@ import namespace="System.Configuration" #> <#@ import namespace="System.Windows.Forms" #> <#+ string ConnectionStringName = "MyConn"; string Namespace = ""; string RepoName = ""; string ClassPrefix = ""; string ClassSuffix = ""; string SchemaName = null; bool IncludeViews = false; bool GenerateOperations = false; bool GenerateCommon = true; bool GeneratePocos = true; bool TrackModifiedColumns = false; static Regex rxCleanUp = new Regex(@"[^wd_]", RegexOptions.Compiled); static Func<string, string> CleanUp = (str) => { str = rxCleanUp.Replace(str, "_"); if (char.IsDigit(str[0])) str = "_" + str; return str; }; string GetConnectionString(ref string connectionStringName, out string providerName) { var _CurrentProject = GetCurrentProject(); providerName=null; string result=""; ExeConfigurationFileMap configFile = new ExeConfigurationFileMap(); configFile.ExeConfigFilename = GetConfigPath(); if (string.IsNullOrEmpty(configFile.ExeConfigFilename)) throw new ArgumentNullException("The project does not contain App.config or Web.config file."); var config = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(configFile, ConfigurationUserLevel.None); var connSection=config.ConnectionStrings; //if the connectionString is empty - which is the defauls //look for count-1 - this is the last connection string //and takes into account AppServices and LocalSqlServer if(string.IsNullOrEmpty(connectionStringName)) { if(connSection.ConnectionStrings.Count>1) { connectionStringName = connSection.ConnectionStrings[connSection.ConnectionStrings.Count-1].Name; result=connSection.ConnectionStrings[connSection.ConnectionStrings.Count-1].ConnectionString; providerName=connSection.ConnectionStrings[connSection.ConnectionStrings.Count-1].ProviderName; } } else { try { result=connSection.ConnectionStrings[connectionStringName].ConnectionString; providerName=connSection.ConnectionStrings[connectionStringName].ProviderName; } catch { result="There is no connection string name called '"+connectionStringName+"'"; } } // if (String.IsNullOrEmpty(providerName)) // providerName="System.Data.SqlClient"; return result; } string _connectionString=""; string _providerName=""; void InitConnectionString() { if(String.IsNullOrEmpty(_connectionString)) { _connectionString=GetConnectionString(ref ConnectionStringName, out _providerName); if(_connectionString.Contains("|DataDirectory|")) { //have to replace it string dataFilePath=GetDataDirectory(); _connectionString=_connectionString.Replace("|DataDirectory|",dataFilePath); } } } public string ConnectionString { get { InitConnectionString(); return _connectionString; } } public string ProviderName { get { InitConnectionString(); return _providerName; } } public EnvDTE.Project GetCurrentProject() { IServiceProvider _ServiceProvider = (IServiceProvider)Host; if (_ServiceProvider == null) throw new Exception("Host property returned unexpected value (null)"); EnvDTE.DTE dte = (EnvDTE.DTE)_ServiceProvider.GetService(typeof(EnvDTE.DTE)); if (dte == null) throw new Exception("Unable to retrieve EnvDTE.DTE"); Array activeSolutionProjects = (Array)dte.ActiveSolutionProjects; if (activeSolutionProjects == null) throw new Exception("DTE.ActiveSolutionProjects returned null"); EnvDTE.Project dteProject = (EnvDTE.Project)activeSolutionProjects.GetValue(0); if (dteProject == null) throw new Exception("DTE.ActiveSolutionProjects[0] returned null"); return dteProject; } private string GetProjectPath() { EnvDTE.Project project = GetCurrentProject(); System.IO.FileInfo info = new System.IO.FileInfo(project.FullName); return info.Directory.FullName; } private string GetConfigPath() { EnvDTE.Project project = GetCurrentProject(); foreach (EnvDTE.ProjectItem item in project.ProjectItems) { // if it is the app.config file, then open it up if (item.Name.Equals("App.config",StringComparison.InvariantCultureIgnoreCase) || item.Name.Equals("Web.config",StringComparison.InvariantCultureIgnoreCase)) return GetProjectPath() + "\" + item.Name; } return String.Empty; } public string GetDataDirectory() { EnvDTE.Project project=GetCurrentProject(); return System.IO.Path.GetDirectoryName(project.FileName)+"\App_Data\"; } static string zap_password(string connectionString) { var rx = new Regex("password=.*;", RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.IgnoreCase); return rx.Replace(connectionString, "password=**zapped**;"); } //得到列说明 string getColumnSummery(string tablename,string columnname) { InitConnectionString(); DbProviderFactory _factory; _factory = DbProviderFactories.GetFactory(ProviderName); using(var conn=_factory.CreateConnection()) { conn.ConnectionString=ConnectionString; conn.Open(); //string sql=@"SELECT TOP 1 ex.value FROM sys.columns c LEFT OUTER JOIN sys.extended_properties ex ON ex.major_id = c.object_id AND ex.minor_id = c.column_id AND ex.name = 'MS_Description' left outer join systypes t on c.system_type_id=t.xtype WHERE OBJECTPROPERTY(c.object_id, 'IsMsShipped')=0 "; string sql=@"SELECT TOP 1 ex.value FROM sys.columns c LEFT OUTER JOIN sys.extended_properties ex ON ex.major_id = c.object_id AND ex.minor_id = c.column_id AND ex.name = 'MS_Description' left outer join systypes t on c.system_type_id=t.xtype WHERE OBJECTPROPERTY(c.object_id, 'IsMsShipped')=0 AND OBJECT_NAME(c.object_id)=@tablename AND c.name=@columname "; using (var cmd=_factory.CreateCommand()) { cmd.Connection=conn; cmd.CommandText=sql; var p = cmd.CreateParameter(); p.ParameterName = "@tablename"; p.Value=tablename; cmd.Parameters.Add(p); p = cmd.CreateParameter(); p.ParameterName = "@columname"; p.Value=columnname; cmd.Parameters.Add(p); var result=cmd.ExecuteScalar(); conn.Close(); if(result!=null) return result.ToString(); else return ProviderName; } return ""; } } //得到表说明备注 string getTableSummery(string tablename) { InitConnectionString(); DbProviderFactory _factory; _factory = DbProviderFactories.GetFactory(ProviderName); using(var conn=_factory.CreateConnection()) { conn.ConnectionString=ConnectionString; conn.Open(); //string sql=@"SELECT TOP 1 ex.value FROM sys.columns c LEFT OUTER JOIN sys.extended_properties ex ON ex.major_id = c.object_id AND ex.minor_id = c.column_id AND ex.name = 'MS_Description' left outer join systypes t on c.system_type_id=t.xtype WHERE OBJECTPROPERTY(c.object_id, 'IsMsShipped')=0 "; string sql=@"SELECT TOP 1 TableSumary FROM (select c.Name AS TableName,isnull(f.[value],'') AS TableSumary from sys.columns a left join sys.types b on a.user_type_id=b.user_type_id inner join sys.objects c on a.object_id=c.object_id and c.Type='U' left join syscomments d on a.default_object_id=d.ID left join sys.extended_properties e on e.major_id=c.object_id and e.minor_id=a.Column_id and e.class=1 left join sys.extended_properties f on f.major_id=c.object_id and f.minor_id=0 and f.class=1) AS Mytb WHERE TableName=@tablename "; using (var cmd=_factory.CreateCommand()) { cmd.Connection=conn; cmd.CommandText=sql; var p = cmd.CreateParameter(); p.ParameterName = "@tablename"; p.Value=tablename; cmd.Parameters.Add(p); var result=cmd.ExecuteScalar(); conn.Close(); if(result!=null) return result.ToString(); else return ProviderName; } return ""; } } /// <summary> /// Summary for the Inflector class /// </summary> public static class Inflector { private static readonly List<InflectorRule> _plurals = new List<InflectorRule>(); private static readonly List<InflectorRule> _singulars = new List<InflectorRule>(); private static readonly List<string> _uncountables = new List<string>(); /// <summary> /// Initializes the <see cref="Inflector"/> class. /// </summary> static Inflector() { AddPluralRule("$", "s"); AddPluralRule("s$", "s"); AddPluralRule("(ax|test)is$", "$1es"); AddPluralRule("(octop|vir)us$", "$1i"); AddPluralRule("(alias|status)$", "$1es"); AddPluralRule("(bu)s$", "$1ses"); AddPluralRule("(buffal|tomat)o$", "$1oes"); AddPluralRule("([ti])um$", "$1a"); AddPluralRule("sis$", "ses"); AddPluralRule("(?:([^f])fe|([lr])f)$", "$1$2ves"); AddPluralRule("(hive)$", "$1s"); AddPluralRule("([^aeiouy]|qu)y$", "$1ies"); AddPluralRule("(x|ch|ss|sh)$", "$1es"); AddPluralRule("(matr|vert|ind)ix|ex$", "$1ices"); AddPluralRule("([m|l])ouse$", "$1ice"); AddPluralRule("^(ox)$", "$1en"); AddPluralRule("(quiz)$", "$1zes"); AddSingularRule("s$", String.Empty); AddSingularRule("ss$", "ss"); AddSingularRule("(n)ews$", "$1ews"); AddSingularRule("([ti])a$", "$1um"); AddSingularRule("((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$", "$1$2sis"); AddSingularRule("(^analy)ses$", "$1sis"); AddSingularRule("([^f])ves$", "$1fe"); AddSingularRule("(hive)s$", "$1"); AddSingularRule("(tive)s$", "$1"); AddSingularRule("([lr])ves$", "$1f"); AddSingularRule("([^aeiouy]|qu)ies$", "$1y"); AddSingularRule("(s)eries$", "$1eries"); AddSingularRule("(m)ovies$", "$1ovie"); AddSingularRule("(x|ch|ss|sh)es$", "$1"); AddSingularRule("([m|l])ice$", "$1ouse"); AddSingularRule("(bus)es$", "$1"); AddSingularRule("(o)es$", "$1"); AddSingularRule("(shoe)s$", "$1"); AddSingularRule("(cris|ax|test)es$", "$1is"); AddSingularRule("(octop|vir)i$", "$1us"); AddSingularRule("(alias|status)$", "$1"); AddSingularRule("(alias|status)es$", "$1"); AddSingularRule("^(ox)en", "$1"); AddSingularRule("(vert|ind)ices$", "$1ex"); AddSingularRule("(matr)ices$", "$1ix"); AddSingularRule("(quiz)zes$", "$1"); AddIrregularRule("person", "people"); AddIrregularRule("man", "men"); AddIrregularRule("child", "children"); AddIrregularRule("sex", "sexes"); AddIrregularRule("tax", "taxes"); AddIrregularRule("move", "moves"); AddUnknownCountRule("equipment"); AddUnknownCountRule("information"); AddUnknownCountRule("rice"); AddUnknownCountRule("money"); AddUnknownCountRule("species"); AddUnknownCountRule("series"); AddUnknownCountRule("fish"); AddUnknownCountRule("sheep"); } /// <summary> /// Adds the irregular rule. /// </summary> /// <param name="singular">The singular.</param> /// <param name="plural">The plural.</param> private static void AddIrregularRule(string singular, string plural) { AddPluralRule(String.Concat("(", singular[0], ")", singular.Substring(1), "$"), String.Concat("$1", plural.Substring(1))); AddSingularRule(String.Concat("(", plural[0], ")", plural.Substring(1), "$"), String.Concat("$1", singular.Substring(1))); } /// <summary> /// Adds the unknown count rule. /// </summary> /// <param name="word">The word.</param> private static void AddUnknownCountRule(string word) { _uncountables.Add(word.ToLower()); } /// <summary> /// Adds the plural rule. /// </summary> /// <param name="rule">The rule.</param> /// <param name="replacement">The replacement.</param> private static void AddPluralRule(string rule, string replacement) { _plurals.Add(new InflectorRule(rule, replacement)); } /// <summary> /// Adds the singular rule. /// </summary> /// <param name="rule">The rule.</param> /// <param name="replacement">The replacement.</param> private static void AddSingularRule(string rule, string replacement) { _singulars.Add(new InflectorRule(rule, replacement)); } /// <summary> /// Makes the plural. /// </summary> /// <param name="word">The word.</param> /// <returns></returns> public static string MakePlural(string word) { return ApplyRules(_plurals, word); } /// <summary> /// Makes the singular. /// </summary> /// <param name="word">The word.</param> /// <returns></returns> public static string MakeSingular(string word) { return ApplyRules(_singulars, word); } /// <summary> /// Applies the rules. /// </summary> /// <param name="rules">The rules.</param> /// <param name="word">The word.</param> /// <returns></returns> private static string ApplyRules(IList<InflectorRule> rules, string word) { string result = word; if (!_uncountables.Contains(word.ToLower())) { for (int i = rules.Count - 1; i >= 0; i--) { string currentPass = rules[i].Apply(word); if (currentPass != null) { result = currentPass; break; } } } return result; } /// <summary> /// Converts the string to title case. /// </summary> /// <param name="word">The word.</param> /// <returns></returns> public static string ToTitleCase(string word) { return Regex.Replace(ToHumanCase(AddUnderscores(word)), @"([a-z])", delegate(Match match) { return match.Captures[0].Value.ToUpper(); }); } /// <summary> /// Converts the string to human case. /// </summary> /// <param name="lowercaseAndUnderscoredWord">The lowercase and underscored word.</param> /// <returns></returns> public static string ToHumanCase(string lowercaseAndUnderscoredWord) { return MakeInitialCaps(Regex.Replace(lowercaseAndUnderscoredWord, @"_", " ")); } /// <summary> /// Adds the underscores. /// </summary> /// <param name="pascalCasedWord">The pascal cased word.</param> /// <returns></returns> public static string AddUnderscores(string pascalCasedWord) { return Regex.Replace(Regex.Replace(Regex.Replace(pascalCasedWord, @"([A-Z]+)([A-Z][a-z])", "$1_$2"), @"([a-zd])([A-Z])", "$1_$2"), @"[-s]", "_").ToLower(); } /// <summary> /// Makes the initial caps. /// </summary> /// <param name="word">The word.</param> /// <returns></returns> public static string MakeInitialCaps(string word) { return String.Concat(word.Substring(0, 1).ToUpper(), word.Substring(1).ToLower()); } /// <summary> /// Makes the initial lower case. /// </summary> /// <param name="word">The word.</param> /// <returns></returns> public static string MakeInitialLowerCase(string word) { return String.Concat(word.Substring(0, 1).ToLower(), word.Substring(1)); } /// <summary> /// Determine whether the passed string is numeric, by attempting to parse it to a double /// </summary> /// <param name="str">The string to evaluated for numeric conversion</param> /// <returns> /// <c>true</c> if the string can be converted to a number; otherwise, <c>false</c>. /// </returns> public static bool IsStringNumeric(string str) { double result; return (double.TryParse(str, NumberStyles.Float, NumberFormatInfo.CurrentInfo, out result)); } /// <summary> /// Adds the ordinal suffix. /// </summary> /// <param name="number">The number.</param> /// <returns></returns> public static string AddOrdinalSuffix(string number) { if (IsStringNumeric(number)) { int n = int.Parse(number); int nMod100 = n % 100; if (nMod100 >= 11 && nMod100 <= 13) return String.Concat(number, "th"); switch (n % 10) { case 1: return String.Concat(number, "st"); case 2: return String.Concat(number, "nd"); case 3: return String.Concat(number, "rd"); default: return String.Concat(number, "th"); } } return number; } /// <summary> /// Converts the underscores to dashes. /// </summary> /// <param name="underscoredWord">The underscored word.</param> /// <returns></returns> public static string ConvertUnderscoresToDashes(string underscoredWord) { return underscoredWord.Replace('_', '-'); } #region Nested type: InflectorRule /// <summary> /// Summary for the InflectorRule class /// </summary> private class InflectorRule { /// <summary> /// /// </summary> public readonly Regex regex; /// <summary> /// /// </summary> public readonly string replacement; /// <summary> /// Initializes a new instance of the <see cref="InflectorRule"/> class. /// </summary> /// <param name="regexPattern">The regex pattern.</param> /// <param name="replacementText">The replacement text.</param> public InflectorRule(string regexPattern, string replacementText) { regex = new Regex(regexPattern, RegexOptions.IgnoreCase); replacement = replacementText; } /// <summary> /// Applies the specified word. /// </summary> /// <param name="word">The word.</param> /// <returns></returns> public string Apply(string word) { if (!regex.IsMatch(word)) return null; string replace = regex.Replace(word, replacement); if (word == word.ToUpper()) replace = replace.ToUpper(); return replace; } } #endregion } #>
第二步:
把GetSummery.ttinclude文件放到Model1.edmx同一级目录,当然也可以放到其他路径,为了方便放到同一级目录。
第三步:打开GetSummery.ttinclude文件,修改GetSummery.ttinclude连接数据库的字符串改为"ConnStr",prioviderName属性必须要有哦。
第四步:打开EF项目文件所在的tt文件
第五步:在tt头部添加 <#@ include file="GetSummery.ttinclude" #>
第六步:加载自定义TT文件用来获取数据库表备注和字段备注说明
在TT文件里搜索:<#=codeStringGenerator.UsingDirectives(inHeader: false)#>
把代码添加<#=codeStringGenerator.UsingDirectives(inHeader: false)#>到下面
/// <summary>
/// <#= getTableSummery(code.Escape(entity)) #>
/// </summary>
如图所示:
第七步:第二次搜索:<#=codeStringGenerator.Property(edmProperty)#>, 在它上方插入代码:
/// <summary>
/// <#= getColumnSummery(code.Escape(entity),code.Escape(edmProperty)) #>
/// </summary>
如图所示:
第八步:保存TT文件,它会自动更新数据库里的各个实体
效果如图:
操作完毕!