How to Inject AutoMapper as an instance using FreshMvvm and FreshIOC - c#

I am trying to inject AutoMapper into my MainPageModel for my Xamarin.Forms app but it crashes when loading the app.
I am setting it up like this, first init the config and then pass the implementing type to the DI container.
// Init automapper
AutoMapper.Mapper.Initialize(cfg => cfg.AddProfile(new AppProfile()));
// Add automapper to DI
FreshMvvm.FreshIOC.Container.Register<IMapper, AutoMapper.Mapper>();
// Load page (results in crash)
var page = FreshPageModelResolver.ResolvePageModel<MainPageModel> ();
The error message I get is:
TinyIoCResolutionException: Resolve failed: IConfigurationProvider
If I try to inject a regular Service with some IService interface instead, that works perfectly fine so it seems to be an issue with how FreshMvvm instanstiates the AutoMapper instance.
In most examples I have seen with injecting AutoMapper with other DI-containers, an instance is first created together with some configuration and then added to the container. Like this for an regular dot net core app:
public void ConfigureServices(IServiceCollection services)
{
// Crate config instance
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new YourProfile());
});
// Create a mapper from config and add instance as singleton
services.AddSingleton<IMapper>(sp => config.CreateMapper())
}
According to the docs (https://github.com/rid00z/FreshMvvm#ioc-container-lifetime-registration-options) it seems FreshMvvm does not support adding instances though, and singletons are mapped like this:
FreshMvvm.FreshIOC.Container.Register<IService, MySingletonService>();
How can I inject AutoMapper with FreshMvvm? Do I need to create a DI mapping for the IConfigurationProvider provider as well? To which implementation if so?

The documentation is not obvious on this point. It is trivial once you understand how FreshIOC works. It accepts an instance as a parameter to the Register call.
var config = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new AppProfile());
});
FreshMvvm.FreshIOC.Container.Register<IMapper>(config.CreateMapper());

Related

Constructor injection of InstancePerRequest to service resolves new instance

I register a service FooRequest as InstancePerRequest in ASP.NET MVC & OWIN:
builder.RegisterType<FooRequest>().AsSelf().InstancePerRequest();
After that I resolve FooRequest in two locations. First is global.asax Application_BeginRequest():
protected void Application_BeginRequest(object sender, EventArgs e)
{
var fooRequest = DependencyResolver.Current.GetService<FooRequest>();
}
A second time in another services constructer. The other service has InstancePerLifetimeScope:
public class FooService
{
public FooService(FooRequest fooRequest)
{
...
}
}
My problem is that those two resolves in different instances of FooService and the one used in constructor injection of service does not call Dispose[Async] on the end of the request.
What am I doing wrong?
Btw. using DependencyResolver.Current.GetService<FooRequest>() outside the constructor does resolve the proper instance of FooRequest in FooService.
Additional requested information
OWIN & Container configuration:
[assembly: OwinStartup(typeof(Project.Web.Startup))]
namespace Project.Web
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
// REGISTER CONTROLLERS SO DEPENDENCIES ARE CONSTRUCTOR INJECTED
builder.RegisterControllers(typeof(MvcApplication).Assembly);
builder.RegisterApiControllers(typeof(MvcApplication).Assembly);
// REGISTER DEPENDENCIES
builder.RegisterModule(new ProjectWebModule());
builder.Register(c => new IdentityFactoryOptions<ApplicationUserManager> { DataProtectionProvider = app.GetDataProtectionProvider() });
// REGISTER FILTERS SO DEPENDENCIES ARE PROPERTY INJECTED
builder.RegisterFilterProvider();
// BUILD THE CONTAINER
var container = builder.Build();
// REPLACE THE MVC DEPENDENCY RESOLVER WITH AUTOFAC
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
// REPLACE THE WEBAPI DEPENDENCY RESOLVER WITH AUTOFAC
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
// REGISTER WITH OWIN
app.UseAutofacMiddleware(container);
app.UseAutofacMvc();
app.Use((context, next) =>
{
var httpContext = context.Get<HttpContextBase>(typeof(HttpContextBase).FullName);
httpContext.SetSessionStateBehavior(SessionStateBehavior.Required);
return next();
});
// STANDARD MVC SETUP
RouteConfig.RegisterRoutes(RouteTable.Routes);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); // immer nach RouteConfig.RegisterRoutes ausführen!
BundleConfig.RegisterBundles(BundleTable.Bundles);
// PLACE ConfigureAuth AFTER RegisterGlobalFilters
ConfigureAuth(app);
}
}
}
ProjectWebModule
namespace Project.Web.Autofac
{
public class ProjectWebModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<FooRequest>().AsSelf().InstancePerRequest();
builder.RegisterType<FooService>().AsSelf().InstancePerLifetimeScope();
builder.Register(c => HttpContext.Current.GetOwinContext().Authentication).As<IAuthenticationManager>();
builder.Register(c =>
//register FakeHttpContext when HttpContext is not available
HttpContext.Current != null
? new HttpContextWrapper(HttpContext.Current) as HttpContextBase
: new FakeHttpContext("~/") as HttpContextBase)
.As<HttpContextBase>()
.InstancePerLifetimeScope()
.OnActivated(e =>
{
Toolbox.HttpContext = e.Instance;
});
builder.Register(c => c.Resolve<HttpContextBase>().Request)
.As<HttpRequestBase>()
.InstancePerLifetimeScope();
builder.Register(c => c.Resolve<HttpContextBase>().Response)
.As<HttpResponseBase>()
.InstancePerLifetimeScope();
builder.Register(c => c.Resolve<HttpContextBase>().Server)
.As<HttpServerUtilityBase>()
.InstancePerLifetimeScope();
builder.Register(c => c.Resolve<HttpContextBase>().Session)
.As<HttpSessionStateBase>()
.InstancePerLifetimeScope();
base.Load(builder);
}
}
}
I mention in the comments on the question that there's not enough info here to really tell, but here's my guess: you have a race condition.
As you know from the Autofac.Mvc.Owin README and the MVC/OWIN integration docs, ASP.NET MVC doesn't actually fully run in the OWIN pipeline. Trying to make the two work creates some weird issues that aren't of Autofac's creation. For example (again, from the Autofac docs):
Minor gotcha: MVC doesn’t run 100% in the OWIN pipeline. It still needs HttpContext.Current and some other non-OWIN things. At application startup, when MVC registers routes, it instantiates an IControllerFactory that ends up creating two request lifetime scopes. It only happens during app startup at route registration time, not once requests start getting handled, but it’s something to be aware of. This is an artifact of the two pipelines being mangled together. We looked into ways to try working around it but were unable to do so in a clean fashion.
But that also means there's a sort of order around when Application_BeginRequest handlers run that could cause oddness like what you're seeing. For example, the Autofac OWIN MVC integration tries to set up a request lifetime (if it's not already set up) when you put app.UseAutofacMvc() in the pipeline, but the ASP.NET MVC framework also tries to set up a request lifetime internally (using DependencyResolver.Current), so those things have to work together. It's not entirely impossible that your Application_BeginRequest handler is resolving from a different lifetime scope than what the MVC framework is trying to set up as the request lifetime, for example if the MVC framework hasn't had a chance to set it up before you've tried resolving from it.
I would recommend if you're trying to use OWIN with MVC, give in to OWIN and actually use OWIN middleware in the pipeline rather than event handlers in MVC. It'll remove the race condition for Application_BeginRequest and give you greater control over the order of operations. It'll also get you closer to where ASP.NET Core is so if/when it's time to migrate your application you won't have to deal with the event handlers that aren't there anymore.
Of course, again, this is totally a guess based on what I could gather from the question. Hopefully it helps.
Edit after new info was added to question: I think my guess is still correct, but it also looks like you're not setting up OWIN right for WebAPI, which could contribute to the problem. You shouldn't be using GlobalConfiguration. Also, again, MVC doesn't really run in OWIN, so you may see weirdness trying to get two app types (MVC, WebAPI) with two different pipelines to mash together. It's why they unified it in ASP.NET Core.

How to use a Service or DbContext inside DbCommandInterceptor?

I have an application that syncs data from a MySql database to a SQL Server database.
Considering those two DbContext services:
services.AddDbContext<SqlServerContext>(options => options
.UseSqlServer(Configuration.GetConnectionString("SqlServer")));
services.AddDbContext<MySqlContext>(options => options
.UseMySql(Configuration.GetConnectionString("MySql"))
.AddInterceptors(new MySqlInterceptor()));
In the MySqlInterceptor(); I want to inject/resolve/use a Service or even the SqlServerContext, in order to get configurations to modify the CommandText.
Any ideas?
Depending on the method you are going to override you will receive CommandEventData object in the method definition which has the DbContext as property.
As to the services and configurations you can configure the interceptor before registration.
Instead of this:
services.AddDbContext<MySqlContext>(options => options
.UseMySql(Configuration.GetConnectionString("MySql"))
.AddInterceptors(new MySqlInterceptor()));
you can do
var interceptor = new MySqlInterceptor(service1, service2 ... etc);
services.AddDbContext<MySqlContext>(options => options
.UseMySql(Configuration.GetConnectionString("MySql"))
.AddInterceptors(interceptor))
How to resolve the interceptor instance:
If you need to auto-wire the dependencies of the interceptor you can do the following
services.AddTransient<Service1>();
services.AddTransient<Service2>();
services.AddTransient<MySqlInterceptor>();
// resolve the instalce of the interceptor
var serviceProvider = services.BuildServiceProvider();
var interceptor = serviceProvider.GetService<MySqlInterceptor>();
// configure mysql context and interceptor
services.AddDbContext<MySqlContext>(options => options
.UseMySql(Configuration.GetConnectionString("MySql"))
.AddInterceptors(interceptor))
As #vasil mentioned in his answer:
Depending on the method you are going to override, you will receive
CommandEventData object in the method definition which has the
DbContext as property.
In my case though, I wanted to resolve a service that used another DbContext, which proved to be cumbersome; so instead I ended up putting the settings I needed into appsettings.json, and used IConfiguration service to get the setting value and sent it to the Interceptor constructor:
services.AddDbContext<MySqlContext>(options => options
.UseMySql(Configuration.GetConnectionString("MySql"))
.AddInterceptors(new MySqlInterceptor(Configuration["SettingValue"])));
Note: If you landed on this answer and was looking for a way to resolve a service inside the ConfigureService method, without calling BuildServiceProvider which, like David Fowler says on a Github issue, you'd be:
building the container while trying to build it
and you'll end up having:
2 containers and one of them will never be disposed.
You can do what Nkosi suggests in his answer:
services.AddScoped<IService>(x =>
new Service(x.GetRequiredService<IOtherService>(),
x.GetRequiredService<IAnotherOne>(),
""));

Automapper - some static class inside project?

I am writing some Web API application, where I have 4 basic layers - API, BusinessLogic(which I call BusinessServices), DAL (which using EF to speak with the database), and EntitiesData(where I have my entities).
API calls businessService, bs ask DAL, DAL using EF is asking database about my EntitiesData.
Ok, now what's the problem ;)
On the BusinessServices, I want to map entities to some DTO, which I can return to API.
I wanted to use AutoMapper, but on tutorials, there are really simple examples, which I understand.
The first question: Should I use 2 IoC containers? Or maybe move my IOC from API to the business services layer?
1st Container is on API level and it contains BusinessServices (like UsesrsService, MessageService, etc.)
The second container would be at BusinessServices level - I want to use it to store my AutoMapper maps.
And this is the second question - what should I do with AutoMapper.
I know, how to create the configuration, did sth like this:
private void Congifure()
{
if(!(configuration == null))
return;
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, UserDto>();
cfg.CreateMap<Message,MessageDto>();
});
}
but what should I do now? pack it to the IoC container?
From which place in the code I should call my class which is configuring mapper?
In businessServices I have only my business-logic classes and DTO's.
You can pack it into your Startup.cs ConfigureServices method:
var config = new MapperConfiguration(cfg =>
{
cfg.CreateMap<User, UserDto>();
cfg.CreateMap<Message,MessageDto>();
});
var mapper = config.CreateMapper();
services.AddScoped<AutoMapper.IMapper>(c => mapper);
And than inject it into your classes:
public class MyService
{
public MyService(IMapper mapper)
{
...
}
}
I would use one mapper, and put it somewhere vertically to your layer like into "helpers" project. Your mapper has to map between different layers so it should sit "between" them. Just move the logic of creation of MapperConfiguration into separate project and call it from your Startup.cs.
The very first thing in the automapper docs speaks about initialization. This should be done where ever you are bootstrapping your IOC container.
You only need 1 IOC container, making 2 would kind of make them useless as you would have broken the dependency tree into 2 halves.
And you should consider using mapping profiles for your different layers.

Registering instances from property of other registered types?

Is there a better way to code this dependency on Configuration, how can I register an object which is provided by a property of another registered object?
I have a configuration object which exposes a configuration property for each service. I'd like to initialize those services based on their configuration object interface.
void RegisterConfiguration(ContainerBuilder builder)
{
var config = new Configuration();
builder.RegisterInstance(config).As<IConfiguration>();
builder.RegisterInstance(config.Service1Configuration).As<IService1Configuration>();
builder.RegisterInstance(config.Service2Configuration).As<IService2Configuration>();
}
Note: I also thought to include ".ExternallyOwned()", but omitted it here because that wasn't part of the question.
Then for each ServiceX I have the following constructor:
public Service1(IService1Configuration config)
{
this.config = config;
}
I would like to be able to simply Register Configuration and have Autofac somehow instantiate Configuration for me.
I was thinking of a factory for the service registrations but I wasn't sure if I could resolve the Configuration object in a factory.
Any ideas how I can make this less explicit and use more of Autofac?
The first argument of the Register method is a IComponentContext. It means that you can use it to resolve some dependencies.
example :
void RegisterConfiguration(ContainerBuilder builder)
{
builder.RegisterType<Configuration>()
.As<IConfiguration>()
.SingleInstance();
builder.Register(c => c.Resolve<Iconfiguration>()
.Service1Configuration)
.As<IService1Configuration>();
builder.Register(c => c.Resolve<IConfiguration>()
.Service2Configuration)
.As<IService2Configuration>();
}

Multiple LUIS models from config

I use 2 LUIS models in 1 LUIS Dialog via attributes
[LuisModel("model1", "")]
[LuisModel("model2", "")]
[Serializable]
public class LuisDialog
I need to get these models from the config file.
In Autofac I can only register 1
builder.Register(c => new LuisModelAttribute("model1", ""))...
How can I set up multiple Luis models from a config?
I don't think that will work since the LuisModel is being injected to the LuisService you are registering (which probably is the next line in your configuration) and the LuisService is just expecting a single model and not an array of them.
The way I can think this could work is, instead of registering the model(s) to the Autofac container, you should register multiple LuisService defining the value of the model parameter of the constructor to each of your models (see this).
In that way, when you resolve your LuisDialog based dialog, it will inject multiple ILuisService (see this) as it's prepared to receive an array of services.
I haven't tried this, but you could see if something like this works:
var model1 = new LuisModelAttribute("model1", "");
var model2 = new LuisModelAttribute("model2", "");
builder.RegisterType<LuisService>()
.WithParameter("model", model1)
.Keyed<ILuisService>(FiberModule.Key_DoNotSerialize)
.AsImplementedInterfaces()
.SingleInstance(); or // .InstancePerLifetimeScope()
builder.RegisterType<LuisService>()
.WithParameter("model", model2)
.Keyed<ILuisService>(FiberModule.Key_DoNotSerialize)
.AsImplementedInterfaces()
.SingleInstance(); or // .InstancePerLifetimeScope()
Alternatively, you can use RegisterInstance and register the instances of the LuisService with their specific model.

Categories