sjh37 / EntityFramework-Reverse-POCO-Code-First-Generator

EntityFramework Reverse POCO Code First Generator - Beautifully generated code that is fully customisable. This generator creates code as if you reverse engineered a database and lovingly created the code by hand. It is free to academics (you need a .edu or a .ac email address), not free for commercial use. Obtain your licence from
https://www.reversepoco.co.uk/
Other
706 stars 230 forks source link

Upgrade from v2 to v3 - Missing IndexAttributes on columns #724

Closed ericoldre closed 3 years ago

ericoldre commented 3 years ago

We have been using version 2 of the generator to create EF6 classes for some time. We are looking to upgrade our team to use version 3. First step is getting version 3 to create a model as similar to version 2 as possible. It's actually gone quite smoothly with just a couple minor issues. The first of which is. We used to get IndexAttributes (DataAnnotations) added to column definitions in version 2. And example:

    [Table("REL_CHECKTABLE_VALUE", Schema = "dbo")]
    [Newtonsoft.Json.JsonObject(NamingStrategyType = typeof(Newtonsoft.Json.Serialization.CamelCaseNamingStrategy))]
    public partial class ChecktableValue : IChangeTracking, IActive
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        [Column(@"CHECKTABLE_VALUE_ID", Order = 1, TypeName = "uniqueidentifier")]
        [Index(@"PK_EXT_CHECKTABLE_VALUE", 1, IsUnique = true, IsClustered = true)]
        [Required]
        [Key]
        [Display(Name = "Checktable value ID")]
        public System.Guid ChecktableValueId { get; set; } = System.Guid.Empty;

        [Column(@"DATASOURCE_ID", Order = 2, TypeName = "uniqueidentifier")]
        [Index(@"IX_REL_CHECKTABLE_VALUE", 1, IsUnique = true, IsClustered = false)]
        [Required]
        [Display(Name = "Datasource ID")]
        public System.Guid DatasourceId { get; set; }

        // remainder omitted for brevity
    }

Other parts of our system are dependent on being able to use the [Index()] attributes via reflection.

I've been trying to use the UpdateColumn() method to add the attribute back, like this:

    // Use the following function if you need to apply additional modifications to a column
    // eg. normalise names etc.
    Settings.UpdateColumn = delegate(Column column, Table table, List<EnumDefinition> enumDefinitions)
    {
        if (Settings.UseDataAnnotations)
        {
            if (false) // detect index here
            {
                // figure out name, properties of index.
                column.Attributes.Add("[Index(name etc)]"); 
            }
        }
    }

However, it appears that the Table object passed to the method does not yet have the Indexs list populated on it yet when the UpdateColumn() function is called. They get added in a later step.

Looking for suggestions on how to retain those same data annotations as we had in version 2. Is there another place we could customize that happens later in the process which would give us a chance to add them to the column's Attributes list? Or some other approach?

sjh37 commented 3 years ago

Hi @ericoldre

It was removed because the data annotations did not define everthing that fluent configuration had, and therefore you always had both. This also meant that any business logic that wanted to use and process the poco's had to inherit a dependancy on entity framework due to entity framework attributes, which is not desirable.

However I understand that you've got reflection code to spot the index attribute. Do you actually need that specific attribute? Could you use your own marker attribute?

For v2, that code used to be in private void SetupConfig()

...
if (Settings.UseDataAnnotations && Indexes.Any())
{
    foreach (var index in Indexes)
    {
        DataAnnotations.Add(string.Format("Index(@\"{0}\", {1}, IsUnique = {2}, IsClustered = {3})",
            index.IndexName,
            index.KeyOrdinal,
            index.IsUnique ? "true" : "false",
            index.IsClustered ? "true" : "false"));
    }
}

in v3 it is callled protected override void SetupConfig(Column c)

ErikEJ commented 3 years ago

The attributes are actually in this package, which does not have any dependencies: https://www.nuget.org/packages/Microsoft.EntityFrameworkCore.Abstractions/

ericoldre commented 3 years ago

Thank you that was incredibly helpful! I managed to replicate the old functionality by using a custom Generator.

    public class GeneratorEf6WithIndexes : GeneratorEf6
    {
        public GeneratorEf6WithIndexes(FileManagementService fileManagementService, Type fileManagerType) 
            : base(fileManagementService, fileManagerType)
        {
        }

        protected override void SetupConfig(Column c)
        {
            base.SetupConfig(c);

            if (c.Indexes != null && c.Indexes.Any())
            {
                foreach (var index in idxes)
                {
                    // pulled from v2 codebase
                    string idxText = string.Format("Index(@\"{0}\", {1}, IsUnique = {2}, IsClustered = {3})",
                        index.IndexName,
                        index.KeyOrdinal,
                        index.IsUnique ? "true" : "false",
                        index.IsClustered ? "true" : "false");

                    c.Attributes.Add($"[{idxText}]");
                }

            }
        }
    }

Then use the custom generator instead of calling GeneratorFactory.Create() (near the end of the *.tt file)

    // original code that shipped with .tt file
    //var generator = GeneratorFactory.Create(fileManagement, FileManagerFactory.GetFileManagerType());

    // instead, create our OWN generator then call Init on it like the Factory.Create method would. 
    var generator = new GeneratorEf6WithIndexes(fileManagement, FileManagerFactory.GetFileManagerType());
    generator.Init(null);

Thank you very much for your help!