Regression problem. I updated my .net Core 1.1 service to AutoMapper 6.1 and 6.1.1 and both now fail my tests. Downgrading to 6.0.2 fixes the problem.
I have a Repository, a DTO class, a matching EF class, and I create a method in the repository that looks like this:
public IQueryable<ShipmentDto> GetShipments()
{
try
{
var qry = _context.Shipments.ProjectTo<ShipmentDto>();
return qry;
}
catch (Exception ex)
{
throw ex;
}
}
(I just added the catch for debugging purposes). The code throws an Missing Method exception, explaining that I need a parameterless constructor. I have looked at the DTO, the EF class and there are NO constructors of ANY kind. They are just dumb data classes. And that includes the CarrierService property. Its class is also a dumb simple-type POCO. For giggles, I added a bunch of parameterless constructors to them and it did NOT fix the problem. The problem occurs in a call stack containing Activator.CreateInstance() called during the map from source to destination types.
Again, problem goes away when I simply downgrade AutoMapper back to 6.0.2. So something was introduced in 6.1.0 in the ProjectTo() call chain that breaks.
// Mapper.Initialize or just the CreateMap snippet
public class AutoMapperProfile : Profile
{
public AutoMapperProfile()
{
CreateMap<Shipment, ShipmentDto>()
.ForMember(dest => dest.CarrierService, opt => opt.MapFrom(src => src.CarrierService.ServiceDescription));
CreateMap<ShipmentDto, Shipment>()
.IgnoreAllPropertiesWithAnInaccessibleSetter();
CreateMap<OrderSummaryItem, OrderSummaryItemDto>();
}
}
I did try commenting out the .ForMember line just in case it had something to do with the CarrierService type, but this did NOT make the problem go away.
6.1.0 introduces the problem. Reversion to 6.0.2 makes the tests run fine.
I expect my repository Dto type to map properly to create an IQueryable I am using in the repository.
Instead, just creating the query object results in an exception in ProjectTo() down to Activator.CreateInstance().
A repro would help. Make a gist that we can execute and see fail.
I'll try to get a simple standalone one that I can make fail. I just wanted to get this in there in case anyone had an 'a-ha' from looking at the check-ins in this area.
This looks related to https://github.com/AutoMapper/AutoMapper/issues/2149
Well, I don't know about that, but a repro for that one would help too :)
I did a source pull for AutoMapper. I am besieged with work issues so I can't do this at this moment, but it's going into my midnight engineering stack to see if I can help you run this down, as it looks like I'm not alone on this one. I'd hate to be stuck on 6.0.2 forever :(
It might be related but as of now we have no repro.
I did debug this against AutoMapper source and reproduced it. The error is happening when an Nullable Int64 type called "StoreNumber" is being mapped to a Dto Type of the same name of type String.
Activator.CreateInstance is trying to create a System.String on line 14 of NullableSourceExpressionBinder.cs.
In C# Immediate this definitely agrees:
Activator.CreateInstance(typeof(System.String))
No parameterless constructor defined for this object.
- System.RuntimeTypeHandle.CreateInstance(System.RuntimeType, bool, bool, ref bool, ref System.RuntimeMethodHandleInternal, ref bool)
- System.RuntimeType.CreateInstanceSlow(bool, bool, bool, ref System.Threading.StackCrawlMark)
- System.Activator.CreateInstance(System.Type, bool)
- System.Activator.CreateInstance(System.Type)
But, as the problem didn't happen before 6.1 and my datatypes didn't change, did Automapper change the way it created types going from an Int64? to a String?
I pulled 6.0.2 branch and debugged that (which ran without error), but it looks like there was a massive re-write of the mapping resolution code between 6.0.2 and 6.1. I'm now looking at making a simple Int64 Nullable => String mapper repro.
OK, let us know :)
Complete example that reproduces the error.
using System;
using System.Linq;
using AutoMapper;
using System.Collections;
using System.Collections.Generic;
using AutoMapper.QueryableExtensions;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Mapper.Initialize(cfg =>
{
cfg.CreateMap<MyEntity, MyEntityDto>();
});
IQueryable<MyEntity> entities = new List<MyEntity>().AsQueryable();
var qry = entities.ProjectTo<MyEntityDto>();
Console.ReadKey();
}
}
public class MyEntity
{
public int Id { get; set; }
public Int64? StoreNumber { get; set; }
}
public class MyEntityDto
{
public int Id { get; set; }
public String StoreNumber { get; set; }
}
}
I see it now. But the problem is that with EF6 this doesn't work with any AM version.
Can this one get re-opened, though? Is there any reason this should NOT work? (Obviously it has worked for me for the last 7 months as I've been on building .net Core services with EF Core)
We're not fixing EF core issues at this time.
What is the expression it tries to generate?
I'm not sure I understand the question. In the actual application, it allows my controller and repository to do DTO-level queries, and let ProjectTo() convert it into the Entity LINQ that will then go to SQL Server with EF Core. My DTO's need a string version of the store number, so my DTO is string, my EF Entity is Int64, and AutoMapper has been building the LINQ Expression and mapping the value flawlessly ... right up through 6.0.2. The rework for 6.1 for nullable sources moved the code to Activator.CreateInstance, which tries to do that to the target (a string), and the CreateInstance barks about it. It didn't fail in 6.0.2 (from what I can see debugging it, because Activator.CreateInstance wasn't used in the class.
What is the Select expression that AutoMapper tries to generate?
I guess an in memory test might work here. It's about the order of binders. When I added the binding from Nullable to GetValueOrDefault, I placed it before the ToString one, that's why it doesn't work anymore. But in EF6, it doesn't matter. Hopefully we wouldn't break anything.
@rmagruder If you want to try a PR, I'll help. @jbogard seems open to a fix.
Okay first, just assume it's selecting the entire object. The actual object has some 30+ fields in it that come back, and additional .Where() extensions are added on to it based upon user filters. This is the 'core setup' piece of that query, which is why I return an IQueryable
@lbargaoanu I'm a little new to this. What is a PR? Edit : Never mind - I'm guessing "Pull Request". Thanks. I still need to figure this source out as the new code replaced a LOT of old code.
Pull request.
Is there any reason this issue can't stay open? I will attempt to find a solution in my time if you guys don't want to work on .net Core support, but I and anyone else encountering this is stuck on 6.0.3 indefinitely because of this...no workaround has been suggested.
@rmagruder it's not .NET Core, it's EF Core. Their LINQ query provider was completely re-written so things that work in EF 6 don't work in EF Core (and vice versa). NHibernate has similar issues.
@lbargaoanu I think that instead of queryable-specific packages, we might have to switch to feature flags for the various query providers on what they support (or not).
@jbogard Except that the only thing I've upgraded from working to not working was Automapper 6.0.2=>6.1.1 (6.1.0 also breaks). As long as I stay on AM 6.0.2, the ProjectTo seems to work flawlessly, I just can never upgrade AM anymore. The version of EF Core I'm using hasn't changed.
In this case it wouldn't help. But I see your point.
Is it just the order of our binders? What would it take to get this to work exactly?
It can be done by improving the condition on the NullableSourceBinder or by moving the StringBinder before it.
Moving the new StringExpressionBinder() above the two NullableBinders in ExpressionBinder.cs DOES make the problem go away. Whether that's the correct solution I don't know because I haven't figured out what ya'll are doing here. I am a bit flummoxed by the Activator.CreateInstance, though. I am not clear on what "improving the condition on the NullableSourceBinder" would accomplish. I guess reordering the binders just means a regression test to make sure it doesn't break something else, and that the ProjectTo works properly (my simple example doesn't actually query data).
That condition is overly broad and it catches this case. But it is broad in order to allow other useful cases. Maybe there is room for improvement here.
So.... reopen issue? :)
Just fix it :)
I still don't understand it. I've never been in the guts of automapper before. I can live with 6.0.2 and work with this as I have time.
Ran into this issue as well. I can't seem to revert AM version from 6.1.1 to 6.0.2 with ASP.NET Core 2.0. Package restore fails. What are my options?
A repro would help. Make a gist that we can execute and see fail.
I found it after a loong debugging session..! I only got the Missing Method Exception with no further details..
The property that caused the exception was a nullable DateTime that wouldn't map to a string 馃拑
As you can see above, this was already fixed. If you've got smth that still fails with the MyGet build I want to see it, but otherwise, there's nothing to do here.
I couldn't get the MyGet build to install.. Got an error in Package Manger Console like:
"Unable to find package AutoMapper with version 6.2.0-ci-01392"
Works now. Perhaps it was some nuget cache problems..
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Most helpful comment
I did debug this against AutoMapper source and reproduced it. The error is happening when an Nullable Int64 type called "StoreNumber" is being mapped to a Dto Type of the same name of type String.
Activator.CreateInstance is trying to create a System.String on line 14 of NullableSourceExpressionBinder.cs.
In C# Immediate this definitely agrees:
But, as the problem didn't happen before 6.1 and my datatypes didn't change, did Automapper change the way it created types going from an Int64? to a String?
I pulled 6.0.2 branch and debugged that (which ran without error), but it looks like there was a massive re-write of the mapping resolution code between 6.0.2 and 6.1. I'm now looking at making a simple Int64 Nullable => String mapper repro.