automappersql-insertdappercomposite-primary-keydapper-extensions

Dapper Extensions custom ClassMapper isn't called on Insert()


I'm using Dapper Extensions and have defined my own custom mapper to deal with entities with composite keys.

     public class MyClassMapper<T> : ClassMapper<T> where T : class
{
    public MyClassMapper()
    {
        // Manage unmappable attributes

        IList<PropertyInfo> toIgnore = typeof(T).GetProperties().Where(x => !x.CanWrite).ToList();

        foreach (PropertyInfo propertyInfo in toIgnore.ToList())
        {
            Map(propertyInfo).Ignore();
        }

        // Manage keys

        IList<PropertyInfo> propsWithId = typeof(T).GetProperties().Where(x => x.Name.EndsWith("Id") || x.Name.EndsWith("ID")).ToList();
        PropertyInfo primaryKey = propsWithId.FirstOrDefault(x => string.Equals(x.Name, $"{nameof(T)}Id", StringComparison.CurrentCultureIgnoreCase));

        if (primaryKey != null && primaryKey.PropertyType == typeof(int))
        {
            Map(primaryKey).Key(KeyType.Identity);
        }
        else if (propsWithId.Any())
        {
            foreach (PropertyInfo prop in propsWithId)
            {
                Map(prop).Key(KeyType.Assigned);
            }
        }

        AutoMap();
    }
}

I also have this test case to test my mapper:

    [Test]
    public void TestMyAutoMapper()
    {
        DapperExtensions.DapperExtensions.DefaultMapper = typeof(MyClassMapper<>);

        MySubscribtionEntityWithCompositeKey entity = new MySubscribtionEntityWithCompositeKey
        {
            SubscriptionID = 145,
            CustomerPackageID = 32
        };

        using (var connection = new SqlConnection(CONNECTION_STRING))
        {
            connection.Open();
            var result = connection.Insert(entity);
            var key1 = result.SubscriptionID;
            var key2 = result.CustomerPackageID;
        }
    }

Note that I set the default mapper in the test case.

The insert fails and I notive that my customer mapper is never called. I have no documentation on the github page on the topic, so I'm not sure if there's anything else I need to do to make dapper extensions use my mapper.

Thanks in advance!


Solution

  • Looking at your question, you are attempting to write your own defalut class mapper derived from the existing one. I never used this approach; so I do not know why it is not working or whether it should work.

    I explicitly map the classes as below:

    public class Customer
    {
        public int CustomerID { get; set; }
        public string Name { get; set; }
    }
    
    public sealed class CustomerMapper : ClassMapper<Customer>
    {
        public CustomerMapper()
        {
            Schema("dbo");
            Table("Customer");
            Map(x => x.CustomerID).Key(KeyType.Identity);
            AutoMap();
        }
    }
    

    The AutoMap() will map rest of the properties based on conventions. Please refer to these two resources for more information about mapping.

    Then I call SetMappingAssemblies at the startup of the project as below:

    DapperExtensions.DapperExtensions.SetMappingAssemblies(new[] { Assembly.GetExecutingAssembly() });
    

    The GetExecutingAssembly() is used in above code because mapping classes (CustomerMapper and other) are in same assembly which is executing. If those classes are placed in other assembly, provide that assembly instead.

    And that's it, it works.

    To set the dialect, I call following line just below the SetMappingAssemblies:

    DapperExtensions.DapperExtensions.SqlDialect = new DapperExtensions.Sql.SqlServerDialect();
    

    Use your preferred dialect instead of SqlServerDialect.

    Apparently, the solution mentioned here may help you achieve what you are actually trying to. But, I cannot be sure, as I said above, I never used it.