Using Editor with EF6, can I reuse the Model generated?

Using Editor with EF6, can I reuse the Model generated?

pcjacksonpcjackson Posts: 18Questions: 6Answers: 0
edited October 2019 in Free community support

Hi, I'm porting an existing EF6 application to use Editor. I'm reading the documentation, and it says to use a model class. Of course, I already have model classes because of EF6. Can I reuse these Model classes or modify them or create some sort of templated interface to expose only the things the Editor can handle? I just want to reduce the amount of repeated code, if possible.

(Platform is C# 7.2 using MVC.NET 5 and EF6.)

This question has accepted answers - jump to:

Answers

  • allanallan Posts: 63,162Questions: 1Answers: 10,408 Site admin
    Answer ✓

    Honestly, I'm not sure. If Editor can access the properties of the class via reflection then yes it would work. Its not something I've attempted though. Be interested to know how it works out if you try it.

    Regards,
    Allan

  • pcjacksonpcjackson Posts: 18Questions: 6Answers: 0
    edited October 2019

    It seems to have trouble with navigation properties.

    (Thanks for the help! I'm not accepting your post as an answer in case I actually manage to figure this out eventually.)

  • pcjacksonpcjackson Posts: 18Questions: 6Answers: 0
    edited October 2019

    Sorry for the double post. I believe I've figured out a very good way to do this. Caution, this is a code dump post.

    So, EF6 uses a template file (*.tt) to generate the code. The code responsible for generating the object looks like this:

    <#=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())
        {
    #>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
        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)
            {
    #>
        <#=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.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
                {
    #>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
    <#
                }
    #>
        <#=codeStringGenerator.NavigationProperty(navigationProperty)#>
    <#
            }
        }
    #>
    }
    

    Where EntityClassOpening is:

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

    So what I did is I created a second generator for just a stripped down version of the object without complex properties or navigation properties. It looks like:

    <#=codeStringGenerator.SimpleEntityClassOpening(entity)#>
    {
    <#
        if (propertiesWithDefaultValues.Any())
        {
    #>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
        public <#=code.Escape(entity)#>()
        {
    <#
            foreach (var edmProperty in propertiesWithDefaultValues)
            {
    #>
            this.<#=code.Escape(edmProperty)#> = <#=typeMapper.CreateLiteral(edmProperty.DefaultValue)#>;
    <#
            }
    #>
        }
    
    <#
        }
    
        if (simpleProperties.Any())
        {
            foreach (var edmProperty in simpleProperties)
            {
    #>
        <#=codeStringGenerator.Property(edmProperty)#>
    <#
            }
        }
    #>
    }
    

    Where SimpleEntityClassOpening is:

        public string SimpleEntityClassOpening(EntityType entity)
        {
            return string.Format(
                CultureInfo.InvariantCulture,
                "{0} {1}partial class Simple{2}{3}",
                Accessibility.ForType(entity),
                _code.SpaceAfter(_code.AbstractOption(entity)),
                _code.Escape(entity),
                _code.StringBefore(" : ", _typeMapper.GetTypeName(entity.BaseType)));
        }
    

    This might cause naming conflicts if you're fond of the word simple in your table names. You can of course change this to whatever you desire.

    Using these "Simple" models, it seems like the .NET library works just fine, at least for retrieving data.

  • allanallan Posts: 63,162Questions: 1Answers: 10,408 Site admin
    Answer ✓

    Awesome - thanks!

    Allan

  • pcjacksonpcjackson Posts: 18Questions: 6Answers: 0
    edited November 2019

    Hello, people of the future. I ran into an issue with DateTime fields with the above code. Per this post you have to ensure that they become string fields instead.

    So to make this happen using the EF6 generator, you need to add this method to CodeStringGenerator and call it instead of Property in the foreach loop.

        public string SimpleProperty(EdmProperty edmProperty)
        {
            string typename = _typeMapper.GetTypeName(edmProperty.TypeUsage);
            if (string.Equals(typename, "System.DateTime") ||
                string.Equals(typename, "Nullable<System.DateTime>"))
            {
                typename = "string";
            }
    
            return string.Format(
                CultureInfo.InvariantCulture,
                "{0} {1} {2} {{ {3}get; {4}set; }}",
                Accessibility.ForProperty(edmProperty),
                typename,
                _code.Escape(edmProperty),
                _code.SpaceAfter(Accessibility.ForGetter(edmProperty)),
                _code.SpaceAfter(Accessibility.ForSetter(edmProperty)));
        }
    

    This is kind of hacky and hard-coded. But hopefully the types we're playing with are more or less stable, so it won't be a problem.

This discussion has been closed.