Determine ASP.NET Core MVC Area during service registration - c#

I'm using ASP.NET Core
My IService is registered with the DI container
There are two implementations: FooService and BarService
I must choose a service based on current request's MVC Area
So I need something like:
services.AddScoped<IService>(
c => IsThisTheFooArea
? c.GetRequiredService<FooService>() as IService
: c.GetRequiredService<BarService>() as IService
);
I don't know how to implement the IsThisTheFooArea check.
How do I access the HttpContext or something similar, so I can inspect the current route?

Here is a way:
ConfigureServices.cs:
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.AddScoped<IService>(provider =>
{
var actionContextAccessor = provider.GetService<IActionContextAccessor>();
var descriptor = actionContextAccessor.ActionContext.ActionDescriptor as ControllerActionDescriptor;
var areaName = descriptor.ControllerTypeInfo.GetCustomAttribute<AreaAttribute>().RouteValue;
if(areaName == "FooArea")
{
return new FooService();
}
else
{
return new BarService();
}
});
Services:
public interface IService { string DoThisThing(); }
public class FooService : IService
{
public string DoThisThing()
{
return "Foo";
}
}
public class BarService : IService
{
public string DoThisThing()
{
return "Bar";
}
}
And controllers:
[Area("FooArea")]
public class FooController : Controller
{
private readonly IService _service;
public FooController(IService service)
{
_service = service;
}
public IActionResult Index()
{
return Content(_service.DoThisThing());
}
}
[Area("BarArea")]
public class BarController : Controller
{
private readonly IService _service;
public BarController(IService service)
{
_service = service;
}
public IActionResult Index()
{
return Content(_service.DoThisThing());
}
}

You need to either implement (or find an implementation based on) IControllerFactory or IDependencyResolver and set it at application startup in order to inject the controller dependencies.
ControllerBuilder.Current.SetControllerFactory(new MyControllerFactory(container));
// Or...
DependencyResolver.SetResolver(new MyDependencyResolver(container));
More info
https://www.asp.net/mvc/overview/older-versions/hands-on-labs/aspnet-mvc-4-dependency-injection

Related

Factory service and injection .net core 3.1

I have service factory in .NET Core 3.1. Each service implements IService and each service has arguments in the constructor, (there is no parameterless ctors)
What is the best way to inject service factory?
--Update with current solution--
public interface IServiceFactory
{
IService Create(string serviceType);
}
public class ServiceFactory : IServiceFactory
{
public IService[] services;
public ServiceFactory(IService[] services)
{
this.services = services;
}
public IService Create(string serviceType)
{
return services.First(s => s.ServiceName == serviceType);
}
}
public interface IService
{
string ServiceName { get; }
bool Send();
}
public class ServiceA : IService
{
private ISrv1 srv1:
private ISrv2 srv2;
public ServiceA(ISrv1 srv1, ISrv2 srv2)
{
this.srv1 = srv1;
this.srv2 = srv2;
}
public ServiceName => return "serviceA";
public bool Send()
{
var data = srv1.GetData();
var msg = new MessageData
{
Id = data.Id,
Email = data.MailAddress
};
return srv2.Send(msg);
}
}
//not full class just the idea for sending message when getting message data from
//different services
public class ServiceB : IService
{
public ServiceB()
{
}
public ServiceName => return "serviceB";
public bool Send() => false;
}
How the factory should create the correct service with all dependencies?
And how to register correctly the services and the factory in configue services?
For now the solution is
services.AddTransient<IServiceFactory>(c =>
new ServiceFactory(new INotificationService[]
{
new ServiceA(c.GetService<ISrv1>(),c.GetService<ISrv2>()),
new ServiceB();
}));
As a solution I used IServiceProvider in the FactoryService for getting the correct service by the givven type in runtime.
I registered the services as following
services.AddScoped<IServiceFactory, ServiceFactory>();
services.AddScoped<ServiceA>()
.AddScoped<IService, ServiceA>(c => c.GetService<ServiceA>());
services.AddScoped<ServiceB>()
.AddScoped<IService, ServiceB>(c => c.GetService<ServiceB>());

Why does Unity (DI) work in the Controller, but not in my service layer?

I've used quite a few different DI containers in the past, but never Unity (Specifically Unity 4.0.1).
I'm working with a plain old .NET MVC app with typical 3 tier architecture. Repository -> Domain -> WebUI.
I need to know what I am doing wrong so that I can get my registered dependencies to work on the domain layer. Here is what I have in my global.asax.
protected void Application_Start()
{
// ...
IUnityContainer container = new UnityContainer();
RegisterDependencies(container);
DependencyResolver.SetResolver(new WebApplicationDependencyResolver(container));
}
protected void RegisterDependencies(IUnityContainer container)
{
container.RegisterType<IUnitOfWork, UnitOfWork>();
}
Here is the WebApplicationDependencyResolver used above:
namespace WebApplication1.Infrastructure
{
public class WebApplicationDependencyResolver : IDependencyResolver
{
private IUnityContainer _container;
public WebApplicationDependencyResolver(IUnityContainer container)
{
_container = container;
}
public object GetService(Type serviceType)
{
try
{
return _container.Resolve(serviceType);
}
catch (Exception)
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
try
{
return _container.ResolveAll(serviceType);
}
catch (Exception)
{
return null;
}
}
}
}
My Domain Layer class CustomerService.cs (which I used both in it's own project and in a folder in the main project):
namespace WebApplication1.Services
{
public class CustomerService
{
private readonly IUnitOfWork _uow;
public CustomerService(IUnitOfWork uow)
{
_uow = uow;
}
}
}
Now, when I try to call the CustomerService class in my controller like so, it doesn't work:
public ActionResult Index()
{
var service = new CustomerService();
return View();
}
But If I use the resolver on the controller itself, it works:
public class HomeController : Controller
{
private IUnitOfWork _unitOfWork;
public HomeController(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
public ActionResult Index()
{
var service = new CustomerService(_unitOfWork);
return View();
}
}
Can anyone direct me in the right direction, to get DI working on the domain layer?
Try injecting the service in the controller instead of injecting IUnitOfWork. Then use the service instance in the controller methods:
public HomeController(CustomerService service)
{
_service = service
}
public ActionResult Index()
{
var model = _service.GetAllCustomers();
return View(model);
}
This should work, but it is not a good idea to have your class depend on another class. A dependency should be a contract (interface). You should refactor CustomerService to extract an interface ICustomerService and inject that one instead in the controller. Then you need to register it with the container in method RegisterDependencies:
container.RegisterType<ICustomerService, CustomerService>();

Web Api OWIN Host with Unity

I'm trying to implement UNITY on my WebApi2 application.
The problem is that I'm using an existing SqlConnection, depending on an identifier found in the URL of the resource.
So I need the identifier provided in the request uri, to create my context with.
Is it possible to get the {dbIdentifier} from the request URI, and parse it into the constructor of MyRepo?
The Request usi will look like: /api/{dbIdentifier}/{controller}/{id}
The structure looks like...
Request POST /api/myDbIdentifier/my/ + PAYLOAD data
Controller:
public class MyController : ApiController
{
private readonly IRepo _repo;
public MyController(IRepo repo)
{
_repo = repo;
}
}
Repo:
public class MyRepo : IRepo
{
private readonly MyContext _context;
public MyRepo(string dbIdentifier)
{
_context = new MyContext(GetConnection(dbIdentifier));
}
public void Insert(string s)
{
//Inserting string in context and save changes
}
private DbConnection(string id)
{
//psudo find connecion from pool, and return instance of DbConnection...
}
}
public interface IRepo
{
void Insert(string s);
}
Context:
public class MyContext : DbContext
{
public MyContext(DbConnection exitingConnection) : base(existingConnection, true)
{ }
}
Btw, it's my first time playing around with WebApi and Unity, so please bear with my ignorance.
UPDATED Unity part of my code...
UnityResolver (taken from https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/dependency-injection):
public class UnityResolver : IDependencyResolver
{
protected IUnityContainer Container;
public UnityResolver(IUnityContainer container)
{
if (container == null)
{
throw new ArgumentNullException(nameof(container), "Please provider an IUnityContainer.");
}
Container = container;
}
public void Dispose()
{
Container.Dispose();
}
public object GetService(Type serviceType)
{
try
{
return Container.Resolve(serviceType);
}
catch (ResolutionFailedException)
{
return null;
}
}
public IEnumerable<object> GetServices(Type serviceType)
{
try
{
return Container.ResolveAll(serviceType);
}
catch (Exception)
{
return new List<object>();
}
}
public IDependencyScope BeginScope()
{
return new UnityResolver(Container.CreateChildContainer());
}
}
Unity Register part in my startup:
public static void Register(HttpConfiguration config)
{
// Configuring DI Container fo IoC (Invert of Control)
var container = new UnityContainer();
container.RegisterType<IRepo, MyRepo>(new HierarchicalLifetimeManager());
config.DependencyResolver = new UnityResolver(container);
}
You can try the following:
1)Create a DelegatingHandler where you can access HtppRequestMessage.RequestUri
2)Extract dbIdentifier from Uri
3)Wrap dbIdentifier with a class (e.g. DbIdentifier) and register it in unity using HierarchicalLifetimeManager
4)Remember to register handler in owin:
httpConfiguration.MessageHandlers.Add(new DbIdentifierHandler());
EDIT.
You can look into this post to find some inspiration :
How to pass Owin context to a Repo being injected into Api controller

Implementing Container Per Request pattern

I'm going to add in the necessary infrastructure to bind each request to its own nested container, So in this case having a Container Per Request gives us a unique session (Context Per Request), In my code I'm using the new implementation of ObjectFactory:
public static class SmObjectFactory
{
private static readonly Lazy<Container> _containerBuilder =
new Lazy<Container>(defaultContainer, LazyThreadSafetyMode.ExecutionAndPublication);
public static IContainer Container
{
get { return _containerBuilder.Value; }
}
private static Container defaultContainer()
{
return new Container(ioc =>
{
ioc.For<IUnitOfWork>()
.HybridHttpOrThreadLocalScoped()
.Use<ApplicationDbContext>();
// config
});
}
}
So, for example If I just set a break point in the ApplicationDbContext's constructor, each time an instance is created. For example in my case I have these controllers:
public partial class HomeController : Controller
{
private readonly IUnitOfWork _uow;
public HomeController(IUnitOfWork uow)
{
_uow = uow;
}
public virtual ActionResult Index()
{
return View();
}
}
public class TestController : Controller
{
private readonly IUnitOfWork _uow;
public TestController(IUnitOfWork uow)
{
_uow = uow;
}
public ActionResult GetData()
{
return Content("Data");
}
}
So the view returned by Index action uses this code to pull in content from TestController:
#Html.Action("GetData", "Test")
In that example, several instances are created per request!
So I've changed SmObjectFactory this way:
public class NewObjectFactory
{
public static IContainer Container { get; set; }
static NewObjectFactory()
{
Container = new Container();
Container.Configure(ioc =>
{
ioc.For<IUnitOfWork>()
.HybridHttpOrThreadLocalScoped()
.Use<ApplicationDbContext>();
// config
}
}
Then in the Global.asax I've added these lines of code for using nested container:
public IContainer Container
{
get
{
return (IContainer)HttpContext.Current.Items["_Container"];
}
set
{
HttpContext.Current.Items["_Container"] = value;
}
}
public void Application_BeginRequest()
{
Container = NewObjectFactory.Container.GetNestedContainer();
}
public void Application_EndRequest()
{
Container.Dispose();
Container = null;
}
And inside Application_Start:
DependencyResolver.SetResolver(
new StructureMapDependencyResolver(() => Container ?? NewObjectFactory.Container));
And inside of the DependencyResolver I've implemented the factory function this way:
public class StructureMapDependencyResolver : IDependencyResolver
{
private readonly Func<IContainer> _factory;
public StructureMapDependencyResolver(Func<IContainer> factory)
{
_factory = factory;
}
public object GetService(Type serviceType)
{
if (serviceType == null)
{
return null;
}
var factory = _factory();
return serviceType.IsAbstract || serviceType.IsInterface
? factory.TryGetInstance(serviceType)
: factory.GetInstance(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return _factory().GetAllInstances(serviceType).Cast<object>();
}
}
Finaly when I run the application I get this error:
No default Instance is registered and cannot be automatically
determined for type
'Microsoft.Owin.Security.DataProtection.IDataProtectionProvider'
But when I comment this line in the Startup.cs file:
ConfigureAuth(app)
everything works and this time the ApplicationDbContext is created once and then disposed. So that's what I want: Only a single instance of the context is created now and it is correctly disposed of at the end of the web request, It means that the nested container reused the context to satisfy the dependencies of both controllers. :)
Any idea?

How can I resolve ILog using ServiceStack and Funq.Container

The ServiceStack AppHost provides a Funq.Container with which to register types that can be injected into Services as they are constructed. Can this container be used to register an ILog factory that returns an ILog appropriate for the type in which it will reside?
Put another way, given the following AppHost:
public class AppHost : AppHostBase
{
public AppHost() : base("Example Web Services", Assembly.GetExecutingAssembly())
{
}
public override void Configure(Funq.Container container)
{
var baseLogFactory = new ServiceStack.Logging.NLogger.NLogFactory();
LogManager.LogFactory = new ServiceStack.Logging.Elmah.ElmahLogFactory(baseLogFactory);
// Would prefer to register a Func<Type, ILog> one time here
}
}
And a Service:
public class FooService : IService<FooRequest>
{
static ILog Log { get { return LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); } }
// Would prefer:
// public ILog { get; set; }
public object Execute(FooRequest request)
{
Log.Info("Received request: " + request.Dump());
return new FooResponse();
}
}
Is there anything I can add to AppHost.Configure to avoid the static ILog boilerplate in all of my Services (and instead just use a plain old ILog property)?
Put a third way, most succinctly, Can I use Funq.Container for ILog injection instead of LogManager?
container.Register<ILog>(
ctx => LogManager.LogFactory.GetLogger(typeof(IService))
);
Now your service could look like this:
public class FooService : IService<FooRequest>
{
public ILog { get; set; }
public object Execute(FooRequest request)
{
Log.Info("Received request: " + request.Dump());
return new FooResponse();
}
}

Categories