c# MVC dependency injection based on route param - c#

What I'm trying to do is to instantiate a service using NInject and a route param.
I have something like this:
DAL
public interface IDALContract
{
object GetById(int id);
}
public class DALPeopleContract : IDALContract
{
public object GetById(int id)
{
//get person
return null;
}
}
public class DALAnimalsContract : IDALContract
{
public object GetById(int id)
{
//get animal
return null;
}
}
public static class DALContractFactory
{
public static IDALContract GetContract(int discriminator)
{
switch(discriminator)
{
case 1: return new DALPeopleContract();
case 2: return new DALAnimalsContract();
default: throw new NotSupportedException();
}
}
}
Business Layer
public interface IMyService
{
object GetById(int id);
}
public class MyService : IMyService
{
private IDALContract _contract;
public MyService(int discriminator)
{
_contract = DALContractFactory.GetContract(discriminator)
}
public object GetById(int id)
{
return _contract.GetById(id);
}
}
Controller
public class MyController
{
private IMyService _myService;
public MyController()
{
//how do I get the discriminator here? (the discriminator should be a route param)
IParameter param = new Parameter("MyParam", discriminator, true);
_myService = NInjectKernel.TryGet<IMyService>(param);
}
ActionResult Index(id)
{
_myService.GetById(id);
return View(model);
}
}
So the issue that I'm having is how to get the param, or is there a better approach to this. Basicaly what I'm trying to do is to have one controller that handles the same actions for different models, but I'm having an issue with the Data Access Layer (DAL).
Would it be a good idea to get the value on OnActionExecuting and instantiate there the service?

I found a clean (in my opinion) solution to my issue.
So now I have a fairly easy way to deal with simple operation on related classes.
I replaced the DALContractFactory with some NInject binding. And I have a custom route defined for my controller that requires a "type" that is read in OnActionExecuting on my controller.
For the models, I have a factory defined and a custom mapper (I haven't posted them here because they were not relevant to the question). If anybody is interested, I can post a sample sln with this approach.
So now I have something like:
DAL
public interface IDALContract
{
object GetById(int id);
}
public class DALPeopleContract : IDALContract
{
public object GetById(int id)
{
//get person
return null;
}
}
public class DALAnimalsContract : IDALContract
{
public object GetById(int id)
{
//get animal
return null;
}
}
Business Layer
public enum Discriminator
{
Animal,
Person
}
public interface IMyService
{
object GetById(int id);
}
public class MyService : IMyService
{
private IDALContract _contract;
public MyService(IDALContract contract)
{
_contract = contract;
}
public object GetById(int id)
{
return _contract.GetById(id);
}
}
Controller
public class MyController : Controller
{
private IMyService _myService;
protected override void OnActionExecuting(ActionExecutingContext ctx)
{
base.OnActionExecuting(ctx);
int type;
var routeValue = ControllerContext.RouteData.Values["type"];
Discriminator type;
if(!Enum.TryParse<Discriminator>(routeValue.ToString(), out type))
{
//set a default value
type = Discriminator.Animal;
}
_myService = NInjectKernel.Instance.GetService<IMyService>("type", type);
}
public ActionResult Index(id)
{
_myService.GetById(id);
return View(model);
}
}
NInjectConfiguration
public class NInjectKernel
{
private readonly IKernel _kernel;
private NInjectKernel()
{
_kernel = new StandardKernel();
}
private static volatile Irr2NInjectKernel _instance;
private static readonly object SyncRoot = new object();
public static Irr2NInjectKernel Instance
{
get
{
if (_instance == null)
{
lock (SyncRoot)
{
if (_instance == null)
{
var temp = new Irr2NInjectKernel();
temp.BindAllDependencies();
_instance = temp;
}
}
}
return _instance;
}
}
private void BindAllDependencies()
{
_kernel.Bind<IMyService>().To<MyService>();
_kernel.Bind<IDALContract>().ToMethod(x =>
{
IParameter parameter = x.Parameters.SingleOrDefault(p => p.Name == "type");
if (parameter != null)
{
var recordType = (Discriminator)parameter.GetValue(x, x.Request.Target);
switch (recordType)
{
case RecordType.Animal:
return new DALAnimalsContract();
case RecordType.Person:
return new DALPeopleContract();
default:
throw new NotSupportedException("DQS type is not suppported.");
}
}
throw new NotSupportedException();
});
}
}

Related

C# Dynamic generic instance

This is my first question here and i'm not very familliar with the C# terminology, so if i get some terms or definitions mixed up i appologize in advance.
I have set up a generic EF data access layer;
public class BaseService<TObject> where TObject : class
{
private DbContext Context;
private static readonly Lazy<BaseService<TObject>> lazy = new Lazy<BaseService<TObject>>(() => new BaseService<TObject>());
public static BaseService<TObject> Instance => lazy.Value;
public BaseService()
{
Context = new evEntities();
}
public BaseService(DbContext context)
{
Context = context;
}
public ICollection<TObject> GetAll()
{
return Context.Set<TObject>().ToList();
}
public async Task<ICollection<TObject>> GetAllAsync()
{
return await Context.Set<TObject>().ToListAsync();
}
public TObject Get(int id)
{
return Context.Set<TObject>().Find(id);
}
}
Together with this;
public static class DA
{
public static DataAccess.Categories Categories => new DataAccess.Categories();
public static DataAccess.Tags Tags => new DataAccess.Tags();
public static DataAccess.Users Users => new DataAccess.Users();
}
public static class DA<T> where T : class
{
public static BaseService<T> Base => new BaseService<T>();
}
So in my Business Layer i can do this;
public class Categories
{
public Categories() { }
public ICollection<Database.Categories> GetAll()
{
return DA.Categories.GetAll().ToList();
}
public async Task<ICollection<Database.Categories>> GetAllAsync()
{
return await DA.Categories.GetAllAsync();
}
public Database.Categories Get(int id)
{
return DA.Categories.Get(id);
}
}
For clarity. My EF creates classes/entities like 'Database.Categories' and 'Database.Users' which i pass as 'TObject' to my BaseService to get a standard way of pulling data from my database for all my entities.
Now my question. In a similar way i want to create a generic Business Layer. Like;
public class BusinessLogicBase<TModel>
{
public ICollection<TDBModel> GetAll()
{
return null;
}
public async Task<ICollection<TDBModel>> GetAllAsync()
{
return await DA.Categories.GetAllAsync();
}
public TDBModel Get(int id)
{
return DA.Categories.Get(id);
}
}
I want to be able to call the DA with a TObject like Database.Categories but this has to be dynamic, based on the type passed to the BusinessLogicBase. So i want to do something like this (which doesn't work);
private ???? DetermineDatabaseModel()
{
switch(typeof(TModell))
{
case Models.Categories:
return Database.Categories;
case Models.Users:
return Database.Users;
}
}
So i can do this;
public ICollection<TDBModel> GetAll()
{
var databaseModel = DetermineDatabaseModel()
return DA<databaseModel>().GetAll();
}
I hope you understand my question and can help me.
Thnx!
Sorry for the long post, and for all you 9gaggers, here's a potato... No just kidding, this is serious.
Have you tried something like:
public class BusinessLogicBase<TDBModel> where TDBModel : class {
public ICollection<TDBModel> GetAll() {
return DA<TDBModel>.Base.GetAll();
}
}
UPDATE 1:
Maybe it would help You if you try to write it without generics first and then convert it into more general pattern using generics. There are some missteps that are easier to solve without generics.
Method public ICollection<TDBModel> GetAll() can't return ICollection<TDBModel> because type parameter TDBModel is not defined either in class signature or method signature. I makes more sense if you define it like this:
public class BusinessLogicBase<TModel>
{
public ICollection<TModel> GetAll()
{
return null;
}
}
UPDATE 2:
try this simple console app to demonstrate dynamic keyword and watch what is stored in variables categories and users.
UPDATE 3:
Based on fiddle - I've changed IBaseService<dynamic> to dynamic:
public class BL<TModel>
{
// This is what i want to make dynamic.
// how do i create a return type that i can use in DA<>..
private Type testDetermineDatabaseModel()
{
switch(typeof(TModel).Name){
case "Categories":
return typeof(Database.Categories);
case "Users":
return typeof(Database.Users);
}
return null;
}
public ICollection<TModel> testGetAll()
{
var databaseModel = testDetermineDatabaseModel();
// return DA<databaseModel>().Base.GetAll();
return new List<TModel>();
}
// NEW
// I have constructed the following.
private dynamic baseService;
public dynamic DetermineDatabaseModel()
{
switch (typeof(TModel).Name)
{
case "Categories":
return new BaseService<Database.Categories>();
case "Users":
return new BaseService<Database.Users>();
default:
return null;
}
}
private IBaseService<TDbModel> GetBase<TDbModel>() where TDbModel : class
{
return new BaseService<TDbModel>();
}
public ICollection<TModel> GetAll()
{
ICollection<TModel> returnValue = new List<TModel>();
// This works!!!
foreach (var item in GetBase<Database.Categories>().GetAll())
{
returnValue.Add((TModel)(object)item);
}
baseService = DetermineDatabaseModel();
// This doesn't!!! It's the same thing!! :(
foreach (var item in baseService.GetAll())
{
returnValue.Add((TModel)(object)item);
}
return returnValue;
}
}
But remember, this not solve the issue You are facing. That is map 2 generic types.

DbContextScope with Generic Repository

I am using the DbContextScope described here
In his example of how to get a hold of a dbcontext outside of the class its instantiated, Mehdi writes:
public class UserRepository : IUserRepository {
private readonly IAmbientDbContextLocator _contextLocator;
public UserRepository(IAmbientDbContextLocator contextLocator)
{
if (contextLocator == null) throw new ArgumentNullException("contextLocator");
_contextLocator = contextLocator;
}
public User Get(Guid id)
{
return _contextLocator.Get<MyDbContext>.Set<User>().Find(id);
}
}
But, if im going for a generic repository, say
public abstract class RepositoryBase<T> : IRepository<T> where T : class, IDomainEntity
{
private readonly DbSet<T> set;
private IAmbientDbContextLocator contextLocator;
protected RepositoryBase(IAmbientDbContextLocator ctxLocator)
{
if (ctxLocator == null) throw new ArgumentNullException(nameof(ctxLocator));
contextLocator = ctxLocator;
}
public T Get(Guid id)
{
//return _contextLocator.Get<MyDbContext>.Set<T>().Find(userId);
}
}
then how is the dbset supposed to be resolved? how do i work with "MyDbContext" in the Get method?
i do have multiple contexts.
public abstract class RepositoryBase<T, TDbContext> : IRepository<T> where T : IDomainEntity where TDbContext : DbContext
{
private readonly DbSet<T> _dbset;
private readonly IAmbientDbContextLocator _contextLocator;
protected RepositoryBase(IAmbientDbContextLocator ctxLocator)
{
if (ctxLocator == null) throw new ArgumentNullException(nameof(ctxLocator));
_contextLocator = ctxLocator;
_dbset = _contextLocator.Get<TDbContext>.Set<T>();
}
protected DbSet<T> DbSet { get { return _dbset; } }
public T Get(Guid id)
{
return DbSet.Find(id);
}
}
If you don't want TDbContext, You can send DbContext on constructor beside of contextlocator. But he forces you to use DbContextScope, I didn't read all article but let's not break his logic.

Repository cannot be used as a type parameter in the generic type of method

I recently came across a problem which I haven't met before about dependencies and looked it up and found Ninject. I have followed a guide on how to use it and have reached a point where I receive and error which I do not understand. I very generally wrote down the error in the title but the full error is as follows:
'Error 1 The type 'MyDBFirstAP.Repository.SQLAPRepository' cannot be
used as type parameter 'TImplementation' in the generic type or method
'Ninject.Syntax.IBindingToSyntax.To()'. There is
no implicit reference conversion from
'MyDBFirstAP.Repository.SQLAPRepository' to
'MyDBFirstAP.Repository.IAPRepository'.'
It occurs here:
public class NinjectControllerFactory : DefaultControllerFactory{
private IKernel ninjectKernel;
public NinjectControllerFactory() {
ninjectKernel = new StandardKernel();
AddBindings();
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType) {
return controllerType == null
? null
: (IController)ninjectKernel.Get(controllerType);
}
private void AddBindings()
{
ninjectKernel.Bind<IAPRepository>().To<SQLAPRepository>(); // On this line
}
}
The beginning of my controller is as:
public class ClientsController : Controller
{
IAPRepository repository;
// GET: Clients
public ClientsController(IAPRepository repository) {
this.repository = repository;
}
Here is the requested SQLRepository code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using MyDBFirstAP.Models;
using MyDBFirstAP.DI;
namespace MyDBFirstAP.Repository {
public class SQLAPRepository {
ApplicationDbContext Database = new ApplicationDbContext();
#region Client
public IQueryable<Client> GetAllClients() {
return Database.Clients;
}
public Client GetClientByID(int id) {
return Database.Clients.FirstOrDefault(c => c.ClientID == id);
}
public IQueryable<Client> GetClientByName(string ClientName) {
return (from clients in Database.Clients
where clients.ClientName.Contains(ClientName)
select clients);
}
public void AddClient(Client client) {
Database.Clients.Add(client);
Database.SaveChanges();
}
public void UpdateClient(Client client) {
var tmpClient = Database.Clients.FirstOrDefault(c => c.ClientID == client.ClientID);
tmpClient.ClientName = client.ClientName;
tmpClient.ClientAddress = client.ClientAddress;
Database.SaveChanges();
}
public void DeleteClient(Client client) {
Database.Clients.Remove(client);
Database.SaveChanges();
}
#endregion
#region Supplier
public IQueryable<Supplier> GetAllSuppliers() {
return Database.Suppliers;
}
public Supplier GetSupplierByID(int id) {
return Database.Suppliers.FirstOrDefault(s => s.SupplierID == id);
}
public IQueryable<Supplier> GetSupplierByName(string SupplierName) {
return(from suppliers in Database.Suppliers
where suppliers.SupplierName.Contains(SupplierName)
select suppliers);
}
public void AddSupplier(Supplier supplier) {
Database.Suppliers.Add(supplier);
Database.SaveChanges();
}
public void UpdateSupplier(Supplier supplier) {
var tmpSupplier = Database.Suppliers.FirstOrDefault(s => s.SupplierID == supplier.SupplierID);
tmpSupplier.SupplierName = supplier.SupplierName;
tmpSupplier.SupplierAddress = supplier.SupplierAddress;
Database.SaveChanges();
}
public void DelteSupplier(Supplier supplier) {
Database.Suppliers.Remove(supplier);
Database.SaveChanges();
}
#endregion
#region Claim
public IQueryable<Claim> GetAllClaims() {
return Database.Claims;
}
public Claim GetClaimByID (int id) {
return Database.Claims.FirstOrDefault(c => c.ClaimID == id);
}
public void AddClaim(Claim claim) {
Database.Claims.Add(claim);
Database.SaveChanges();
}
public void UpdateClaim(Claim claim) {
var tmpClaim = Database.Claims.FirstOrDefault(c => c.ClaimID == claim.ClaimID);
tmpClaim.ClaimTotal = claim.ClaimTotal;
tmpClaim.ClaimWIP = claim.ClaimWIP;
tmpClaim.FK_ClientID = claim.FK_ClientID;
tmpClaim.FK_SupplierID = claim.FK_SupplierID;
Database.SaveChanges();
}
public void DeleteClaim(Claim claim) {
Database.Claims.Remove(claim);
Database.SaveChanges();
}
#endregion
}
}
Can someone please help me understand this error and also help my fix it please. Thank you.
SQLAPRepository must implement IAPRepository.
public class SQLAPRepository : IAPRepository
{
....
}
as Radin said it must implement the IAPRepository interface. When you do dependency injection you're allowing any implementation of an interface to be used at runtime. For production code there is either an explicit configuration mapping or some kind of interrogation of available implementations at runtime.
In NancyFX TinyIoC is used, and it does not require explicit type mapping. For other solutions like Unity there is an explicit type mapping done for many implementations container.RegisterType<IMyInterface,MyImplementation>();

programmatically change a dependency in Castle Windsor

I have a class that calls out to an internet service to get some data:
public class MarketingService
{
private IDataProvider _provider;
public MarketingService(IDataProvider provider)
{
_provider = provider;
}
public string GetData(int id)
{
return _provider.Get(id);
}
}
Currently I have two providers: HttpDataProvider and FileDataProvider. Normally I will wire up to the HttpDataProvider but if the external web service fails, I'd like to change the system to bind to the FileDataProvider . Something like:
public string GetData(int id)
{
string result = "";
try
{
result = GetData(id); // call to HttpDataProvider
}
catch (Exception)
{
// change the Windsor binding so that all future calls go automatically to the
// FileDataProvier
// And while I'm at it, retry against the FileDataProvider
}
return result;
}
So when this has been executed all future instances of MarketingService will automatically be wired up to the FileDataProvider. How to change a Windsor binding on the fly?
One solution would be using selector
public class ForcedImplementationSelector<TService> : IHandlerSelector
{
private static Dictionary<Type, Type> _forcedImplementation = new Dictionary<Type, Type>();
public static void ForceTo<T>() where T: TService
{
_forcedImplementation[typeof(TService)] = typeof(T);
}
public static void ClearForce()
{
_forcedImplementation[typeof(TService)] = null;
}
public bool HasOpinionAbout(string key, Type service)
{
return service == typeof (TService);
}
public IHandler SelectHandler(string key, Type service, IHandler[] handlers)
{
var tService = typeof(TService);
if (_forcedImplementation.ContainsKey(tService) && _forcedImplementation[tService] != null)
{
return handlers.FirstOrDefault(handler => handler.ComponentModel.Implementation == _forcedImplementation[tService]);
}
// return default
return handlers[0];
}
}
Test and usage
[TestFixture]
public class Test
{
[Test]
public void ForceImplementation()
{
var container = new WindsorContainer();
container.Register(Component.For<IFoo>().ImplementedBy<Foo>());
container.Register(Component.For<IFoo>().ImplementedBy<Bar>());
container.Kernel.AddHandlerSelector(new ForcedImplementationSelector<IFoo>());
var i = container.Resolve<IFoo>();
Assert.AreEqual(typeof(Foo), i.GetType());
ForcedImplementationSelector<IFoo>.ForceTo<Bar>();
i = container.Resolve<IFoo>();
Assert.AreEqual(typeof(Bar), i.GetType());
ForcedImplementationSelector<IFoo>.ClearForce();
i = container.Resolve<IFoo>();
Assert.AreEqual(typeof(Foo), i.GetType());
}
}
Alternatively you could create a proxy:
public class AutoSelectingDataProvider : IDataProvider
{
public AutoSelectingDataPovider(HttpDataProvider httpDataProvider, FallBackDataProvider fallBackProvider)
{
_httpDataProvider = httpDataProvider;
_fallBackDataProvider = fallBackDataProvider;
}
public string GetData(int id)
{
try
{
return _httpDataProvider.GetData(id);
}
catch (Exception)
{
return _fallBackDataProvider.GetData(id);
}
return result;
}
}
container.Register(
Component.For<HttpDataProvider>(),
Component.For<FallBackDataProvider>(),
Component.For<IDataProvider>().ImplementedBy<FallBackDataProvider>());
This will always first try to get data from the HttpDataProvider if not succesfull use the fallback. If you want you can introduce state and after a failure always use the fallback. This way you can keep using the IDataProvider in your application without needing to obtain a new one from the container.

Modify existing WCF communication object

This is how I used to make method calls:
SvcHelper.Using<SomeWebServiceClient>(proxy =>
{
proxy.SomeMethod();
}
public class SvcHelper
{
public static void Using<TClient>(Action<TClient> action) where TClient : ICommunicationObject, IDisposable, new()
{
}
}
This is how I make method calls:
ChannelFactory<ISomethingWebService> cnFactory = new ChannelFactory<ISomethingWebService>("SomethingWebService");
ISomethingWebService client = cnFactory.CreateChannel();
using (new OperationContextScope((IContextChannel)client))
{
client.SomeMethod();
}
My question is: Instead of replacing every instance of my original method call approach; Is there a way to modify my SvcHelper and do the creation of the channel in the SvcHelper constructor and then simply pass the interface like the following:
SvcHelper.Using<ISomethingWebService>(client =>
{
client.SomeMethod();
}
Hope this makes sense and thanks in advance.
First, you don't want to create a new ChannelFactory<T> every call to the Using helper method. They are the most costly thing to construct in the WCF universe. So, at bare minimum, you will want to use a caching approach there.
Second, you don't want to tie yourself to "client" types at all anymore. Just work straight with the service contract interfaces.
Starting from what you've got, here's where I'd go based on how I've done this in the past:
public class SvcHelper
{
private static ConcurrentDictionary<ChannelFactoryCacheKey, ChannelFactory> ChannelFactories = new ConcurrentDictionary<ChannelFactoryCacheKey, ChannelFactory>();
public static void Using<TServiceContract>(Action<TServiceContract> action) where TServiceContract : class
{
SvcHelper.Using<TServiceContract>(action, "*");
}
public static void Using<TServiceContract>(Action<TServiceContract> action, string endpointConfigurationName) where TServiceContract : class
{
ChannelFactoryCacheKey cacheKey = new ChannelFactoryCacheKey(typeof(TServiceContract), endpointConfigurationName);
ChannelFactory<TServiceContract> channelFactory = (ChannelFactory<TServiceContract>)SvcHelper.ChannelFactories.GetOrAdd(
cacheKey,
missingCacheKey => new ChannelFactory<TServiceContract>(missingCacheKey.EndpointConfigurationName));
TServiceContract typedChannel = channelFactory.CreateChannel();
IClientChannel clientChannel = (IClientChannel)typedChannel;
try
{
using(new OperationContextScope((IContextChannel)typedChannel))
{
action(typedChannel);
}
}
finally
{
try
{
clientChannel.Close();
}
catch
{
clientChannel.Abort();
}
}
}
private sealed class ChannelFactoryCacheKey : IEquatable<ChannelFactoryCacheKey>
{
public ChannelFactoryCacheKey(Type channelType, string endpointConfigurationName)
{
this.channelType = channelType;
this.endpointConfigurationName = endpointConfigurationName;
}
private Type channelType;
public Type ChannelType
{
get
{
return this.channelType;
}
}
private string endpointConfigurationName;
public string EndpointConfigurationName
{
get
{
return this.endpointConfigurationName;
}
}
public bool Equals(ChannelFactoryCacheKey compareTo)
{
return object.ReferenceEquals(this, compareTo)
||
(compareTo != null
&&
this.channelType == compareTo.channelType
&&
this.endpointConfigurationName == compareTo.endpointConfigurationName);
}
public override bool Equals(object compareTo)
{
return this.Equals(compareTo as ChannelFactoryCacheKey);
}
public override int GetHashCode()
{
return this.channelType.GetHashCode() ^ this.endpointConfigurationName.GetHashCode();
}
}
}
This should work:
public class SvcHelper
{
public static void Using<TClient>(Action<TClient> action) where TClient : ICommunicationObject, IDisposable
{
ChannelFactory<TClient> cnFactory = new ChannelFactory<TClient>("SomethingWebService");
TClient client = cnFactory.CreateChannel();
using (new OperationContextScope((IContextChannel)client))
{
action(client);
}
}
}

Categories