I am attempting to configure a web service with Autofac so that I can map a different connection context for each controller:
// database connections
container.Register(c => new DocumentControllerActivator()).As<IHttpControllerActivator>().InstancePerApiControllerType(typeof(DocumentController));
container.Register(c => new WorkflowControllerActivator()).As<IHttpControllerActivator>().InstancePerApiControllerType(typeof(WorkflowController));
and:
public class WorkflowControllerActivator : IHttpControllerActivator
{
// snip...
var connectionString = "workflow connection string";
var container = new ContainerBuilder();
container.Register(c =>
{
var newConnectionContext = new SqlServerConnectionContext(connectionString) {ProductID = productId};
newConnectionContext.Open();
return newConnectionContext;
}).As<ISqlServerConnectionContext>().As<IConnectionContext>().InstancePerApiRequest();
var dr = (AutofacWebApiDependencyResolver)GlobalConfiguration.Configuration.DependencyResolver;
container.Update(dr.Container.ComponentRegistry);
return (IHttpController)request.GetDependencyScope().GetService(typeof(WorkflowController));
}
The DocumentControllerActivator differs only in the connection string the the return object type.
[AutofacControllerConfiguration]
public class WorkflowController : ApiController
When I attempt to access the service, the DocumentController throws an error saying that "Unable to cast object of type 'SearchService.Controllers.WorkflowController' to type 'SearchService.Controllers.DocumentController'." It's as if the second InstancePerApiControllerType registration is overwriting the first (i.e. it is doing it for all controllers).
Any suggestions where I've gone wrong? Or, an alternate solution? (Other than a service locator pattern in each controller.)
Not sure why the ControllerActivator approach isn't working, but a simpler and less web api stack specific alternative could be:
public interface ISqlServerConnectionContextFactory
{
ISqlServerConnectionContext Create();
}
// Register this with your ContainerBuilder
public class SqlServerConnectionContextFactory : ISqlServerConnectionContextFactory
{
private string _connectionString;
public SqlServerConnectionContextFactory(string connectionString)
{
_connectionString = connectionString;
}
public ISqlServerConnectionContext Create()
{
var connectionContext = new SqlServerConnectionContext(_connectionString);
connectionContext.Open();
return connectionContext;
}
}
public class MyController : ApiController
{
private ISqlServerConnectionContext _sqlServerConnectionContext;
public MyController(Func<string, ISqlServerConnectionContextFactory> connectionFactory)
{
_sqlServerConnectionContext = connectionFactory("MyConnectionString");
}
}
See http://nblumhardt.com/2010/01/the-relationship-zoo/ for more information about AutoFac relationships and auto generated factories
In this case, when the AutoFac Container sees the Func parameter, it passes in a delegate that acts as a factory method that returns a ISqlServerConnectionContextFactory and passes the string through to its constructor. AutoFac really is rather clever!
Related
For simplicity sake, I'd like to Inject a generic repository into a view model. I'm add the the ViewModel and Repository to the services collection and the ViewModel depends on the Repository. From what I've read, the ViewModel's constructor parameters should be resolved automatically, since it's using IConfiguration. I've tried explicitly instantiating the ViewModel in the Services, but still get the same runtime error and seems to defeat the purpose as I'm creating another instance of the repository.
Repository/IRepository look like this
public class Repository<TPoco, TDatabaseConnection> : IRepository<TPoco, TDatabaseConnection>
where TPoco : class
where TDatabaseConnection : IDbConnection, new()
{
public Repository(IConfiguration configuration, string connectionName = "DefaultConnection" )//SqlConnectionConfiguration configuration) //(string connectionString)
{
_connectionString = configuration.GetConnectionString(connectionName);
_configuration = configuration;
}
...
View Model
public class PersonViewModel
{
private IRepository<Person, IDbConnection> _PersonRepository;
public List<Person> Persons { get; set; }
public PersonViewModel(IRepository<Person, IDbConnection> personRepository)
{
_PersonRepository=personRepository;
}
...
In Startup.cs file I add the services like so:
services.AddScoped<IRepository<Person, SQLiteConnection>, Repository<Person, SQLiteConnection>>();
services.AddScoped<PersonViewModel>(); //runtime errors here
I'm getting two runtime errors (System.AggregateException)
Inner Exception 1:
InvalidOperationException: Error while validating the service descriptor 'ServiceType: BlazorInjection.ViewModels.PersonViewModel Lifetime: Scoped ImplementationType: BlazorInjection.ViewModels.PersonViewModel': Unable to resolve service for type 'DapperRepository.IRepository`2[BlazorInjection.Models.Person,System.Data.IDbConnection]' while attempting to activate 'BlazorInjection.ViewModels.PersonViewModel'.
Inner Exception 2:
InvalidOperationException: Unable to resolve service for type 'DapperRepository.IRepository`2[BlazorInjection.Models.Person,System.Data.IDbConnection]' while attempting to activate 'BlazorInjection.ViewModels.PersonViewModel'.
Am I missing a concept? How do I correct this?
change class structure to:
public class PersonViewModel
{
private IRepository<Person> _PersonRepository;
public List<Person> Persons { get; set; }
public PersonViewModel(IRepository<Person> personRepository)
{
_PersonRepository = personRepository;
}
}
public interface IDbConnection
{
}
public class Person { }
public interface IRepository<TPoco> { }
public class SQLiteConnection : IDbConnection
{
private string _connectionString;
private IConfiguration _configuration;
public SQLiteConnection(IConfiguration configuration, string connectionStringName)
{
_connectionString = configuration.GetConnectionString(connectionStringName);
_configuration = configuration;
}
}
public class Repository<TPoco> : IRepository<TPoco>
where TPoco : class
{
public IDbConnection Connection { get; }
public Repository(IDbConnection connection)
{
Connection = connection;
}
}
And than in Startup.cs simply change to
services.AddScoped<IDbConnection>(sp => ActivatorUtilities.CreateInstance<SQLiteConnection>(sp, "defaultConnectionStringName"));
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
services.AddScoped<PersonViewModel>();
Notice generic repository registration which makes it easy to extend your data layer. ActivatorUtilities allows you to combine current scope container services with custom parameters. This way (one generic, injected connection) is also in my opinion better from the design perspective, as your repository clients do not need to know the underlying database implementation.
I have MemoryCache objects (Application,Configuration etc) which I registered them as Singleton. Also there are scoped repositories which selects data from db to fill cache.
For example here is the Singleton registered class,
public class ApplicationCache : MultipleLoadCache<Application>
{
public ApplicationCache()
{
}
}
MultipleLoadCache overrides the CacheItemPolicy, (there is also SingleLoadCache),
public class MultipleLoadCache<TEntity> : SmartCache<TEntity> where TEntity : class
{
public MultipleLoadCache()
{
}
protected override CacheItemPolicy SetPolicy()
{
return new CacheItemPolicy()
{
AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(15)
};
}
}
And base class is,
public class SmartCache<TEntity> : IDisposable where TEntity : class
{
public bool TryGetList(IRepository<TEntity> repository, out List<TEntity> valueList)
{
valueList = null;
lock (cacheLock)
{
GenerateCacheIfNotExists(repository, out valueList);
if (valueList == null || valueList.Count == 0)
{
valueList = (List<TEntity>)_memoryCache.Get(key);
}
}
return valueList != null;
}
I know that scoped services can't be injected to singleton class. So I prefer to use method injection.
private void GenerateCacheIfNotExists(IRepository<TEntity> repository, out List<TEntity> list)
{
list = null;
if (!_memoryCache.Any(x => x.Key == key)) // if key not exists, get db records from repo.
{
IEnumerable<TEntity> tempList = repository.GetList();
list = tempList.ToList();
_cacheItemPolicy = SetPolicy();
SetCacheList(list);
}
}
}
And at controller I try to get cache values, but this part seems wrong to me. If I try to get cache values, I shouldn't pass repository as parameter.
private readonly ApplicationCache _appCache;
public LogController(ApplicationCache appCache)
{
_appCache = appCache;
}
[HttpPost]
[Route("Register")]
public List<Application> Register([FromServices] IApplicationRepository repository)
{
List<Application> cf;
_appCache.TryGetList(repository, out cf);
return cf;
}
Also, by doing Method Injection. I am also unable to use RemovedCallBack event of CacheItemPolicy. Because, when callback triggers (reload cache), I need repository to get records from db again.
Is this design seems nice, what is the best design to do this by using callback events of MemoryCache?
Update 1-
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddMemoryCache();
services.AddSingleton(x => new ApplicationCache());
services.AddScoped<IApplicationRepository, ApplicationRepository>();
}
Thanks,
I had the same issue. Since static classes is compiled at the beginning it cannot inject the required services later. I figured it out by using IServiceScopeFactory.
You basically inject IServiceScopeFactory serviceScopeFactory in the constructer .
static SampleClass(IServiceScopeFactory serviceScopeFactory){
//serviceScopedFactory will act as Singleton, since it is a static class
_serviceScopeFactory = serviceScopeFactory;
}
And use it like this in the method :
using (var scope = _serviceScopeFactory.CreateScope())
{
var service = scope.ServiceProvider.GetRequiredService<IService>();
//Here you can use the service. This will be used as Scoped since it will be
//recreated everytime it is called
}
Below is a simple but functional example of roughly how I would do Dependency Injection. This works great when my DbContext connection string is not dynamic. Even if it's passed in to the factory through a config file or whatever, it doesn't matter so long as it's the same one all the time.
What I need is to wrap my head around how to make (ideally minor) modifications to the below code to allow for the connection string to be determined dynamically at run time.
For example, say on the View the user was able to not only select the teacher to be passed into the Post method of the controller, but also the school. If, for simplicity sake, there are 2 schools that have the exact same database structure, but have different connection strings how do I get that down from the controller to the factory?
I've experimented with passing a value from method to method, but this isn't really sustainable for large projects, increases the likelihood of errors and overall is just messy (besides violations of SOLID) to be passing something from layer to layer like that. (If desired I can add the not exactly ideal attempts I've made, I've omitted them for brevity sake since this is already a fairly long question what with the code examples and all).
Controller
public class HomeController : Controller
{
private readonly IDataService _dataService;
public HomeController(IDataService dataService)
{
_dataService = dataService;
}
public ActionResult Index()
{
var results = _dataService.GetTeachers();
var model = new ViewModel
{
Teachers = results
};
return View(model);
}
[HttpPost]
public ActionResult Index(ViewModel model)
{
var results = _dataService.GetCourses(model.Teacher);
model.Courses = new List<string>(results);
return View(model);
}
}
Service
public class DataService : IDataService
{
private readonly IDataRepo _dataRepo;
public DataService(IDataRepo dataRepo)
{
_dataRepo = dataRepo;
}
public List<string> GetCourses(string teacherName)
{
return _dataRepo.GetCourses()
.Where(c => c.Teacher.FirstName == teacherName)
.Select(c => c.Name)
.ToList();
}
public List<string> GetTeachers()
{
return _dataRepo.GetCourses()
.Select(c => c.Teacher.FirstName)
.ToList();
}
}
Repository
public class DataRepo : IDataRepo
{
private readonly SchoolContext _context;
public DataRepo()
{
_context = ContextFactory.MakeContext();
}
public IEnumerable<Course> GetCourses()
{
return _context.Courses;
}
}
Context Factory
public static class ContextFactory
{
public static SchoolContext MakeContext()
{
var connString =
"connStringA";
return new SchoolContext(connString);
}
}
UnityConfig
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<IDataService, DataService>();
container.RegisterType<IDataRepo, DataRepo>();
DependencyResolver.SetResolver(new UnityDependencyResolver(container));
}
First, you have to decide how are you going to get the current connection string to use. Is it through a URL? or using the current user or whatever other way.
Then, create another database that has a mapping between the connection strings and the method you chose (user, url ...)
Lastly, implement a way to get the record from the database.
so, assuming that you will use the URL as an identifier for the current tenant, your entity class should be like this:
public class Tenant
{
public string Url {get;set;}
public string ConnectionString {get;set;}
}
An interface that represents the logic to get the current tenant:
public interface ICurrentTenantService
{
Tenant GetCurrentTenant();
}
And now you will put its implementation
public class CurrentTenantService : ICurrentTenantService
{
public Tenant GetCurrentTenant()
{
string currentUrl = HttpContext.Current.Url; //make sure to get only the base URL here
return TenantDbContext.Tenants.FirstOrDefault(t=>t.Url == url); //TenantDbContext should be a class that has the Tenant entity
}
}
Now you have to wire up the context factory to the tenant service like this
public static class ContextFactory
{
private readonly ICurrentTenantService currentTenantService;
//Inject it in the constructor
public static SchoolContext MakeContext()
{
var currentTenant= currentTenantService.GetCurrentTenant(); //Check for NULL
return new SchoolContext(currentTenant.ConnectionString);
}
}
i´m doing an app, using ServiceStack. I could inject an object without problems, but, the object can be modified outside the Service Class, so, i need to re inject again
Here is the code:
public class ClientManager: ApplicationContext{
public ClientManager(AppConfig appConfig)
{
_appConfig = appConfig;
_activeForm = LayoutFactory(appConfig.Layout);
var appHost = new AppHost(_activeForm, _appConfig);
var listeningOn = string.Format("http://*:{0}/", new Uri(appConfig.UrlBroker).Port);
appHost.Init();
appHost.Start(listeningOn);
var timerMetadata = new Timer(CheckMetadata, null, 0, 60000);
}
}
public class AppHost : AppSelfHostBase
{
private ILayout _layout;
private AppConfig _appConfig;
public AppHost(ILayout activeForm, AppConfig appConfig)
: base("ClientService", typeof(ClientService).Assembly)
{
_layout = activeForm;
_appConfig = appConfig;
}
public override void Configure(Container container)
{
container.Register("activeForm", _layout);
container.Register("config", _appConfig);
}
}
public class ClientService : Service
{
public HttpResult Post(Person request)
{
HttpResult response = new HttpResult();
_initConf = ServiceStackHost.Instance.Container.ResolveNamed<AppConfig>("config");
}
}
So, the class ClientManager has a thread which can modify the object appConfig and activeForm (this objects are injected into the service class)
Now, if i modify the object, it doesn´t inject it again. I think thath i should dispose the AppHost, and start it again, what do you think?
Thanks
It's very rare that you'd want to dispose the AppHost unless you're running Integration tests where you want to start/destroy multiple AppHost instances.
Also I'd strongly recommend against using names when registering dependencies, just use the types of the dependencies as normal, e.g:
container.Register(_layout);
container.Register(_appConfig);
Any dependencies registered in the IOC are then automatically injected into your Service class by declaring a public property with that type, e.g:
public class ClientService : Service
{
public AppConfig AppConfig { get; set; }
public HttpResult Post(Person request)
{
HttpResult response = new HttpResult();
var _initConf = AppConfig;
}
}
This injects the same instance that's registered in the IOC, so if you modify the instance later the Service would inject the same modified instance by default.
I have a ICommand interface and tasks that are using dependencies injected by constructor. Dependencies are using different constructors so they have to be resolved by the request itself. I want to tell my container how to resolve some dependencies in the specific context it's being resolved.
interface ICommand
{
string Do();
}
interface IUser
{
string Name { get; }
}
class Welcome : ICommand
{
IUser _user;
public Welcome(IUser user)
{
_user = user;
}
public string Do()
{
return "Hello, "+_user.Name;
}
}
class OAuthUser : IUser
{
// use remote service to get data
public OAuthUser (IOAuthService service, JsonWebToken token)
{
// to be implemented
}
}
class TemporaryTokenUser : IUser
{
// use sql to check if user has temporary token
public TemporaryTokenUser (IDbConnection db, string token)
{
// to be implemented
}
}
class UserPasswordUser : IUser
{
// try authenticating user with credentials
public UserPasswordUser (IAuthService svc, string user, string password)
{
// to be implemented
}
}
I've registered my interfaces and classes in LightInject:
var container = new LightInject.ServiceContainer();
container.Register<ICommand, Welcome>("welcome");
Now, I want to do something like this in my requests:
using (var scope = container.BeginScope())
{
// I need to tell my container how to resolve this dependency in case its needed
// but method below does not exist
scope.ResolverForScope<IUser>(()=>createIUser(request));
var command = container.GetInstance<ICommand>(command);
return command.Do();
}
What would be the correct way to do this in maintainable way with any DI container, considering that dependency chain might get quite long for complex methods?
EDIT
I made my use case more clear (changed classes implementing IUser).
static class ScopedContainerExtensions
{
class ScopedContainer
{
Dictionary<Type, object> factories = new Dictionary<Type,object>();
public void Register<T>(Func<T> factory)
where T: class
{
factories.Add(typeof(T), new Lazy<T>(factory));
}
public T Resolve<T>()
{
return ((Lazy<T>)factories[typeof(T)]).Value;
}
}
public static void UseScopedContainerFor<Service>(this IServiceContainer container)
{
if (!container.CanGetInstance(typeof(ScopedContainer), ""))
{
container.Register<ScopedContainer>(new PerScopeLifetime());
}
container.Register<Service>(sf=>sf.GetInstance<ScopedContainer>().Resolve<Service>());
}
public static void ResolverForCurrentScope<T>(this IServiceContainer container, Func<IServiceFactory, T> factory)
where T : class
{
var scope = container.ScopeManagerProvider.GetScopeManager().CurrentScope;
container.GetInstance<ScopedStorage>().Register<T>(() =>
{
var instance = factory(container);
var disposable = instance as IDisposable;
if (disposable != null)
scope.TrackInstance(disposable);
return instance;
});
}
Registration:
container.UseScopedContainerFor<IUser>();
Usage in scope:
container.ResolverForCurrentScope<IUser>(fac => fac.GetInstance<OAuthUserFactory>().Create(fac.GetInstance<IOAuthService>(), Request));
It might be developed via using the Factory pattern.
With this approach, you might be able to get an instance of the specific user with a Factory to provide instances for each concrete class.
Using explicit service registration:
var container = new LightInject.ServiceContainer();
//register your command passing a user instance
container.Register<ICommand>(factory => new Welcome(factory.GetUser<IUser>(request)));
using (var scope = container.BeginScope())
{
var command = (ICommand)container.GetInstance<ICommand>();
return command.Do();
}
I just referred to LightInject web page. There is a chapter called "Dependency Constructors" for further information. http://www.lightinject.net/#toc16
Hope it helps