diff --git a/EfEnumToLookup/EfEnumToLookup.csproj b/EfEnumToLookup/EfEnumToLookup.csproj index 70a0b0e..32a5d14 100644 --- a/EfEnumToLookup/EfEnumToLookup.csproj +++ b/EfEnumToLookup/EfEnumToLookup.csproj @@ -47,13 +47,14 @@ + - + diff --git a/EfEnumToLookup/LookupGenerator/ContextExtensions.cs b/EfEnumToLookup/LookupGenerator/ContextExtensions.cs new file mode 100644 index 0000000..3ae32ad --- /dev/null +++ b/EfEnumToLookup/LookupGenerator/ContextExtensions.cs @@ -0,0 +1,48 @@ +namespace EfEnumToLookup.LookupGenerator +{ + using System.Data.Entity; + using System.Data.Entity.Core.Metadata.Edm; + using System.Data.Entity.Core.Objects; + using System.Data.Entity.Infrastructure; + using System.Linq; + using System.Text.RegularExpressions; + + public static class ContextExtensions + { + public static string GetTableName(this DbContext context) where T : class + { + var objectContext = ((IObjectContextAdapter)context).ObjectContext; + + return objectContext.GetTableName(); + } + + public static string GetTableName(this ObjectContext context) where T : class + { + string sql = context.CreateObjectSet().ToTraceString(); + Regex regex = new Regex("FROM (?.*) AS"); + Match match = regex.Match(sql); + + string table = match.Groups["table"].Value; + return table; + } + + public static string GetDefaultSchema(this DbContext context) + { + ////var table = (((IObjectContextAdapter)context).ObjectContext).MetadataWorkspace.GetItems(DataSpace.SSpace).FirstOrDefault(); + + var table = (((IObjectContextAdapter)context).ObjectContext).MetadataWorkspace.GetItemCollection(DataSpace.SSpace) + .GetItems() + .Single() + .BaseEntitySets + .OfType() + .SingleOrDefault(s => !s.MetadataProperties.Contains("Type") || s.MetadataProperties["Type"].ToString() == "Tables"); + + if(table == null) + { + return "dbo"; + } + + return table.MetadataProperties["Schema"].Value.ToString(); + } + } +} diff --git a/EfEnumToLookup/LookupGenerator/EnumParser.cs b/EfEnumToLookup/LookupGenerator/EnumParser.cs index 3bd9351..9ecbe48 100644 --- a/EfEnumToLookup/LookupGenerator/EnumParser.cs +++ b/EfEnumToLookup/LookupGenerator/EnumParser.cs @@ -57,7 +57,8 @@ public IEnumerable GetLookupValues(Type lookup) { Id = (int)numericValue, Name = EnumName(value), - }); + Description = EnumDescriptionValue(value) + }); } return values; } @@ -70,12 +71,6 @@ public IEnumerable GetLookupValues(Type lookup) /// private string EnumName(Enum value) { - var description = EnumDescriptionValue(value); - if (description != null) - { - return description; - } - var name = value.ToString(); if (SplitWords) diff --git a/EfEnumToLookup/LookupGenerator/EnumToLookup.cs b/EfEnumToLookup/LookupGenerator/EnumToLookup.cs index f8138ef..6578ab5 100644 --- a/EfEnumToLookup/LookupGenerator/EnumToLookup.cs +++ b/EfEnumToLookup/LookupGenerator/EnumToLookup.cs @@ -1,29 +1,28 @@ namespace EfEnumToLookup.LookupGenerator { - using System; - using System.Collections.Generic; - using System.ComponentModel; - using System.Data.Entity; - using System.Data.Entity.Infrastructure; - using System.Data.SqlClient; - using System.Linq; - using System.Reflection; - using System.Text; - using System.Text.RegularExpressions; - - /// - /// Makes up for a missing feature in Entity Framework 6.1 - /// Creates lookup tables and foreign key constraints based on the enums - /// used in your model. - /// Use the properties exposed to control behaviour. - /// Run Apply from your Seed method in either your database initializer - /// or your EF Migrations. - /// It is safe to run repeatedly, and will ensure enum values are kept in line - /// with your current code. - /// Source code: https://github.com/timabell/ef-enum-to-lookup - /// License: MIT - /// - public class EnumToLookup : IEnumToLookup + using System; + using System.Collections.Generic; + using System.Data.Entity; + using System.Data.Entity.Core.Metadata.Edm; + using System.Data.Entity.Infrastructure; + using System.Data.SqlClient; + using System.Linq; + using System.Reflection; + using System.Text; + + /// + /// Makes up for a missing feature in Entity Framework 6.1 + /// Creates lookup tables and foreign key constraints based on the enums + /// used in your model. + /// Use the properties exposed to control behaviour. + /// Run Apply from your Seed method in either your database initializer + /// or your EF Migrations. + /// It is safe to run repeatedly, and will ensure enum values are kept in line + /// with your current code. + /// Source code: https://github.com/timabell/ef-enum-to-lookup + /// License: MIT + /// + public class EnumToLookup : IEnumToLookup { private readonly EnumParser _enumParser; @@ -31,7 +30,8 @@ public EnumToLookup() { // set default behaviour, can be overridden by setting properties on object before calling Apply() NameFieldLength = 255; - TableNamePrefix = "Enum_"; + DescriptionFieldLength = 255; + TableNamePrefix = "Enum_"; _enumParser = new EnumParser { SplitWords = true }; } @@ -51,12 +51,18 @@ public bool SplitWords /// public int NameFieldLength { get; set; } - /// - /// Prefix to add to all the generated tables to separate help group them together - /// and make them stand out as different from other tables. - /// Defaults to "Enum_" set to null or "" to not have any prefix. + /// + /// The size of the Description field that will be added to the generated lookup tables. + /// Adjust to suit your data if required, defaults to 255. /// - public string TableNamePrefix { get; set; } + public int DescriptionFieldLength { get; set; } + + /// + /// Prefix to add to all the generated tables to separate help group them together + /// and make them stand out as different from other tables. + /// Defaults to "Enum_" set to null or "" to not have any prefix. + /// + public string TableNamePrefix { get; set; } /// /// Suffix to add to all the generated tables to separate help group them together @@ -110,7 +116,8 @@ private IDbHandler GetDbHandler() IDbHandler dbHandler = new SqlServerHandler { NameFieldLength = NameFieldLength, - TableNamePrefix = TableNamePrefix, + DescriptionFieldLength = DescriptionFieldLength, + TableNamePrefix = TableNamePrefix, TableNameSuffix = TableNameSuffix, }; return dbHandler; @@ -136,16 +143,17 @@ from enm in enums NumericType = enm.GetEnumUnderlyingType(), Values = _enumParser.GetLookupValues(enm), }).ToList(); - + var model = new LookupDbModel { Lookups = lookups, References = enumReferences, - }; + Schema = context.GetDefaultSchema() + }; return model; } - - private static int ExecuteSqlCommand(DbContext context, string sql, IEnumerable parameters = null) + + private static int ExecuteSqlCommand(DbContext context, string sql, IEnumerable parameters = null) { if (parameters == null) { diff --git a/EfEnumToLookup/LookupGenerator/IDbHandler.cs b/EfEnumToLookup/LookupGenerator/IDbHandler.cs index c26c47c..2f4ca31 100644 --- a/EfEnumToLookup/LookupGenerator/IDbHandler.cs +++ b/EfEnumToLookup/LookupGenerator/IDbHandler.cs @@ -12,11 +12,17 @@ internal interface IDbHandler /// int NameFieldLength { get; set; } - /// - /// Prefix to add to all the generated tables to separate help group them together - /// and make them stand out as different from other tables. + /// + /// The size of the Description field that will be added to the generated lookup tables. + /// Adjust to suit your data if required. /// - string TableNamePrefix { get; set; } + int DescriptionFieldLength { get; set; } + + /// + /// Prefix to add to all the generated tables to separate help group them together + /// and make them stand out as different from other tables. + /// + string TableNamePrefix { get; set; } /// /// Suffix to add to all the generated tables to separate help group them together diff --git a/EfEnumToLookup/LookupGenerator/LookupDbModel.cs b/EfEnumToLookup/LookupGenerator/LookupDbModel.cs index e8d79b8..eab367b 100644 --- a/EfEnumToLookup/LookupGenerator/LookupDbModel.cs +++ b/EfEnumToLookup/LookupGenerator/LookupDbModel.cs @@ -9,6 +9,7 @@ namespace EfEnumToLookup.LookupGenerator /// internal class LookupDbModel { + public string Schema { get; set; } public IList Lookups { get; set; } public IList References { get; set; } } diff --git a/EfEnumToLookup/LookupGenerator/LookupValue.cs b/EfEnumToLookup/LookupGenerator/LookupValue.cs index ac1c0ae..4b62398 100644 --- a/EfEnumToLookup/LookupGenerator/LookupValue.cs +++ b/EfEnumToLookup/LookupGenerator/LookupValue.cs @@ -4,5 +4,6 @@ internal class LookupValue { public int Id { get; set; } public string Name { get; set; } - } + public string Description { get; set; } + } } diff --git a/EfEnumToLookup/LookupGenerator/SqlServerHandler.cs b/EfEnumToLookup/LookupGenerator/SqlServerHandler.cs index 7830b6b..8dea874 100644 --- a/EfEnumToLookup/LookupGenerator/SqlServerHandler.cs +++ b/EfEnumToLookup/LookupGenerator/SqlServerHandler.cs @@ -13,12 +13,18 @@ class SqlServerHandler : IDbHandler /// public int NameFieldLength { get; set; } - /// - /// Prefix to add to all the generated tables to separate help group them together - /// and make them stand out as different from other tables. - /// Defaults to "Enum_" set to null or "" to not have any prefix. + /// + /// The size of the Description field that will be added to the generated lookup tables. + /// Adjust to suit your data if required, defaults to 255. /// - public string TableNamePrefix { get; set; } + public int DescriptionFieldLength { get; set; } + + /// + /// Prefix to add to all the generated tables to separate help group them together + /// and make them stand out as different from other tables. + /// Defaults to "Enum_" set to null or "" to not have any prefix. + /// + public string TableNamePrefix { get; set; } /// /// Suffix to add to all the generated tables to separate help group them together @@ -45,32 +51,33 @@ private string BuildSql(LookupDbModel model) sql.AppendLine("set nocount on;"); sql.AppendLine("set xact_abort on; -- rollback on error"); sql.AppendLine("begin tran;"); - sql.AppendLine(CreateTables(model.Lookups)); - sql.AppendLine(PopulateLookups(model.Lookups)); - sql.AppendLine(AddForeignKeys(model.References)); + sql.AppendLine(CreateTables(model.Lookups, model.Schema)); + sql.AppendLine(PopulateLookups(model.Lookups, model.Schema)); + sql.AppendLine(AddForeignKeys(model.References, model.Schema)); sql.AppendLine("commit;"); return sql.ToString(); } - private string CreateTables(IEnumerable enums) + private string CreateTables(IEnumerable enums, string schema) { var sql = new StringBuilder(); + foreach (var lookup in enums) - { + { sql.AppendFormat( - @"IF OBJECT_ID('{0}', 'U') IS NULL + @"IF OBJECT_ID('{0}', 'U') IS NULL begin - CREATE TABLE [{0}] (Id {2} CONSTRAINT PK_{0} PRIMARY KEY, Name nvarchar({1})); - exec sys.sp_addextendedproperty @name=N'MS_Description', @level0type=N'SCHEMA', @level0name=N'dbo', @level1type=N'TABLE', + CREATE TABLE [{4}].[{0}] (Id {2} CONSTRAINT PK_{0} PRIMARY KEY, Name nvarchar({1}), Description nvarchar({3})); + exec sys.sp_addextendedproperty @name=N'MS_Description', @level0type=N'SCHEMA', @level0name=N'{4}', @level1type=N'TABLE', @level1name=N'{0}', @value=N'Automatically generated. Contents will be overwritten on app startup. Table & contents generated by https://github.com/timabell/ef-enum-to-lookup'; end ", - TableName(lookup.Name), NameFieldLength, NumericSqlType(lookup.NumericType)); + TableName(lookup.Name), NameFieldLength, NumericSqlType(lookup.NumericType), DescriptionFieldLength, schema); } return sql.ToString(); } - private string AddForeignKeys(IEnumerable refs) + private string AddForeignKeys(IEnumerable refs, string schema) { var sql = new StringBuilder(); foreach (var enumReference in refs) @@ -78,45 +85,44 @@ private string AddForeignKeys(IEnumerable refs) var fkName = string.Format("FK_{0}_{1}", enumReference.ReferencingTable, enumReference.ReferencingField); sql.AppendFormat( - " IF OBJECT_ID('{0}', 'F') IS NULL ALTER TABLE [{1}] ADD CONSTRAINT {0} FOREIGN KEY ([{2}]) REFERENCES [{3}] (Id);\r\n", - fkName, enumReference.ReferencingTable, enumReference.ReferencingField, TableName(enumReference.EnumType.Name) - ); + " IF OBJECT_ID('{0}', 'F') IS NULL ALTER TABLE [{4}].[{1}] ADD CONSTRAINT {0} FOREIGN KEY ([{2}]) REFERENCES [{4}].[{3}] (Id);\r\n", + fkName, enumReference.ReferencingTable, enumReference.ReferencingField, TableName(enumReference.EnumType.Name), schema); } return sql.ToString(); } - private string PopulateLookups(IEnumerable lookupData) + private string PopulateLookups(IEnumerable lookupData, string schema) { var sql = new StringBuilder(); - sql.AppendLine(string.Format("CREATE TABLE #lookups (Id int, Name nvarchar({0}) COLLATE database_default);", NameFieldLength)); + sql.AppendLine(string.Format("CREATE TABLE #lookups (Id int, Name nvarchar({0}), Description nvarchar({1}) COLLATE database_default);", NameFieldLength, DescriptionFieldLength)); foreach (var lookup in lookupData) { - sql.AppendLine(PopulateLookup(lookup)); + sql.AppendLine(PopulateLookup(lookup, schema)); } sql.AppendLine("DROP TABLE #lookups;"); return sql.ToString(); } - private string PopulateLookup(LookupData lookup) + private string PopulateLookup(LookupData lookup, string schema) { var sql = new StringBuilder(); foreach (var value in lookup.Values) { - sql.AppendFormat("INSERT INTO #lookups (Id, Name) VALUES ({0}, N'{1}');\r\n", value.Id, SanitizeSqlString(value.Name)); + sql.AppendFormat("INSERT INTO #lookups (Id, Name, Description) VALUES ({0}, N'{1}', N'{2}');\r\n", value.Id, SanitizeSqlString(value.Name), SanitizeSqlString(value.Description)); } sql.AppendLine(string.Format(@" -MERGE INTO [{0}] dst +MERGE INTO [{1}].[{0}] dst USING #lookups src ON src.Id = dst.Id WHEN MATCHED AND src.Name <> dst.Name THEN UPDATE SET Name = src.Name WHEN NOT MATCHED THEN - INSERT (Id, Name) - VALUES (src.Id, src.Name) + INSERT (Id, Name, Description) + VALUES (src.Id, src.Name, src.Description) WHEN NOT MATCHED BY SOURCE THEN DELETE ;" - , TableName(lookup.Name))); + , TableName(lookup.Name), schema)); sql.AppendLine("TRUNCATE TABLE #lookups;"); return sql.ToString(); @@ -124,7 +130,7 @@ WHEN NOT MATCHED BY SOURCE THEN private static string SanitizeSqlString(string value) { - return value.Replace("'", "''"); + return value == null ? null : value.Replace("'", "''"); } private string TableName(string enumName) diff --git a/EfEnumToLookup/Properties/AssemblyInfo.cs b/EfEnumToLookup/Properties/AssemblyInfo.cs index 69eb094..04fba4a 100644 --- a/EfEnumToLookup/Properties/AssemblyInfo.cs +++ b/EfEnumToLookup/Properties/AssemblyInfo.cs @@ -33,6 +33,6 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.8.0.0")] +[assembly: AssemblyFileVersion("2.0.0.0")] [assembly:InternalsVisibleTo("EfEnumToLookupTests,PublicKey=002400000480000094000000060200000024000052534131000400000100010099a4b9a877a10f75bc4c1a9bf08ee9e21a2df3b9746f2535505b77b3f82971aa8a07caa6cd60b9df6e4b2982ebdb78da960ec1e7688a3b56b6da65e05783ee51f345b1cf0e5a38a02f3cc7c1763d7e469edafc59ef8c21b1a9bcca8548973429577362d992e4a6ed001cb28dc54ecc7ef298e94676bd918fd9e9b521e0cb7ae5")] diff --git a/EfEnumToLookupTests/App.config b/EfEnumToLookupTests/App.config index 047f1c8..7643ed3 100644 --- a/EfEnumToLookupTests/App.config +++ b/EfEnumToLookupTests/App.config @@ -10,7 +10,7 @@ - + diff --git a/EfEnumToLookupTests/EfEnumToLookupTests.csproj b/EfEnumToLookupTests/EfEnumToLookupTests.csproj index c1764a2..b202401 100644 --- a/EfEnumToLookupTests/EfEnumToLookupTests.csproj +++ b/EfEnumToLookupTests/EfEnumToLookupTests.csproj @@ -100,6 +100,9 @@ EfEnumToLookup + + +