henkmollema / Dommel

CRUD operations with Dapper made simple.
MIT License
634 stars 100 forks source link

Invalid Column map when inherited from base Entity #249

Closed BelvedereHenrique closed 3 years ago

BelvedereHenrique commented 3 years ago

Hi,

I have this base entity class:

    public class EntityBase
    {
        public int Id { get; set; }
        public bool Status { get; set; }
    }

and my entities inherit from it:

    public class Avaliacao : EntityBase
    {
        public string Designacao { get; set; }
    }
    public class CondicaoAcesso : EntityBase
    {
        public string Designacao { get; set; }
    }

Here are the mappings:

    public class AvaliacaoMap : DommelEntityMap<Avaliacao>
    {
        public AvaliacaoMap()
        {
            ToTable("T_AQV_AVALIACAO");
            Map(a => a.Id).ToColumn("PK_AVL").IsKey().IsIdentity();
            Map(a => a.Designacao).ToColumn("DESIG_AVL");
            Map(a => a.Status).ToColumn("STATUS_AVL");
        }
    }
    public class CondicaoAcessoMap : DommelEntityMap<CondicaoAcesso>
    {
        public CondicaoAcessoMap()
        {
            ToTable("T_AQV_COND_ACESSO");
            Map(a => a.Id).ToColumn("PK_CAC").IsKey().IsIdentity();
            Map(a => a.Designacao).ToColumn("DESIG_CAC");
            Map(a => a.Status).ToColumn("STATUS_CAC");
        }
    }

Here's the problem I'm facing.

If I create one of those, using dommel Insert, the second one won't work, because dommel (i think so) gets lost on columns mapping of the base entity. For exemple, If I insert Avaliacao, the columns are : PK_AVL, DESIG_AVL, STATUS_AVL. Then, right after, if I try to insert CondicaoAcesso, which the columns are: PK_CAC, DESIG_CAC, STATUS_CAC ; Dommel fails.

On the case mentioned above, dommel will try tu insert on CondicaoAcesso using these columns: PK_AVL, DESIG_CAC, STATUS_CAC

See that PK and Status are wrong? Something is binding the first entity columns we inserted (avaliacao) on the second insert (CondicaoAcesso);

Debugging the code, I think I've found where the issue is, but I don't know how to fix it yet. It seens like Insert.cs, line 86 (var columnNames = typeProperties.Select(p => Resolvers.Column(p, sqlBuilder)).ToArray();) is getting the wrong columns.

Here are 2 screenshots of: 1 - Inserting First Entity (avaliacao) and 2 - Trying to insert second entity (CondicaoAcesso) 1- image

2- image

Any idea on how to fix this, or if I'm doing something wrong?

Versions: image

BelvedereHenrique commented 3 years ago

One more thing, the same happens for the Update as well. But on the update, since it utilizes the Key column, both Status and PK are wrong.

BelvedereHenrique commented 3 years ago

I think I've found the problem source. Column resolver has a cache using a dictionary. The problem is, the key for the dictionary is the type of the entity. Since all of my entities inherit from a base entity, the cache always stores this base type as the key, not the correct one. So, whenever a second entity comes to be inserted, the cache retrieves the column name value it stored for EntityBase on the first request.

On this screenshot, I'm trying to insert a second entity of different type, but inherited from the same base class. Since the cache stores the base class as the key, as I mentioned above, it wont work. image

BelvedereHenrique commented 3 years ago

Is there a way to disable the cache or something like that?

BelvedereHenrique commented 3 years ago

Fixed.

Turned my EntityBase into a Interface, IEntityBase with properties. image (https://docs.microsoft.com/pt-br/dotnet/csharp/programming-guide/classes-and-structs/interface-properties)

Now the cache doesn't look to the interface to get the type and store it, it looks into the class instance type image

Followed this documentation to understand how getType works https://docs.microsoft.com/en-us/dotnet/api/system.type.makegenerictype?view=net-5.0

BelvedereHenrique commented 3 years ago

Still, a option to disable cache would be nice. What you think?

henkmollema commented 3 years ago

Using an interface is a good way to solve this. Since .NET Core there is no reliable way to determine the 'reflected' type of a property.

Disabled caching would hurt performance a lot, you don't want that.