Multiple LUIS models from config - c#

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.

Related

How to instantiate a Service with parameters in MVVM Light ViewModelLocator?

I have been searching for about 3 hours.
I need to know how do I instantiate a Service with parameters that are produced by another service in MVVM Light's ViewModelLocator.
In my ViewModelLocator class I register my View Models and then register services that they use, like this:
SimpleIoc.Default.Register<INetworkClient>(() => new NetworkClient());
SimpleIoc.Default.Register<IApiClientAuthentication>(() => new ApiClientAuthentication());
SimpleIoc.Default.Register<IApiClient>(() =>
{
INetworkClient networkClient = ServiceLocator.Current.GetInstance<INetworkClient>();
return new ApiClient(networkClient, new Uri("https://someuri.com/"),
authResponse.AdminUserId, authResponse.ApiKey);
});
The problem is that the "authResponse" that I pass to the ApiClient above is a result produced by calling a method on implementation of IApiClientAuthentication (which I also register with IoC container above). That response is not available when I create the ApiClient above. How should I go about it.
Please, at least push me in the right direction.

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.

How to Inject AutoMapper as an instance using FreshMvvm and FreshIOC

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());

How to make AutoFac use same instance of nested dependency per top-level object? (SignalR dependency injection per hub)

I am trying to set up my AutoFac registration in such a way that this test passes:
[Test]
public void Autofac_registration_test()
{
// Given
var builder = new ContainerBuilder();
RegisterServices(builder);
var container = builder.Build();
// When
var firstHub = container.Resolve<Hub>();
var secondHub = container.Resolve<Hub>();
// Then
firstHub.Should().NotBe(secondHub);
firstHub.FooRepo.Context.Should().Be(firstHub.BarRepo.Context);
firstHub.FooRepo.Context.Should().NotBe(secondHub.FooRepo.Context);
}
i.e. I want to use the same Context object all the way down within a single Hub, but use a different one when a new Hub is created.
RegisterServices is currently just:
private void RegisterServices(ContainerBuilder builder)
{
builder.RegisterType<MyHub>();
builder.RegisterType<FooRepo>();
builder.RegisterType<BarRepo>();
builder.RegisterType<Context>(); // How should I scope this?
}
Which fails at firstHub.FooRepo.Context.Should().Be(firstHub.BarRepo.Context); because Context is transiently scoped.
But scoping context per lifetime also fails, this time at firstHub.FooRepo.Context.Should().NotBe(secondHub.FooRepo.Context);.
It feels like this is a reasonable thing to want to do, so am I missing anything obvious out-of-the-box here?
Or will I have to do something manual to track Hub creation?
(For context, this is for a SignalR app. Hubs are created per SignalR request, so this was an attempt to match the unit-of-work lifetime of an HTTP request in normal webby situations).
What #Steven said in his comment was correct, I needed a per-object-graph lifestyle.
Castle.Windsor supports this, so I swicthed to using that for my dependency injection instead of AutoFac. The registration now looks like:
container.Register(Component.For<Hub>().LifestyleTransient());
container.Register(Component.For<FooRepo>().LifestyleTransient());
container.Register(Component.For<BarRepo>().LifestyleTransient());
container.Register(Component.For<Context>().LifestyleBoundTo<Hub>()); // Important bit
For more information, see: http://docs.castleproject.org/Windsor.LifeStyles.ashx?HL=scope#Bound_8

Categories