Automapper and request specific resources - unity-container

I'm considering automapper for an asp mvc intranet app I am writing. My controllers are currently created using Unity dependency injection, where each container gets dependencies unique to the request.
I need to know if automapper can be made to use a request specific resource ICountryRepository to look up an object, like so....
domainObject.Country = CountryRepository.Load(viewModelObject.CountryCode);

Couple of options here. One is to do a custom resolver:
.ForMember(dest => dest.Country, opt => opt.ResolveUsing<CountryCodeResolver>())
Then your resolver would be (assuming CountryCode is a string. Could be a string, whatever):
public class CountryCodeResolver : ValueResolver<string, Country> {
private readonly ICountryRepository _repository;
public CountryCodeResolver(ICountryRepository repository) {
_repository = repository;
}
protected override Country ResolveCore(string source) {
return _repository.Load(source);
}
}
Finally, you'll need to hook in Unity to AutoMapper:
Mapper.Initialize(cfg => {
cfg.ConstructServicesUsing(type => myUnityContainer.Resolve(type));
// Other AutoMapper configuration here...
});
Where "myUnityContainer" is your configured Unity container. A custom resolver defines a mapping between one member and another. We often define a global type converter for all string -> Country mappings, so that I don't need to configure every single member. It looks like this:
Mapper.Initialize(cfg => {
cfg.ConstructServicesUsing(type => myUnityContainer.Resolve(type));
cfg.CreateMap<string, Country>().ConvertUsing<StringToCountryConverter>();
// Other AutoMapper configuration here...
});
Then the converter is:
public class StringToCountryConverter : TypeConverter<string, Country> {
private readonly ICountryRepository _repository;
public CountryCodeResolver(ICountryRepository repository) {
_repository = repository;
}
protected override Country ConvertCore(string source) {
return _repository.Load(source);
}
}
In a custom type converter, you wouldn't need to do any member-specific mapping. Any time AutoMapper sees a string -> Country conversion, it uses the above type converter.

Related

Error in CreateInstance() while dynamically creating object of concrete type in Factory Pattern

I am actually new to design patterns and trying to implement factory pattern with .NET Core.
I tried to see couple of posts related to factory pattern and trying to implement it, I have added the concrete types in the config and reading it as dictionary in my code -
My Factory Interface -
public interface IEmpFactory
{
public BaseEmployee CreateEmployeeType<EmpType>()
where EmpType : BaseEmployee, new();
}
Implementation -
public class EmpFactoryImpl : IEmpFactory
{
public BaseEmployee CreateEmployeeType<EmpType>()
where EmpType: BaseEmployee, new()
{
return new EmpType();
}
}
Below are my services which are using the Factory as dependency -
public interface IEmpService
{
public string GetEmployeeBonus();
}
public class ContractEmpService : IEmpService
{
IEmpFactory _empFactory;
public ContractEmpService(IEmpFactory empFactory) =>
_empFactory = empFactory;
private BaseEmployee CreateMyEmployee() =>
_empFactory.CreateEmployeeType<ContractEmp>();
public string GetEmployeeBonus() =>
return CreateMyEmployee().GetBonus();
}
public class PermEmpService : IEmpService
{
private readonly IEmpFactory _empFactory;
public PermEmpService(IEmpFactory empFactory) =>
_empFactory = empFactory;
private BaseEmployee CreateMyEmployee() =>
_empFactory.CreateEmployeeType<PermEmp>();
public string GetEmployeeBonus() =>
CreateMyEmployee().GetBonus();
}
Added these concrete types in the config -
"ConfigurationProps": {
"EmpServices": {
"PermEmp": "SimpleFactoryWithoutSwitchCase.Service.PermEmpService",
"ContractEmp": "SimpleFactoryWithoutSwitchCase.Service.ContractEmpService"
}
}
Created the class to create a instance of the concrete type based on the type i.e, PermEmp or ContractEmp dynamically -
public class EmployeeTypeRouter : IEmployeeTypeRouter
{
private readonly ConfigurationProps _props;
public EmployeeTypeRouter(ConfigurationProps props)
{
_props = props;
}
public IEmpService GetInstance(string key)
{
string className = _props.EmpServices
.Where(k => k.Key.Equals(key)).FirstOrDefault().Value;
Type t = Type.GetType(className);
return (IEmpService)Activator.CreateInstance(t);
}
}
This is my calling method -
[HttpGet(Name = "GetEmployeeBonus")]
public string Get()
{
string type = "PermEmp";
IEmpService empService = _empRouter.GetInstance(type);
return empService.GetEmployeeBonus();
}
based on the type passed here i want to fetch the concrete type and call the method.
I am getting the error like this on CreateInstance method -
System.MissingMethodException: `Cannot dynamically create an instance of type 'SimpleFactoryWithoutSwitchCase.Service.PermEmpService'. Reason: No parameterless constructor defined.'
Which is very clear, but I don't want to create a parameterless constructor.
Since I am registering the dependencies in .NET Core, do I need to pass it again here? (which does not make sense for me)
Any help is really appreciated or if you feel I am doing something wrong please let me know.
Your EmployeeTypeRouter class tries to replicate the creation process that your DI Container can do more eloquently. So instead of calling Activator.CreateInstance, forward the resolution to the DI Container.
This means the following things:
Register all known IEmpService at startup.
Resolve the expected type from the IServiceProvider from inside the EmployeeTypeRouter.
In other words, change the startup code to the following:
var dictionary = props.EmpServices
.ToDictionary(p => p.Key, p => Type.GetType(p.Value));
foreach (string pair in dictionary)
{
services.AddTransient(pair.Value);
}
services.AddTransient<IEmployeeTypeRouter, EmployeeTypeRouter>();
services.AddTransient<Func<string, IEmpService>>(sp =>
key => (IEmpService)sp.GetRequiredService(dictionary[key]));
And change EmployeeTypeRouter to the following:
public class EmployeeTypeRouter : IEmployeeTypeRouter
{
private readonly Func<string, IEmpService> _factory;
public EmployeeTypeRouter(Func<string, IEmpService> factory)
{
_factory = factory;
}
public IEmpService GetInstance(string key) =>
_factory.Invoke(key);
}
In the previous code snippet, EmployeeTypeRouter makes use of the Func<string, IEmpService> delegate, which functions as factory. Under the covers the delegate calls back into the IServiceProvider.
There are of course several ways to skin a cat. You could also move some of the startup logic into EmployeeTypeRouter, or even remove the IEmployeeTypeRouter altogether and let application code depend directly on Func<string, IEmpService> delegate.

How to distinguish between multiple databases with the same structure and same context in the URL for an ASP.Net Core Web API

I'm trying to make a web api in asp.net core which can target multiple databases on the same server with the exact same structure. As such they should be able to share the same context and controllers. Making separate contexts and controllers for each database would quickly get out of hand and make it a pain to maintain. As such I should be able to do this in Startup.cs.
services.AddDbContext<DatabaseContext>(opt => opt.UseMySQL(Configuration.GetConnectionString("db1")));
services.AddDbContext<DatabaseContext>(opt => opt.UseMySQL(Configuration.GetConnectionString("db2")));
services.AddDbContext<DatabaseContext>(opt => opt.UseMySQL(Configuration.GetConnectionString("db3")));
The problem I'm running into is how I can distinguish which database is being queried. Ideally I would like to have it such that the url can be ...{database}[controller]... where the database parameter can be used to determine which database is being queried. I've tried looking into the multi-tenant approach however I can't work out how to adapt it to my specific use case.
Any help would be greatly appreciated.
Take a look at this:
https://entityframeworkcore.com/knowledge-base/58123230/connect-multiple-databases-to--net-core-project-via-entity-framework-core
You can do it with multiple DB Contexts from inheriting the principal DB Context where you define the DbSets.
Create base context and including all settings into this, DBSET:
public abstract class BaseContext : DbContext
{
public BaseContext(DbContext options)
: base(options)
{ }
public DbSet<object> FirstDbSet { get; set; }
...
}
inherit from BaseContext for both DBs(Databases):
public class NavaContext : BaseContext
{
public NavaContext (DbContext<NavaContext> options) : base(options)
{
}
}
public class StackContext : BaseContext
{
public StackContext(DbContext<StackContext> options) : base(options)
{
}
}
and register both in Startup.cs:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddDbContext<NavaContext>(options => options.UseSqlServer(Configuration.GetConnectionString("LATAMConnectionString")));
services.AddDbContext<StackContext>(options => options.UseSqlServer(Configuration.GetConnectionString("EUConnectionString")));
// Autofac
var builder = new ContainerBuilder();
// needed only if you plan to inject ICollection<BaseContext>
builder.RegisterType<NavaContext>().As<BaseContext>();
builder.RegisterType<StackContext>().As<BaseContext>();
builder.Populate(services);
return new AutofacServiceProvider(builder.Build());
}
add connection strings in appsettings.json:
"ConnectionStrings": {
"NavaConnectionString": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true",
"StackConnectionString": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;Trusted_Connection=True;MultipleActiveResultSets=true"
}
and now you can inject both contexts:
public class ReportRepository : IReportRepository
{
private readonly NavaContext latamDbContext;
private readonly StackContext euDbContext;
public ReportRepository(NavaContext latamDbContext, StackContext euDbContext)
{
this.latamDbContext = latamDbContext;
this.euDbContext = euDbContext;
}
}
or if you plan to inject collection of contexts:
public class ReportRepository : IReportRepository
{
private readonly ICollection<BaseContext> dbContexts;
public ReportRepository(ICollection<BaseContext> dbContexts)
{
this.dbContexts = dbContexts;
}
}
to access specific context:
var _stackContext= dbContexts.FirstOrDefault(x => x is StackContext) as StackContext;
var _navaContext= dbContexts.FirstOrDefault(x => x is NavaContext) as NavaContext;
The best way to create connection when there are multiple databases in dot net core is with IDesignTimeDbContextFactory
public class BloggingContextFactory : IDesignTimeDbContextFactory<BloggingContext>
{
public BloggingContext CreateDbContext(string[] args)
{
// logic to determine which connection string to use
var optionsBuilder = new DbContextOptionsBuilder<BloggingContext>();
optionsBuilder.UseSqlite("connectionString");
return new BloggingContext(optionsBuilder.Options);
}
}
Inject BloggingContextFactory using register services and pass args[] for logic to use connection string

Dependency injection for non controller classes and calling them

Trying to wrap my head around dependency injection, coming from the world of static classes and instantiated classes. Here is what I currently have:
[SomeFilter]
public class AController : Controller
{
private readonly IOptions<AppSettings> _appSettings;
public AController(IOptions<AppSettings> appSettings)
{
_appSettings = appSettings;
}
// GET: /characters/
public IActionResult Index()
{
//do something
}
SomeFilter gets called immediately, and does this:
public class SomeFilter: ActionFilterAttribute, IActionFilter
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
OtherClass.RunMe();
}
}
OtherClass looks like this:
public class OtherClass
{
private readonly IOptions<AppSettings> _appSettings;
public OtherClass(IOptions<AppSettings> appSettings)
{
_appSettings = appSettings;
}
public RunMe()
{
//do something
}
I also have OtherClass registered as a service.Singleton in the Startup.cs.
I get an error stating:
"An object reference is required for a non-static field"
for the OtherClass.RunMe(); call.
I was under the assumption that I can call this class from anywhere within my code instead of having to create a new instance of it? Essentially, how do I call methods from other classes using dependency injection?
You can't constructor injection on filters. It's all about run time order. When you try injection on constructor, your IoC container not reachable at the moment. You should be use property/setter injection.
I prefer using structuremap container for to do this. Because structuremap has very easy to apply any type injection. For example when you have a filter registry like this
public class ActionFilterRegistry : Registry
{
public ActionFilterRegistry(Func<IContainer> containerFactory)
{
For<IFilterProvider>().Use(
new StructureMapFilterProvider(containerFactory));
Policies.SetAllProperties(x =>
x.Matching(p =>
p.DeclaringType.CanBeCastTo(typeof(ActionFilterAttribute)) &&
p.DeclaringType.Namespace.StartsWith("YourNameSpace") &&
!p.PropertyType.IsPrimitive &&
p.PropertyType != typeof(string)));
}
}
And you register it on your global.asax(prefer) or one of your startup class.
Example app_Start method.
DependencyResolver.SetResolver(new StructureMapDependencyResolver(() => Container ?? ObjectFactory.Container));
ObjectFactory.Container.Configure(cfg =>
{
cfg.AddRegistry(new StandardRegistry());
cfg.AddRegistry(new ControllerRegistry());
cfg.AddRegistry(new ActionFilterRegistry(() => Container));
cfg.AddRegistry(new MvcRegistry());
});
Then you can use any filter with injection. But pay attention you shouldn't be use contructor injection like you do.
I found a way to inject into the filter by using
[ServiceFilter(typeof(MyFilter))]
instead of just
[MyFilter]
and within the filter's
(ActionExecutingContext context)
{
var controller = context.Controller as Controller.
controller.whateverIneed
}
This now provides me with what I need within the filter. What I also realized is that I can't remove the need for creating references to other classes using new, as I was under the impression Core's dependency was all about "no more new". This gets into fundamentals with Core which I'm still grasping with.
What I ended up doing was creating new classes that do some work, but they are setup as services and registered in the startup.cs. I'm still grappling on how to intermingle registered services (which I can inject) and new instances of worker classes (which usually hold static information), and passing information between them all.

Autofac lazy property injection

I'm trying to inject business logic implementations into web API base controller. Somehow property in base controller is always null.
Also how can I do lazy injection?
Startups.cs
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
var containerBuilder = new ContainerBuilder();
containerBuilder.RegisterType<ViewBusinessLogic>().As<IViewBusinessLogic>().
PropertiesAutowired();
containerBuilder.Populate(services);
var container = containerBuilder.Build();
return container.Resolve<IServiceProvider>();
}
Interface, implementation and base controller:
public interface IViewBusinessLogic
{
IEnumerable<dynamic> GetView(Guid viewId);
}
public class ViewBusinessLogic : BusinessLogic, IViewBusinessLogic
{
public IEnumerable<dynamic> GetView(Guid viewId)
{
return new List<dynamic>
{
new { Test = "Test1" },
new { Test = "Test2" }
};
}
}
public abstract class BaseController : Controller
{
public IViewBusinessLogic ViewBusinessLogic { get; }
}
Controllers aren't resolved by the DI framework by default. You need to add AddControllerAsServices to have them be resolved by the DI of your choice.
From this GitHub issue:
Hi,
Maybe I'm wrong but as I tested deeply (and checked Mvc source code), Controllers are not resolved from IServiceProvider, but only constructor arguments of them are resolved from IServiceProvider.
Is that by design? I'm very suprised. Because, I'm using a different DI framework which supports property injection. And I can not use property injection since Controller instances are not requested from IServiceProvider.
Have you added AddControllersAsServices in your Startup (https://github.com/aspnet/Mvc/blob/ab76f743f4ee537939b69bdb9f79bfca35398545/test/WebSites/ControllersFromServicesWebSite/Startup.cs#L37)
The example above quoted for future reference.
public void ConfigureServices(IServiceCollection services)
{
var builder = services
.AddMvc()
.ConfigureApplicationPartManager(manager => manager.ApplicationParts.Clear())
.AddApplicationPart(typeof(TimeScheduleController).GetTypeInfo().Assembly)
.ConfigureApplicationPartManager(manager =>
{
manager.ApplicationParts.Add(new TypesPart(
typeof(AnotherController),
typeof(ComponentFromServicesViewComponent),
typeof(InServicesTagHelper)));
manager.FeatureProviders.Add(new AssemblyMetadataReferenceFeatureProvider());
})
// This here is important
.AddControllersAsServices()
.AddViewComponentsAsServices()
.AddTagHelpersAsServices();
services.AddTransient<QueryValueService>();
services.AddTransient<ValueService>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}
As for the second part of your question: I don't think it's possible to have lazy instantiation via IoC container at all. Best fit for you is to create a factory class and inject the factory rather than the concrete service.
But usually you don't need lazy instantiation anyways, the instantiation of services should be fast. If it's not, you probably doing some funky stuff in the constructor (connecting somewhere, or doing other long running operations), which is an anti-pattern.

strucutreMap Dependency injection is not working

In my application i configured structuremap like
public class DefaultRegistry : Registry {
#region Constructors and Destructors
public DefaultRegistry() {
Scan(
scan => {
scan.Assembly("Eterp.Data.ErpCore");
scan.Assembly("Eterp.Data.Seed");
scan.Assembly("Eterp.Application.ErpCore");
scan.TheCallingAssembly();
scan.WithDefaultConventions();
});
ForConcreteType<AclAuthorizationManager>().Configure.Ctor<IResourceOperationAppService>()
}
#endregion
}
And i have class
public class AclAuthorizationManager : ClaimsAuthorizationManager
{
private readonly IResourceOperationAppService _resourceOperationAppService;
public AclAuthorizationManager(IResourceOperationAppService resourceOperationAppService)
{
_resourceOperationAppService = resourceOperationAppService;
}
public override bool CheckAccess(AuthorizationContext context)
{
var isCurrentUserAuthorized = context.Principal.Identity.IsAuthenticated;
return isCurrentUserAuthorized && _resourceOperationAppService.CanAccessResource(context.Action.FirstOrDefault().Value, context.Principal.Claims);
}
}
This class is custom claim authorization class using in my application, but when i exceuting the application,i am getting an error which related to lack of parameter required by the constructor, ( This class has constructor with parameter type IResourceOperation). but i already configured all the details in structureMap . i am sure that my structuremap configuration is working 100% well expect the creation of this AclAuthorizationManager class.because i am able to to apply DI in other classes.
What is wrong part in my code?
in my experience when you specify the type constructor must say that inherits from the interface.
Therefore, you should replace this line:
ForConcreteType<AclAuthorizationManager>().Configure.Ctor<IResourceOperationAppService>()
By:
ForConcreteType<AclAuthorizationManager>().Configure.Ctor<IResourceOperationAppService>().Is<ResourceOperationAppService>()
Where is the implementation ResourceOperationAppService IResourceOperationAppService.

Resources