JasperFx / lamar

Fast Inversion of Control Tool and Successor to StructureMap
https://jasperfx.github.io/lamar
MIT License
568 stars 119 forks source link

Having trouble using AutoMapper #159

Closed cecbr closed 5 years ago

cecbr commented 5 years ago

Please,

I want to convert my StructureMap based project to Lamar.

All my classes that are using IMAPPER into constructor failed with message "Expression of type 'System.Object' cannot be used for assignment to type 'AutoMapper.IMapper'".

Can you help me?

Carlos. WebApplication22.zip

kmbell commented 5 years ago

I am also experiencing this error. AutoMapper worked with Lamar 2.x, but began failing after updating Lamar to 3.0.1, and 3.1.0 also has the same problem.

CodingGorilla commented 5 years ago

I currently use Lamar and AutoMapper in a .NET Core 2.2 web app with no problems. How are you registering AutoMapper?

kmbell commented 5 years ago

I'm using the AutoMapper.Extensions.Microsoft.DependencyInjection NuGet package and calling the following at the end of my Startup.ConfigureServices(..) method:

services.AddAutoMapper(typeof(Startup).Assembly);

I've done a bit more research into this and the issue seems to be related to registering a service using a manually created instance of ServiceDescriptor with an inline factory lambda. Here's how the extension method above registers the IMapper interface:

services.Add(new ServiceDescriptor(
    typeof(IMapper),
    sp => new Mapper(sp.GetRequiredService<IConfigurationProvider>(), sp.GetService),
    serviceLifetime));

Because the factory parameter of ServiceDescriptor is of type Func<IServiceProvider, object>, the lambda above that creates the new Mapper has return type object but the service type is IMapper. This is where the error in Lamar arises as it's trying to assign the result of calling the implementation factory function to a variable of type IMapper.

This can be fixed worked around by changing the AutoMapper registration like so:

Func<IServiceProvider, Mapper> factory = sp => new Mapper(sp.GetRequiredService<IConfigurationProvider>(), sp.GetService);
services.Add(new ServiceDescriptor(typeof(IMapper), factory, serviceLifetime));

With this change, the lambda is created with the correct type and the assignment works. I don't think this is really a problem with AutoMapper though as it is the compiler which is throwing away useful type information. Instead, I've tried adding a 'convert' expression to InlineLambdaCreationFrame in Lamar if the factory method result type does not match the service type.

var invokeMethod = _setter.InitialValue.GetType().GetMethod("Invoke");
var invoke = Expression.Call(Expression.Constant(_setter.InitialValue), invokeMethod, scope);

// Added cast to convert factory method result to the service type
Expression cast = invoke;
if (invoke.Type != variableExpr.Type)
{
     cast = Expression.Convert(invoke, variableExpr.Type);
}

definition.Body.Add(Expression.Assign(variableExpr, cast));

This also fixes the problem and will cover potentially more cases than just AutoMapper.

If this seems reasonable, let me know and I can prepare a pull request. Are there any tests for registering services through the .NET API as opposed to the Lamar native API?

jeremydmiller commented 5 years ago

Fixed by https://github.com/JasperFx/lamar/pull/164