Skip to content

SqlConnection.InsertAsync tries to insert IList<> property, AutoMapper fails to ignore it, Map() throws exception for IList #266

@balchen

Description

@balchen

I'm trying to use DapperExtensions to do SqlConnection.InsertAsync on my POCO that looks like this:

  public class Customer 
  {
     public int Id { get; set; }
     public string Name { get; set; }
     ......
     public IList<App> Apps { get; set; }
  }

When I do SqlConnection.InsertAsync(customer), it fails with a System.NotSupportedException: The member of type App cannot be used as a parameter value.

It took me a while to spot the extra space after "member". Obviously there was supposed to be a member name in that exception message, but it's blank.

For one, I can't understand why InsertAsync is trying to map an IList property, but this behaviour is the same with Dapper.Contrib. Since I can't annotate the POCO to ignore the property, I've implemented a custom mapper:

    public class CustomerMap: PluralizedAutoClassMapper<Customer>
    {
        public CustomerMap()
            : base()
        {
            Map(c => c.Apps).Ignore();
        }
    }

and then on startup I do:

            DapperExtensions.DapperAsyncExtensions.DefaultMapper = typeof(PluralizedAutoClassMapper<>);
            DapperExtensions.DapperAsyncExtensions.SetMappingAssemblies(new[]
            {
                this.GetType().Assembly
            });

At runtime, the mapping code is executed, and Map() throws ApplicationException: "Unable to UnMap because mapping does not exist."

That message makes absolutely no sense to me at this point, since the call to base() would/should have caused the AutoClassMapper to AutoMap().

I looked at the code for ClassMapper, and the exception message seems to be a copy-paste mistake:


        /// <summary>
        /// Fluently, maps an entity property to a column
        /// </summary>
        protected virtual MemberMap Map(PropertyInfo propertyInfo, MemberMap parent = null)
        {
            var result = new MemberMap(propertyInfo, this, parent: parent);
            if (GuardForDuplicatePropertyMap(result))
            {
                if (propertyInfo.PropertyType.IsClass)
                {
                    result = (MemberMap)Properties.FirstOrDefault(p => p.Name.Equals(result.Name) && p.ParentProperty == result.ParentProperty);
                }
                else
                {
                    throw new ApplicationException("Unable to UnMap because mapping does not exist.");
                }
            }
            else
            {
                Properties.Add(result);
            }
            return result;
        }

        /// <summary>
        /// Removes a propertymap entry
        /// </summary>
        /// <param name="expression"></param>
        protected virtual void UnMap(Expression<Func<T, object>> expression)
        {
            var propertyInfo = ReflectionHelper.GetProperty(expression) as PropertyInfo;
            var mapping = Properties.SingleOrDefault(w => w.Name == propertyInfo.Name);

            if (mapping == null)
            {
                throw new ApplicationException("Unable to UnMap because mapping does not exist.");
            }

            this.Properties.Remove(mapping);
        }

The exception message is identical to the one from UnMap(), where it makes more sense.

However, even if the exception message made sense, an exception is still thrown by ClassMapper. The exception is thrown if I try to Map() a property -- that is not a class -- for the second time. Map() won't throw exception the first time and happily map the property, but the second time around, only class properties can be mapped.

This logic also makes no sense to me. I'm brand new to this library and code base, so I can't say that this logic is incorrect, but it certainly seems weird.

Seeing this, I changed my IList<> property to List<>, and Map() will now allow me to map it and ignore it.

Still, InsertAsync throw the same System.NotSupportedException: The member of type App cannot be used as a parameter value.

Obviously, InsertAsync is still trying to insert a value based on List, and it isn't ignoring it like the mapping says.

It feels like I'm halfway down a rabbit hole here, and I'm either waaaay off or there are several bugs interacting in unhealthy ways.

Nuget library version: 1.7.0.

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions