项目中使用了EF框架,使用的是Database-First方式,因为数据库已经存在,所以采用Database-First方式,ef生成实体类的时候,发现微软没有自动生成表字段和表说明,在网上找了些资料,由于都不太全,倒腾了近2个小时,所以根据网上的帖子为基础,写得更详细一点,让初学者更容易明白和少走弯路。网上也有一些自动生成的软件,可以自动生成,但是更新数据库需要重新生成,有点麻烦,所有根据T4模板解决
第一步:
我们需要有GetSummery.ttinclude文件,
1 <#@ template language="C#v3.5" hostspecific="True" #> 2 <#@ assembly name="EnvDTE" #> 3 <#@ assembly name="System.Data" #> 4 <#@ assembly name="System.Xml" #> 5 <#@ assembly name="System.Configuration" #> 6 <#@ assembly name="System.Windows.Forms" #> 7 <#@ import namespace="System.Collections.Generic" #> 8 <#@ import namespace="System.Data" #> 9 <#@ import namespace="System.Data.SqlClient" #> 10 <#@ import namespace="System.Data.Common" #> 11 <#@ import namespace="System.Diagnostics" #> 12 <#@ import namespace="System.Globalization" #> 13 <#@ import namespace="System.IO" #> 14 <#@ import namespace="System.Linq" #> 15 <#@ import namespace="System.Text" #> 16 <#@ import namespace="System.Text.RegularExpressions" #> 17 <#@ import namespace="System.Configuration" #> 18 <#@ import namespace="System.Windows.Forms" #> 19 <#+ 20 21 string ConnectionStringName = "ConnStr"; 22 string Namespace = ""; 23 string RepoName = ""; 24 string ClassPrefix = ""; 25 string ClassSuffix = ""; 26 string SchemaName = null; 27 bool IncludeViews = false; 28 bool GenerateOperations = false; 29 bool GenerateCommon = true; 30 bool GeneratePocos = true; 31 bool TrackModifiedColumns = false; 32 33 34 static Regex rxCleanUp = new Regex(@"[^wd_]", RegexOptions.Compiled); 35 36 static Func<string, string> CleanUp = (str) => 37 { 38 str = rxCleanUp.Replace(str, "_"); 39 if (char.IsDigit(str[0])) str = "_" + str; 40 41 return str; 42 }; 43 44 45 46 string GetConnectionString(ref string connectionStringName, out string providerName) 47 { 48 var _CurrentProject = GetCurrentProject(); 49 50 providerName=null; 51 52 string result=""; 53 ExeConfigurationFileMap configFile = new ExeConfigurationFileMap(); 54 configFile.ExeConfigFilename = GetConfigPath(); 55 56 if (string.IsNullOrEmpty(configFile.ExeConfigFilename)) 57 throw new ArgumentNullException("The project does not contain App.config or Web.config file."); 58 59 60 var config = System.Configuration.ConfigurationManager.OpenMappedExeConfiguration(configFile, ConfigurationUserLevel.None); 61 var connSection=config.ConnectionStrings; 62 63 //if the connectionString is empty - which is the defauls 64 //look for count-1 - this is the last connection string 65 //and takes into account AppServices and LocalSqlServer 66 if(string.IsNullOrEmpty(connectionStringName)) 67 { 68 if(connSection.ConnectionStrings.Count>1) 69 { 70 connectionStringName = connSection.ConnectionStrings[connSection.ConnectionStrings.Count-1].Name; 71 result=connSection.ConnectionStrings[connSection.ConnectionStrings.Count-1].ConnectionString; 72 providerName=connSection.ConnectionStrings[connSection.ConnectionStrings.Count-1].ProviderName; 73 } 74 } 75 else 76 { 77 try 78 { 79 result=connSection.ConnectionStrings[connectionStringName].ConnectionString; 80 providerName=connSection.ConnectionStrings[connectionStringName].ProviderName; 81 } 82 catch 83 { 84 result="There is no connection string name called '"+connectionStringName+"'"; 85 } 86 } 87 88 // if (String.IsNullOrEmpty(providerName)) 89 // providerName="System.Data.SqlClient"; 90 91 return result; 92 } 93 94 string _connectionString=""; 95 string _providerName=""; 96 97 void InitConnectionString() 98 { 99 if(String.IsNullOrEmpty(_connectionString)) 100 { 101 _connectionString=GetConnectionString(ref ConnectionStringName, out _providerName); 102 103 if(_connectionString.Contains("|DataDirectory|")) 104 { 105 //have to replace it 106 string dataFilePath=GetDataDirectory(); 107 _connectionString=_connectionString.Replace("|DataDirectory|",dataFilePath); 108 } 109 } 110 } 111 112 public string ConnectionString 113 { 114 get 115 { 116 InitConnectionString(); 117 return _connectionString; 118 } 119 } 120 121 public string ProviderName 122 { 123 get 124 { 125 InitConnectionString(); 126 return _providerName; 127 } 128 } 129 130 public EnvDTE.Project GetCurrentProject() { 131 132 IServiceProvider _ServiceProvider = (IServiceProvider)Host; 133 if (_ServiceProvider == null) 134 throw new Exception("Host property returned unexpected value (null)"); 135 136 EnvDTE.DTE dte = (EnvDTE.DTE)_ServiceProvider.GetService(typeof(EnvDTE.DTE)); 137 if (dte == null) 138 throw new Exception("Unable to retrieve EnvDTE.DTE"); 139 140 Array activeSolutionProjects = (Array)dte.ActiveSolutionProjects; 141 if (activeSolutionProjects == null) 142 throw new Exception("DTE.ActiveSolutionProjects returned null"); 143 144 EnvDTE.Project dteProject = (EnvDTE.Project)activeSolutionProjects.GetValue(0); 145 if (dteProject == null) 146 throw new Exception("DTE.ActiveSolutionProjects[0] returned null"); 147 148 return dteProject; 149 150 } 151 152 private string GetProjectPath() 153 { 154 EnvDTE.Project project = GetCurrentProject(); 155 System.IO.FileInfo info = new System.IO.FileInfo(project.FullName); 156 return info.Directory.FullName; 157 } 158 159 private string GetConfigPath() 160 { 161 EnvDTE.Project project = GetCurrentProject(); 162 foreach (EnvDTE.ProjectItem item in project.ProjectItems) 163 { 164 // if it is the app.config file, then open it up 165 if (item.Name.Equals("App.config",StringComparison.InvariantCultureIgnoreCase) || item.Name.Equals("Web.config",StringComparison.InvariantCultureIgnoreCase)) 166 return GetProjectPath() + "\" + item.Name; 167 } 168 return String.Empty; 169 } 170 171 public string GetDataDirectory() 172 { 173 EnvDTE.Project project=GetCurrentProject(); 174 return System.IO.Path.GetDirectoryName(project.FileName)+"\App_Data\"; 175 } 176 177 static string zap_password(string connectionString) 178 { 179 var rx = new Regex("password=.*;", RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.IgnoreCase); 180 return rx.Replace(connectionString, "password=**zapped**;"); 181 } 182 183 //得到列说明 184 string getColumnSummery(string tablename,string columnname) 185 { 186 InitConnectionString(); 187 DbProviderFactory _factory; 188 189 _factory = DbProviderFactories.GetFactory(ProviderName); 190 191 using(var conn=_factory.CreateConnection()) 192 { 193 conn.ConnectionString=ConnectionString; 194 conn.Open(); 195 196 197 //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 "; 198 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 "; 199 200 using (var cmd=_factory.CreateCommand()) 201 { 202 cmd.Connection=conn; 203 cmd.CommandText=sql; 204 205 var p = cmd.CreateParameter(); 206 p.ParameterName = "@tablename"; 207 p.Value=tablename; 208 cmd.Parameters.Add(p); 209 210 p = cmd.CreateParameter(); 211 p.ParameterName = "@columname"; 212 p.Value=columnname; 213 cmd.Parameters.Add(p); 214 215 var result=cmd.ExecuteScalar(); 216 217 conn.Close(); 218 219 if(result!=null) 220 return result.ToString(); 221 else 222 return ProviderName; 223 } 224 225 return ""; 226 227 } 228 229 } 230 231 232 233 234 //得到表说明备注 235 string getTableSummery(string tablename) 236 { 237 InitConnectionString(); 238 DbProviderFactory _factory; 239 240 _factory = DbProviderFactories.GetFactory(ProviderName); 241 242 using(var conn=_factory.CreateConnection()) 243 { 244 conn.ConnectionString=ConnectionString; 245 conn.Open(); 246 247 248 //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 "; 249 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 "; 250 251 using (var cmd=_factory.CreateCommand()) 252 { 253 cmd.Connection=conn; 254 cmd.CommandText=sql; 255 256 var p = cmd.CreateParameter(); 257 p.ParameterName = "@tablename"; 258 p.Value=tablename; 259 cmd.Parameters.Add(p); 260 261 var result=cmd.ExecuteScalar(); 262 263 conn.Close(); 264 265 if(result!=null) 266 return result.ToString(); 267 else 268 return ProviderName; 269 } 270 271 return ""; 272 273 } 274 275 } 276 277 278 279 280 281 /// <summary> 282 /// Summary for the Inflector class 283 /// </summary> 284 public static class Inflector { 285 private static readonly List<InflectorRule> _plurals = new List<InflectorRule>(); 286 private static readonly List<InflectorRule> _singulars = new List<InflectorRule>(); 287 private static readonly List<string> _uncountables = new List<string>(); 288 289 /// <summary> 290 /// Initializes the <see cref="Inflector"/> class. 291 /// </summary> 292 static Inflector() { 293 AddPluralRule("$", "s"); 294 AddPluralRule("s$", "s"); 295 AddPluralRule("(ax|test)is$", "$1es"); 296 AddPluralRule("(octop|vir)us$", "$1i"); 297 AddPluralRule("(alias|status)$", "$1es"); 298 AddPluralRule("(bu)s$", "$1ses"); 299 AddPluralRule("(buffal|tomat)o$", "$1oes"); 300 AddPluralRule("([ti])um$", "$1a"); 301 AddPluralRule("sis$", "ses"); 302 AddPluralRule("(?:([^f])fe|([lr])f)$", "$1$2ves"); 303 AddPluralRule("(hive)$", "$1s"); 304 AddPluralRule("([^aeiouy]|qu)y$", "$1ies"); 305 AddPluralRule("(x|ch|ss|sh)$", "$1es"); 306 AddPluralRule("(matr|vert|ind)ix|ex$", "$1ices"); 307 AddPluralRule("([m|l])ouse$", "$1ice"); 308 AddPluralRule("^(ox)$", "$1en"); 309 AddPluralRule("(quiz)$", "$1zes"); 310 311 AddSingularRule("s$", String.Empty); 312 AddSingularRule("ss$", "ss"); 313 AddSingularRule("(n)ews$", "$1ews"); 314 AddSingularRule("([ti])a$", "$1um"); 315 AddSingularRule("((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$", "$1$2sis"); 316 AddSingularRule("(^analy)ses$", "$1sis"); 317 AddSingularRule("([^f])ves$", "$1fe"); 318 AddSingularRule("(hive)s$", "$1"); 319 AddSingularRule("(tive)s$", "$1"); 320 AddSingularRule("([lr])ves$", "$1f"); 321 AddSingularRule("([^aeiouy]|qu)ies$", "$1y"); 322 AddSingularRule("(s)eries$", "$1eries"); 323 AddSingularRule("(m)ovies$", "$1ovie"); 324 AddSingularRule("(x|ch|ss|sh)es$", "$1"); 325 AddSingularRule("([m|l])ice$", "$1ouse"); 326 AddSingularRule("(bus)es$", "$1"); 327 AddSingularRule("(o)es$", "$1"); 328 AddSingularRule("(shoe)s$", "$1"); 329 AddSingularRule("(cris|ax|test)es$", "$1is"); 330 AddSingularRule("(octop|vir)i$", "$1us"); 331 AddSingularRule("(alias|status)$", "$1"); 332 AddSingularRule("(alias|status)es$", "$1"); 333 AddSingularRule("^(ox)en", "$1"); 334 AddSingularRule("(vert|ind)ices$", "$1ex"); 335 AddSingularRule("(matr)ices$", "$1ix"); 336 AddSingularRule("(quiz)zes$", "$1"); 337 338 AddIrregularRule("person", "people"); 339 AddIrregularRule("man", "men"); 340 AddIrregularRule("child", "children"); 341 AddIrregularRule("sex", "sexes"); 342 AddIrregularRule("tax", "taxes"); 343 AddIrregularRule("move", "moves"); 344 345 AddUnknownCountRule("equipment"); 346 AddUnknownCountRule("information"); 347 AddUnknownCountRule("rice"); 348 AddUnknownCountRule("money"); 349 AddUnknownCountRule("species"); 350 AddUnknownCountRule("series"); 351 AddUnknownCountRule("fish"); 352 AddUnknownCountRule("sheep"); 353 } 354 355 /// <summary> 356 /// Adds the irregular rule. 357 /// </summary> 358 /// <param name="singular">The singular.</param> 359 /// <param name="plural">The plural.</param> 360 private static void AddIrregularRule(string singular, string plural) { 361 AddPluralRule(String.Concat("(", singular[0], ")", singular.Substring(1), "$"), String.Concat("$1", plural.Substring(1))); 362 AddSingularRule(String.Concat("(", plural[0], ")", plural.Substring(1), "$"), String.Concat("$1", singular.Substring(1))); 363 } 364 365 /// <summary> 366 /// Adds the unknown count rule. 367 /// </summary> 368 /// <param name="word">The word.</param> 369 private static void AddUnknownCountRule(string word) { 370 _uncountables.Add(word.ToLower()); 371 } 372 373 /// <summary> 374 /// Adds the plural rule. 375 /// </summary> 376 /// <param name="rule">The rule.</param> 377 /// <param name="replacement">The replacement.</param> 378 private static void AddPluralRule(string rule, string replacement) { 379 _plurals.Add(new InflectorRule(rule, replacement)); 380 } 381 382 /// <summary> 383 /// Adds the singular rule. 384 /// </summary> 385 /// <param name="rule">The rule.</param> 386 /// <param name="replacement">The replacement.</param> 387 private static void AddSingularRule(string rule, string replacement) { 388 _singulars.Add(new InflectorRule(rule, replacement)); 389 } 390 391 /// <summary> 392 /// Makes the plural. 393 /// </summary> 394 /// <param name="word">The word.</param> 395 /// <returns></returns> 396 public static string MakePlural(string word) { 397 return ApplyRules(_plurals, word); 398 } 399 400 /// <summary> 401 /// Makes the singular. 402 /// </summary> 403 /// <param name="word">The word.</param> 404 /// <returns></returns> 405 public static string MakeSingular(string word) { 406 return ApplyRules(_singulars, word); 407 } 408 409 /// <summary> 410 /// Applies the rules. 411 /// </summary> 412 /// <param name="rules">The rules.</param> 413 /// <param name="word">The word.</param> 414 /// <returns></returns> 415 private static string ApplyRules(IList<InflectorRule> rules, string word) { 416 string result = word; 417 if (!_uncountables.Contains(word.ToLower())) { 418 for (int i = rules.Count - 1; i >= 0; i--) { 419 string currentPass = rules[i].Apply(word); 420 if (currentPass != null) { 421 result = currentPass; 422 break; 423 } 424 } 425 } 426 return result; 427 } 428 429 /// <summary> 430 /// Converts the string to title case. 431 /// </summary> 432 /// <param name="word">The word.</param> 433 /// <returns></returns> 434 public static string ToTitleCase(string word) { 435 return Regex.Replace(ToHumanCase(AddUnderscores(word)), @"([a-z])", 436 delegate(Match match) { return match.Captures[0].Value.ToUpper(); }); 437 } 438 439 /// <summary> 440 /// Converts the string to human case. 441 /// </summary> 442 /// <param name="lowercaseAndUnderscoredWord">The lowercase and underscored word.</param> 443 /// <returns></returns> 444 public static string ToHumanCase(string lowercaseAndUnderscoredWord) { 445 return MakeInitialCaps(Regex.Replace(lowercaseAndUnderscoredWord, @"_", " ")); 446 } 447 448 449 /// <summary> 450 /// Adds the underscores. 451 /// </summary> 452 /// <param name="pascalCasedWord">The pascal cased word.</param> 453 /// <returns></returns> 454 public static string AddUnderscores(string pascalCasedWord) { 455 return Regex.Replace(Regex.Replace(Regex.Replace(pascalCasedWord, @"([A-Z]+)([A-Z][a-z])", "$1_$2"), @"([a-zd])([A-Z])", "$1_$2"), @"[-s]", "_").ToLower(); 456 } 457 458 /// <summary> 459 /// Makes the initial caps. 460 /// </summary> 461 /// <param name="word">The word.</param> 462 /// <returns></returns> 463 public static string MakeInitialCaps(string word) { 464 return String.Concat(word.Substring(0, 1).ToUpper(), word.Substring(1).ToLower()); 465 } 466 467 /// <summary> 468 /// Makes the initial lower case. 469 /// </summary> 470 /// <param name="word">The word.</param> 471 /// <returns></returns> 472 public static string MakeInitialLowerCase(string word) { 473 return String.Concat(word.Substring(0, 1).ToLower(), word.Substring(1)); 474 } 475 476 477 /// <summary> 478 /// Determine whether the passed string is numeric, by attempting to parse it to a double 479 /// </summary> 480 /// <param name="str">The string to evaluated for numeric conversion</param> 481 /// <returns> 482 /// <c>true</c> if the string can be converted to a number; otherwise, <c>false</c>. 483 /// </returns> 484 public static bool IsStringNumeric(string str) { 485 double result; 486 return (double.TryParse(str, NumberStyles.Float, NumberFormatInfo.CurrentInfo, out result)); 487 } 488 489 /// <summary> 490 /// Adds the ordinal suffix. 491 /// </summary> 492 /// <param name="number">The number.</param> 493 /// <returns></returns> 494 public static string AddOrdinalSuffix(string number) { 495 if (IsStringNumeric(number)) { 496 int n = int.Parse(number); 497 int nMod100 = n % 100; 498 499 if (nMod100 >= 11 && nMod100 <= 13) 500 return String.Concat(number, "th"); 501 502 switch (n % 10) { 503 case 1: 504 return String.Concat(number, "st"); 505 case 2: 506 return String.Concat(number, "nd"); 507 case 3: 508 return String.Concat(number, "rd"); 509 default: 510 return String.Concat(number, "th"); 511 } 512 } 513 return number; 514 } 515 516 /// <summary> 517 /// Converts the underscores to dashes. 518 /// </summary> 519 /// <param name="underscoredWord">The underscored word.</param> 520 /// <returns></returns> 521 public static string ConvertUnderscoresToDashes(string underscoredWord) { 522 return underscoredWord.Replace('_', '-'); 523 } 524 525 526 #region Nested type: InflectorRule 527 528 /// <summary> 529 /// Summary for the InflectorRule class 530 /// </summary> 531 private class InflectorRule { 532 /// <summary> 533 /// 534 /// </summary> 535 public readonly Regex regex; 536 537 /// <summary> 538 /// 539 /// </summary> 540 public readonly string replacement; 541 542 /// <summary> 543 /// Initializes a new instance of the <see cref="InflectorRule"/> class. 544 /// </summary> 545 /// <param name="regexPattern">The regex pattern.</param> 546 /// <param name="replacementText">The replacement text.</param> 547 public InflectorRule(string regexPattern, string replacementText) { 548 regex = new Regex(regexPattern, RegexOptions.IgnoreCase); 549 replacement = replacementText; 550 } 551 552 /// <summary> 553 /// Applies the specified word. 554 /// </summary> 555 /// <param name="word">The word.</param> 556 /// <returns></returns> 557 public string Apply(string word) { 558 if (!regex.IsMatch(word)) 559 return null; 560 561 string replace = regex.Replace(word, replacement); 562 if (word == word.ToUpper()) 563 replace = replace.ToUpper(); 564 565 return replace; 566 } 567 } 568 569 #endregion 570 } 571 572 #>
,GetSummery.ttinclude是模板文件,为了实现EF框架添加数据库自动生成实体注释。
第二步:
把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文件,它会自动更新数据库里的各个实体
效果图如下 :
最后附上完整的代码:
<#@ template language="C#" debug="false" hostspecific="true"#> <#@ include file="GetSummery.ttinclude" #> <#@ include file="EF.Utility.CS.ttinclude"#> <#@ output extension=".cs"#> <# const string inputFile = @"OADBModel.edmx"; var textTransform = DynamicTextTransformation.Create(this); var code = new CodeGenerationTools(this); var ef = new MetadataTools(this); var typeMapper = new TypeMapper(code, ef, textTransform.Errors); var fileManager = EntityFrameworkTemplateFileManager.Create(this); var itemCollection = new EdmMetadataLoader(textTransform.Host, textTransform.Errors).CreateEdmItemCollection(inputFile); var codeStringGenerator = new CodeStringGenerator(code, typeMapper, ef); if (!typeMapper.VerifyCaseInsensitiveTypeUniqueness(typeMapper.GetAllGlobalItems(itemCollection), inputFile)) { return string.Empty; } WriteHeader(codeStringGenerator, fileManager); string summary=string.Empty; foreach (var entity in typeMapper.GetItemsToGenerate<EntityType>(itemCollection)) { fileManager.StartNewFile(entity.Name + ".cs"); BeginNamespace(code); if(entity.Documentation !=null && entity.Documentation.Summary!=null) summary=entity.Documentation.Summary; else summary=entity.Name; #> <#=codeStringGenerator.UsingDirectives(inHeader: false)#> /// <summary> /// <#= getTableSummery(code.Escape(entity)) #> /// </summary> <#=codeStringGenerator.EntityClassOpening(entity)#> { <# var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(entity); var collectionNavigationProperties = typeMapper.GetCollectionNavigationProperties(entity); var complexProperties = typeMapper.GetComplexProperties(entity); if (propertiesWithDefaultValues.Any() || collectionNavigationProperties.Any() || complexProperties.Any()) { #> public <#=code.Escape(entity)#>() { <# foreach (var edmProperty in propertiesWithDefaultValues) { #> this.<#=code.Escape(edmProperty)#> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>; <# } foreach (var navigationProperty in collectionNavigationProperties) { #> this.<#=code.Escape(navigationProperty)#> = new HashSet<<#=typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType())#>>(); <# } foreach (var complexProperty in complexProperties) { #> this.<#=code.Escape(complexProperty)#> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>(); <# } #> } <# } var simpleProperties = typeMapper.GetSimpleProperties(entity); if (simpleProperties.Any()) { foreach (var edmProperty in simpleProperties) { if (edmProperty.Documentation != null && edmProperty.Documentation.Summary != null) { summary=edmProperty.Documentation.Summary; } else { summary=""; } #> /// <summary> /// <#= getColumnSummery(code.Escape(entity),code.Escape(edmProperty)) #> /// </summary> <#=codeStringGenerator.Property(edmProperty)#> <# } } if (complexProperties.Any()) { #> <# foreach(var complexProperty in complexProperties) { #> <#=codeStringGenerator.Property(complexProperty)#> <# } } var navigationProperties = typeMapper.GetNavigationProperties(entity); if (navigationProperties.Any()) { #> <# foreach (var navigationProperty in navigationProperties) { if(navigationProperty.Documentation != null &&navigationProperty.Documentation.Summary != null) { summary=navigationProperty.Documentation.Summary; } else { summary=""; } #> /// <summary> /// <#=summary#> /// </summary> <#=codeStringGenerator.NavigationProperty(navigationProperty)#> <# } } #> } <# EndNamespace(code); } foreach (var complex in typeMapper.GetItemsToGenerate<ComplexType>(itemCollection)) { fileManager.StartNewFile(complex.Name + ".cs"); BeginNamespace(code); #> <#=codeStringGenerator.UsingDirectives(inHeader: false, includeCollections: false)#> <#=Accessibility.ForType(complex)#> partial class <#=code.Escape(complex)#> { <# var complexProperties = typeMapper.GetComplexProperties(complex); var propertiesWithDefaultValues = typeMapper.GetPropertiesWithDefaultValues(complex); if (propertiesWithDefaultValues.Any() || complexProperties.Any()) { #> public <#=code.Escape(complex)#>() { <# foreach (var edmProperty in propertiesWithDefaultValues) { #> this.<#=code.Escape(edmProperty)#> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>; <# } foreach (var complexProperty in complexProperties) { #> this.<#=code.Escape(complexProperty)#> = new <#=typeMapper.GetTypeName(complexProperty.TypeUsage)#>(); <# } #> } <# } var simpleProperties = typeMapper.GetSimpleProperties(complex); if (simpleProperties.Any()) { foreach(var edmProperty in simpleProperties) { #> <#=codeStringGenerator.Property(edmProperty)#> <# } } if (complexProperties.Any()) { #> <# foreach(var edmProperty in complexProperties) { #> <#=codeStringGenerator.Property(edmProperty)#> <# } } #> } <# EndNamespace(code); } foreach (var enumType in typeMapper.GetEnumItemsToGenerate(itemCollection)) { fileManager.StartNewFile(enumType.Name + ".cs"); BeginNamespace(code); #> <#=codeStringGenerator.UsingDirectives(inHeader: false, includeCollections: false)#> <# if (typeMapper.EnumIsFlags(enumType)) { #> [Flags] <# } #> <#=codeStringGenerator.EnumOpening(enumType)#> { <# var foundOne = false; foreach (MetadataItem member in typeMapper.GetEnumMembers(enumType)) { foundOne = true; #> <#=code.Escape(typeMapper.GetEnumMemberName(member))#> = <#=typeMapper.GetEnumMemberValue(member)#>, <# } if (foundOne) { this.GenerationEnvironment.Remove(this.GenerationEnvironment.Length - 3, 1); } #> } <# EndNamespace(code); } fileManager.Process(); #> <#+ public void WriteHeader(CodeStringGenerator codeStringGenerator, EntityFrameworkTemplateFileManager fileManager) { fileManager.StartHeader(); #> //------------------------------------------------------------------------------ // <auto-generated> // <#=GetResourceString("Template_GeneratedCodeCommentLine1")#> // // <#=GetResourceString("Template_GeneratedCodeCommentLine2")#> // <#=GetResourceString("Template_GeneratedCodeCommentLine3")#> // </auto-generated> //------------------------------------------------------------------------------ <#=codeStringGenerator.UsingDirectives(inHeader: true)#> <#+ fileManager.EndBlock(); } public void BeginNamespace(CodeGenerationTools code) { var codeNamespace = code.VsNamespaceSuggestion(); if (!String.IsNullOrEmpty(codeNamespace)) { #> namespace <#=code.EscapeNamespace(codeNamespace)#> { <#+ PushIndent(" "); } } public void EndNamespace(CodeGenerationTools code) { if (!String.IsNullOrEmpty(code.VsNamespaceSuggestion())) { PopIndent(); #> } <#+ } } public const string TemplateId = "CSharp_DbContext_Types_EF5"; public class CodeStringGenerator { private readonly CodeGenerationTools _code; private readonly TypeMapper _typeMapper; private readonly MetadataTools _ef; public CodeStringGenerator(CodeGenerationTools code, TypeMapper typeMapper, MetadataTools ef) { ArgumentNotNull(code, "code"); ArgumentNotNull(typeMapper, "typeMapper"); ArgumentNotNull(ef, "ef"); _code = code; _typeMapper = typeMapper; _ef = ef; } public string Property(EdmProperty edmProperty) { return string.Format( CultureInfo.InvariantCulture, "{0} {1} {2} {{ {3}get; {4}set; }}", Accessibility.ForProperty(edmProperty), _typeMapper.GetTypeName(edmProperty.TypeUsage), _code.Escape(edmProperty), _code.SpaceAfter(Accessibility.ForGetter(edmProperty)), _code.SpaceAfter(Accessibility.ForSetter(edmProperty))); } public string NavigationProperty(NavigationProperty navigationProperty) { var endType = _typeMapper.GetTypeName(navigationProperty.ToEndMember.GetEntityType()); return string.Format( CultureInfo.InvariantCulture, "{0} {1} {2} {{ {3}get; {4}set; }}", AccessibilityAndVirtual(Accessibility.ForProperty(navigationProperty)), navigationProperty.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many ? ("ICollection<" + endType + ">") : endType, _code.Escape(navigationProperty), _code.SpaceAfter(Accessibility.ForGetter(navigationProperty)), _code.SpaceAfter(Accessibility.ForSetter(navigationProperty))); } public string AccessibilityAndVirtual(string accessibility) { return accessibility + (accessibility != "private" ? " virtual" : ""); } public string EntityClassOpening(EntityType entity) { return string.Format( CultureInfo.InvariantCulture, "{0} {1}partial class {2}{3}", Accessibility.ForType(entity), _code.SpaceAfter(_code.AbstractOption(entity)), _code.Escape(entity), _code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType))); } public string EnumOpening(SimpleType enumType) { return string.Format( CultureInfo.InvariantCulture, "{0} enum {1} : {2}", Accessibility.ForType(enumType), _code.Escape(enumType), _code.Escape(_typeMapper.UnderlyingClrType(enumType))); } public void WriteFunctionParameters(EdmFunction edmFunction, Action<string, string, string, string> writeParameter) { var parameters = FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef); foreach (var parameter in parameters.Where(p => p.NeedsLocalVariable)) { var isNotNull = parameter.IsNullableOfT ? parameter.FunctionParameterName + ".HasValue" : parameter.FunctionParameterName + " != null"; var notNullInit = "new ObjectParameter("" + parameter.EsqlParameterName + "", " + parameter.FunctionParameterName + ")"; var nullInit = "new ObjectParameter("" + parameter.EsqlParameterName + "", typeof(" + parameter.RawClrTypeName + "))"; writeParameter(parameter.LocalVariableName, isNotNull, notNullInit, nullInit); } } public string ComposableFunctionMethod(EdmFunction edmFunction, string modelNamespace) { var parameters = _typeMapper.GetParameters(edmFunction); return string.Format( CultureInfo.InvariantCulture, "{0} IQueryable<{1}> {2}({3})", AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)), _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace), _code.Escape(edmFunction), string.Join(", ", parameters.Select(p => p.FunctionParameterType + " " + p.FunctionParameterName).ToArray())); } public string ComposableCreateQuery(EdmFunction edmFunction, string modelNamespace) { var parameters = _typeMapper.GetParameters(edmFunction); return string.Format( CultureInfo.InvariantCulture, "return ((IObjectContextAdapter)this).ObjectContext.CreateQuery<{0}>("[{1}].[{2}]({3})"{4});", _typeMapper.GetTypeName(_typeMapper.GetReturnType(edmFunction), modelNamespace), edmFunction.NamespaceName, edmFunction.Name, string.Join(", ", parameters.Select(p => "@" + p.EsqlParameterName).ToArray()), _code.StringBefore(", ", string.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray()))); } public string FunctionMethod(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption) { var parameters = _typeMapper.GetParameters(edmFunction); var returnType = _typeMapper.GetReturnType(edmFunction); var paramList = String.Join(", ", parameters.Select(p => p.FunctionParameterType + " " + p.FunctionParameterName).ToArray()); if (includeMergeOption) { paramList = _code.StringAfter(paramList, ", ") + "MergeOption mergeOption"; } return string.Format( CultureInfo.InvariantCulture, "{0} {1} {2}({3})", AccessibilityAndVirtual(Accessibility.ForMethod(edmFunction)), returnType == null ? "int" : "ObjectResult<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">", _code.Escape(edmFunction), paramList); } public string ExecuteFunction(EdmFunction edmFunction, string modelNamespace, bool includeMergeOption) { var parameters = _typeMapper.GetParameters(edmFunction); var returnType = _typeMapper.GetReturnType(edmFunction); var callParams = _code.StringBefore(", ", String.Join(", ", parameters.Select(p => p.ExecuteParameterName).ToArray())); if (includeMergeOption) { callParams = ", mergeOption" + callParams; } return string.Format( CultureInfo.InvariantCulture, "return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction{0}("{1}"{2});", returnType == null ? "" : "<" + _typeMapper.GetTypeName(returnType, modelNamespace) + ">", edmFunction.Name, callParams); } public string DbSet(EntitySet entitySet) { return string.Format( CultureInfo.InvariantCulture, "{0} DbSet<{1}> {2} {{ get; set; }}", Accessibility.ForReadOnlyProperty(entitySet), _typeMapper.GetTypeName(entitySet.ElementType), _code.Escape(entitySet)); } public string UsingDirectives(bool inHeader, bool includeCollections = true) { return inHeader == string.IsNullOrEmpty(_code.VsNamespaceSuggestion()) ? string.Format( CultureInfo.InvariantCulture, "{0}using System;{1}" + "{2}", inHeader ? Environment.NewLine : "", includeCollections ? (Environment.NewLine + "using System.Collections.Generic;") : "", inHeader ? "" : Environment.NewLine) : ""; } } public class TypeMapper { private const string ExternalTypeNameAttributeName = @"http://schemas.microsoft.com/ado/2006/04/codegeneration:ExternalTypeName"; private readonly System.Collections.IList _errors; private readonly CodeGenerationTools _code; private readonly MetadataTools _ef; public TypeMapper(CodeGenerationTools code, MetadataTools ef, System.Collections.IList errors) { ArgumentNotNull(code, "code"); ArgumentNotNull(ef, "ef"); ArgumentNotNull(errors, "errors"); _code = code; _ef = ef; _errors = errors; } public string GetTypeName(TypeUsage typeUsage) { return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace: null); } public string GetTypeName(EdmType edmType) { return GetTypeName(edmType, isNullable: null, modelNamespace: null); } public string GetTypeName(TypeUsage typeUsage, string modelNamespace) { return typeUsage == null ? null : GetTypeName(typeUsage.EdmType, _ef.IsNullable(typeUsage), modelNamespace); } public string GetTypeName(EdmType edmType, string modelNamespace) { return GetTypeName(edmType, isNullable: null, modelNamespace: modelNamespace); } public string GetTypeName(EdmType edmType, bool? isNullable, string modelNamespace) { if (edmType == null) { return null; } var collectionType = edmType as CollectionType; if (collectionType != null) { return String.Format(CultureInfo.InvariantCulture, "ICollection<{0}>", GetTypeName(collectionType.TypeUsage, modelNamespace)); } var typeName = _code.Escape(edmType.MetadataProperties .Where(p => p.Name == ExternalTypeNameAttributeName) .Select(p => (string)p.Value) .FirstOrDefault()) ?? (modelNamespace != null && edmType.NamespaceName != modelNamespace ? _code.CreateFullName(_code.EscapeNamespace(edmType.NamespaceName), _code.Escape(edmType)) : _code.Escape(edmType)); if (edmType is StructuralType) { return typeName; } if (edmType is SimpleType) { var clrType = UnderlyingClrType(edmType); if (!IsEnumType(edmType)) { typeName = _code.Escape(clrType); } return clrType.IsValueType && isNullable == true ? String.Format(CultureInfo.InvariantCulture, "Nullable<{0}>", typeName) : typeName; } throw new ArgumentException("edmType"); } public Type UnderlyingClrType(EdmType edmType) { ArgumentNotNull(edmType, "edmType"); var primitiveType = edmType as PrimitiveType; if (primitiveType != null) { return primitiveType.ClrEquivalentType; } if (IsEnumType(edmType)) { return GetEnumUnderlyingType(edmType).ClrEquivalentType; } return typeof(object); } public object GetEnumMemberValue(MetadataItem enumMember) { ArgumentNotNull(enumMember, "enumMember"); var valueProperty = enumMember.GetType().GetProperty("Value"); return valueProperty == null ? null : valueProperty.GetValue(enumMember, null); } public string GetEnumMemberName(MetadataItem enumMember) { ArgumentNotNull(enumMember, "enumMember"); var nameProperty = enumMember.GetType().GetProperty("Name"); return nameProperty == null ? null : (string)nameProperty.GetValue(enumMember, null); } public System.Collections.IEnumerable GetEnumMembers(EdmType enumType) { ArgumentNotNull(enumType, "enumType"); var membersProperty = enumType.GetType().GetProperty("Members"); return membersProperty != null ? (System.Collections.IEnumerable)membersProperty.GetValue(enumType, null) : Enumerable.Empty<MetadataItem>(); } public bool EnumIsFlags(EdmType enumType) { ArgumentNotNull(enumType, "enumType"); var isFlagsProperty = enumType.GetType().GetProperty("IsFlags"); return isFlagsProperty != null && (bool)isFlagsProperty.GetValue(enumType, null); } public bool IsEnumType(GlobalItem edmType) { ArgumentNotNull(edmType, "edmType"); return edmType.GetType().Name == "EnumType"; } public PrimitiveType GetEnumUnderlyingType(EdmType enumType) { ArgumentNotNull(enumType, "enumType"); return (PrimitiveType)enumType.GetType().GetProperty("UnderlyingType").GetValue(enumType, null); } public string CreateLiteral(object value) { if (value == null || value.GetType() != typeof(TimeSpan)) { return _code.CreateLiteral(value); } return string.Format(CultureInfo.InvariantCulture, "new TimeSpan({0})", ((TimeSpan)value).Ticks); } public bool VerifyCaseInsensitiveTypeUniqueness(IEnumerable<string> types, string sourceFile) { ArgumentNotNull(types, "types"); ArgumentNotNull(sourceFile, "sourceFile"); var hash = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase); if (types.Any(item => !hash.Add(item))) { _errors.Add( new CompilerError(sourceFile, -1, -1, "6023", String.Format(CultureInfo.CurrentCulture, GetResourceString("Template_CaseInsensitiveTypeConflict")))); return false; } return true; } public IEnumerable<SimpleType> GetEnumItemsToGenerate(IEnumerable<GlobalItem> itemCollection) { return GetItemsToGenerate<SimpleType>(itemCollection) .Where(e => IsEnumType(e)); } public IEnumerable<T> GetItemsToGenerate<T>(IEnumerable<GlobalItem> itemCollection) where T: EdmType { return itemCollection .OfType<T>() .Where(i => !i.MetadataProperties.Any(p => p.Name == ExternalTypeNameAttributeName)) .OrderBy(i => i.Name); } public IEnumerable<string> GetAllGlobalItems(IEnumerable<GlobalItem> itemCollection) { return itemCollection .Where(i => i is EntityType || i is ComplexType || i is EntityContainer || IsEnumType(i)) .Select(g => GetGlobalItemName(g)); } public string GetGlobalItemName(GlobalItem item) { if (item is EdmType) { return ((EdmType)item).Name; } else { return ((EntityContainer)item).Name; } } public IEnumerable<EdmProperty> GetSimpleProperties(EntityType type) { return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); } public IEnumerable<EdmProperty> GetSimpleProperties(ComplexType type) { return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type); } public IEnumerable<EdmProperty> GetComplexProperties(EntityType type) { return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); } public IEnumerable<EdmProperty> GetComplexProperties(ComplexType type) { return type.Properties.Where(p => p.TypeUsage.EdmType is ComplexType && p.DeclaringType == type); } public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(EntityType type) { return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null); } public IEnumerable<EdmProperty> GetPropertiesWithDefaultValues(ComplexType type) { return type.Properties.Where(p => p.TypeUsage.EdmType is SimpleType && p.DeclaringType == type && p.DefaultValue != null); } public IEnumerable<NavigationProperty> GetNavigationProperties(EntityType type) { return type.NavigationProperties.Where(np => np.DeclaringType == type); } public IEnumerable<NavigationProperty> GetCollectionNavigationProperties(EntityType type) { return type.NavigationProperties.Where(np => np.DeclaringType == type && np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many); } public FunctionParameter GetReturnParameter(EdmFunction edmFunction) { ArgumentNotNull(edmFunction, "edmFunction"); var returnParamsProperty = edmFunction.GetType().GetProperty("ReturnParameters"); return returnParamsProperty == null ? edmFunction.ReturnParameter : ((IEnumerable<FunctionParameter>)returnParamsProperty.GetValue(edmFunction, null)).FirstOrDefault(); } public bool IsComposable(EdmFunction edmFunction) { ArgumentNotNull(edmFunction, "edmFunction"); var isComposableProperty = edmFunction.GetType().GetProperty("IsComposableAttribute"); return isComposableProperty != null && (bool)isComposableProperty.GetValue(edmFunction, null); } public IEnumerable<FunctionImportParameter> GetParameters(EdmFunction edmFunction) { return FunctionImportParameter.Create(edmFunction.Parameters, _code, _ef); } public TypeUsage GetReturnType(EdmFunction edmFunction) { var returnParam = GetReturnParameter(edmFunction); return returnParam == null ? null : _ef.GetElementType(returnParam.TypeUsage); } public bool GenerateMergeOptionFunction(EdmFunction edmFunction, bool includeMergeOption) { var returnType = GetReturnType(edmFunction); return !includeMergeOption && returnType != null && returnType.EdmType.BuiltInTypeKind == BuiltInTypeKind.EntityType; } } public class EdmMetadataLoader { private readonly IDynamicHost _host; private readonly System.Collections.IList _errors; public EdmMetadataLoader(IDynamicHost host, System.Collections.IList errors) { ArgumentNotNull(host, "host"); ArgumentNotNull(errors, "errors"); _host = host; _errors = errors; } public IEnumerable<GlobalItem> CreateEdmItemCollection(string sourcePath) { ArgumentNotNull(sourcePath, "sourcePath"); if (!ValidateInputPath(sourcePath)) { return new EdmItemCollection(); } var schemaElement = LoadRootElement(_host.ResolvePath(sourcePath)); if (schemaElement != null) { using (var reader = schemaElement.CreateReader()) { IList<EdmSchemaError> errors; var itemCollection = MetadataItemCollectionFactory.CreateEdmItemCollection(new[] { reader }, out errors); ProcessErrors(errors, sourcePath); return itemCollection; } } return new EdmItemCollection(); } public string GetModelNamespace(string sourcePath) { ArgumentNotNull(sourcePath, "sourcePath"); if (!ValidateInputPath(sourcePath)) { return string.Empty; } var model = LoadRootElement(_host.ResolvePath(sourcePath)); if (model == null) { return string.Empty; } var attribute = model.Attribute("Namespace"); return attribute != null ? attribute.Value : ""; } private bool ValidateInputPath(string sourcePath) { if (sourcePath == "$" + "edmxInputFile" + "$") { _errors.Add( new CompilerError(_host.TemplateFile ?? sourcePath, 0, 0, string.Empty, GetResourceString("Template_ReplaceVsItemTemplateToken"))); return false; } return true; } public XElement LoadRootElement(string sourcePath) { ArgumentNotNull(sourcePath, "sourcePath"); var root = XElement.Load(sourcePath, LoadOptions.SetBaseUri | LoadOptions.SetLineInfo); return root.Elements() .Where(e => e.Name.LocalName == "Runtime") .Elements() .Where(e => e.Name.LocalName == "ConceptualModels") .Elements() .Where(e => e.Name.LocalName == "Schema") .FirstOrDefault() ?? root; } private void ProcessErrors(IEnumerable<EdmSchemaError> errors, string sourceFilePath) { foreach (var error in errors) { _errors.Add( new CompilerError( error.SchemaLocation ?? sourceFilePath, error.Line, error.Column, error.ErrorCode.ToString(CultureInfo.InvariantCulture), error.Message) { IsWarning = error.Severity == EdmSchemaErrorSeverity.Warning }); } } public bool IsLazyLoadingEnabled(EntityContainer container) { string lazyLoadingAttributeValue; var lazyLoadingAttributeName = MetadataConstants.EDM_ANNOTATION_09_02 + ":LazyLoadingEnabled"; bool isLazyLoading; return !MetadataTools.TryGetStringMetadataPropertySetting(container, lazyLoadingAttributeName, out lazyLoadingAttributeValue) || !bool.TryParse(lazyLoadingAttributeValue, out isLazyLoading) || isLazyLoading; } } public static void ArgumentNotNull<T>(T arg, string name) where T : class { if (arg == null) { throw new ArgumentNullException(name); } } private static readonly Lazy<System.Resources.ResourceManager> ResourceManager = new Lazy<System.Resources.ResourceManager>( () => new System.Resources.ResourceManager("System.Data.Entity.Design", typeof(MetadataItemCollectionFactory).Assembly), isThreadSafe: true); public static string GetResourceString(string resourceName) { ArgumentNotNull(resourceName, "resourceName"); return ResourceManager.Value.GetString(resourceName, null); } #>