I am using Castle to create my database context based on a given interface. I have the following code in my Installer class and this works fine at the moment.
private ConfigureDelegate ConfigureContext()
{
return p => p.Named(p.ServiceType.Name)
.LifeStyle.PerWebRequest
.DependsOn(new { connectionString = ConfigurationManager.ConnectionStrings["conStringName"].ConnectionString });
}
However i now have a scenario where this installer will find more than one concrete implementation of my interface, where each one should have a different connection string supplied.
Is this possible - if so, could someone point me in the right direction.
TIA
Yes, it's possible if you can write a piece of code that provides the connection string name for the service. Perhaps something like this:
private ConfigureDelegate ConfigureContext()
{
return p => p.Named(p.ServiceType.Name)
.LifeStyle.PerWebRequest
.DependsOn(new
{
connectionString =
ConfigurationManager
.ConnectionStrings[GetConnectionName(p.ServiceType.Name)]
.ConnectionString
});
}
private string GetConnectionName(string serviceName)
{
// return the connection name
}
Related
Here's what I'm doing so far (code simplified):
public class MyRegistrationSource : IRegistrationSource
{
public MyRegistrationSource(ContainerBuilder builder /*,...*/)
{
// ...
this.builder = builder;
}
public IEnumerable<IComponentRegistration> RegistrationsFor(
Service service, Func<Service, IEnumerable<IComponentRegistration>> registrationAccessor)
{
// Some checks here
var interfaceType = serviceWithType.ServiceType;
var implementorType = FindTheRightImplementor(interfaceType);
if (myRegisterConditionSatisfied)
{
return Register(implementorType, interfaceType);
}
return Empty;
}
private IEnumerable<IComponentRegistration> Register(Type concrete, Type #interface)
{
var regBuilder = builder.RegisterType(concrete).As(#interface).IfNotRegistered(#interface);
return new[] { regBuilder.CreateRegistration() };
}
}
Then, at startup I'm doing something like
builder.RegisterSource(
new NonRegisteredServicesRegistrationSource(builder/*, ...*/));
The above is intended to register those matching services only when there's no previous registration. I tried doing the registration without using the ContainerBuilder but couldn't get it to work.
This is working but are there any issues in passing-in the ContainerBuilder instance to the RegistrationSource?
Thanks!
I'd probably argue against passing in a ContainerBuilder.
Every type you register in your source will add a callback to a list of callbacks inside the Container Builder which will never get cleared, potentially creating a memory leak.
I'd suggest calling the static method RegistrationBuilder.ForType instead, which will give you a fluent builder and should let you subsequently call CreateRegistration as you are now.
You can see some pretty good examples of how do this in our Moq integration:
var reg = RegistrationBuilder.ForType(concrete)
.As(#interface)
.CreateRegistration();
Also, I don't believe IfNotRegistered will have any effect when used outside the context of a ContainerBuilder. You should use the provided registrationAccessor parameter to the registration source to look up a TypedService to see if it has already been registered:
var isRegistered = registrationAccessor(new TypedService(#interface)).Any();
PLEASE READ THE EDIT SECTION, IT CAN HELP ME TO CLARIFY THE QUESTION
I have this structure right now:
WebApp.csproj
Application.csproj
Data.csproj
Oracle.csproj
SqlServer.csproj
My Data (3) is just a project referenced by WebApp (1) to decides which one (4 or 5) should be called based on the web.config.
If the web.config contains the app.key DBaseDL with 'oracle' value, it should LoadAssembly 4, instead, assembly 5.
Classes of 4 and 5 are mirror but with the query on each syntax (4 has syntax for oracle and 5 for sql server). Those mirror classes implements a commom interface between then, like this:
namespace MyProject.Oracle
{
public class User : IUser
{
//...
}
}
namespace MyProject.SqlServer
{
public class User : IUser
{
//...
}
}
On Data (3) csproj I'm trying to create a Factory which creates the User class from 4 or 5, based on the web.config settings like I said before. So I do something like this:
public class UserDataFactory : IDataLayerFactory<IUser>
{
private readonly string _key;
public UserDataFactory(string key)
{
_key = key;
}
public IUser Create()
{
string strPath = string.Empty;
string strClassName = string.Empty;
string strAssemblyName = string.Empty;
string strVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
if (_key.Equals("oracle"))
strAssemblyName = "MyProjejct.Oracle";
else
strAssemblyName = "MyProject.SQLServer";
strPath = strAssemblyName + ", Version=" + strVersion + ", Culture=neutral, PublicKeyToken=xxxxxxx";
strClassName = strAssemblyName + ".User";
return (IUser)Assembly.Load(strPath).CreateInstance(strClassName);
}
}
My problem starts on this IDataFactory class.
I already did everything works with forced values (like always on SQLServer or Oracle) but not with IoC + the settings key for database.
I'm trying to do something like this with AutoFac but I'm probably missing something and I'm blind.
public static void ConfigureContainer()
{
var builder = new ContainerBuilder();
builder.RegisterControllers(Assembly.GetExecutingAssembly()).InstancePerRequest();
builder.RegisterType<Mediator>().As<IMediator>().InstancePerLifetimeScope();
builder.RegisterType<AutofacValidatorFactory>().As<IValidatorFactory>().SingleInstance();
builder.RegisterType<AutofacDataLayerFactory>().As<IDataFactory>().InstancePerLifetimeScope();
builder.RegisterType<FluentValidationModelValidatorProvider>().As<ModelValidatorProvider>();
builder.RegisterType<RegistryManagerService>().As<IRegistryManagerService>().SingleInstance().WithParameter("appName", ConfigurationManager.AppSettings["APPNAME"]);
builder.Register<ServiceFactory>(context =>
{
var c = context.Resolve<IComponentContext>();
return t => c.Resolve(t);
});
builder.RegisterAssemblyTypes(Assembly.Load("MyProj.Application"))
.Where(x => x.Name.EndsWith("Handler"))
.AsImplementedInterfaces();
builder.RegisterAssemblyTypes(Assembly.Load("MyProj.Application"))
.Where(x => x.Name.EndsWith("Validator"))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
builder.RegisterAssemblyTypes(Assembly.Load("MyProj.Application"))
.Where(x => x.Name.EndsWith("DataFactory"))
.AsImplementedInterfaces()
.InstancePerLifetimeScope();
var container = builder.Build();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}
}
Just to say, IDataFactory is about User now, but it will be a lot of things, and I don't want to register for every class again on autofac. I'm trying to register all my IDataFactory so the constructor of my handlers on Application.csproj should get the already created by factory classes, the DataAccess object from Oracle or SqlServer.
Can someone help me?
EDIT
After digging and learning a little more about the AutoFac, IoC and DI, I realize a easy way to register my class, but I'm pretty sure there is a better and more ellegant solution then mine.
I just read the key from configuration manager and write this code at the AutoFacConfig file:
if (key == "oracle")
builder.RegisterType<DocspiderOracleDbService>().As<IDocspiderDbService>().InstancePerRequest();
else
builder.RegisterType<DocspiderSqlServerDbService>().As<IDocspiderDbService>().InstancePerRequest();
This worked because only the service from the correct database is registered, but, I'll keep digging how to make it the right away.
If anyone have an idea, I'll be greatfull.
Problem:
I have webapi serviss where almost every user has its own database instance to connect. So i have to set different connection string for each user. To recognize user i will pass specific Token into header. Regarding on this Token, system has to build and set differenct connection string into Data Access layer constructor (Order in this case)
Question:
Is it possible to pass argument to Ninject or any kind of IoC binder regarding on request header?
IOrders _orders;
public HomeController(IOrders order)
{
_orders = order;
}
Here is an Ninject binding, but as you can guess, HttpContext.Current is null.
private static void RegisterServices(IKernel kernel)
{
var some_value = HttpContext.Current.Request.Headers.GetValues("Token");
kernel.Bind<IOrders>()
.To<Orders>()
.WhenInjectedInto<HomeController>()
.WithConstructorArgument("Token", some_value);
}
Maybe there is much elegant way to do this using Controller Factory ?
I would create a service class that does this lookup for you. then inject this service into the Orders implementation.
public interface IRequestContext {
string ConnectionString {get;}
}
public class HttpHeaderRequestContext : IRequestContext {
public string ConnectionString {
get {
var token = HttpContext.Current.Request.Headers.GetValues("Token");
// .. lookup conn string based on token
}
}
}
public class Orders : IOrders {
public Orders(IRequestContext ctx) {
// create new connection w/ ctx.ConnectionString
}
}
using this method, the lookup of headers and connection strings is abstracted away from the implementation. this makes it easier to test and easier swap out with a different method of obtaining a connection string if the need arises.
After implementing Dave approach, i realized that i could solve this connection string injection by feeding HttpContext.Current into Ninject binding like this:
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IOrders>()
.To<Orders>()
.WhenInjectedInto<HomeController>()
.WithConstructorArgument("smth", x => {
var token = HttpContext.Current.Request.Headers.Get("Token");
var _db = new SomeDataCxt();
var connStr = _db.DbStringRepository.GetByToken(token);
return connStr;
});
}
I'm currently trying out Autofac in a new ASP.NET MVC project after having used Ninject, Castle Windsor and other IoC containers in the last years. So while I know about IoC containers in general, I'm fairly new to Autofac and I'm still looking for some best practices.
Currently I'm trying to find out if there is a way to resolve the innermost nested scope.
I have the following situation: a component that is registered as SingleInstance() has a method that creates a nested lifetime scope, providing a configuration action to configure some components as InstancePerLifetimeScope, and within this nested scope resolves the registered components to do something useful, like so:
ILifetimeScope currentScope = ???;
using (var scope = currentScope.BeginLifetimeScope(cb => {
cb.RegisterType<X>().InstancePerLifetimeScope();
// ...
}))
{
var comp = scope.Resolve<X>();
// ...
}
The issue is that I would like currentScope to be the innermost lifetime scope, because I know that X depends on components inside the innermost scope. In the simplest case that would be e.g. the current request lifetime scope. I can of course get it with AutofacDependencyResolver.Current.RequestLifetimeScope but I don't want to use that as it isn't really well testable. Also, that lifetime scope isn't necessarily the innermost.
So, is there a way to find the innermost lifetime scope given e.g. the root container or a different ILifetimeScope?
In Autofac, the innermost scope is always the container. Using the AutofacDependencyResolver, it'd be
AutofacDependencyResolver.Current.ApplicationContainer
There is no way from a nested scope (if all you have is an ILifetimeScope) to "walk backward" to get to the container. I'm not necessarily sure you want to do that, anyway.
It sounds like your SingleInstance component is doing some sort of service location, basically, with manual registration/resolution of certain components. If the set of types being registered is fixed, I might recommend (if possible) some redesign of your system, so the SingleInstance component isn't registered as SingleInstance anymore and instead gets registered as InstancePerDependency, then have that take these other items in as constructor parameters.
Instead of...
// Consuming class like this...
public class BigComponent
{
public void DoSomethingCool()
{
using(var scope = ...)
{
var c = scope.Resolve<SubComponent>();
c.DoWork();
}
}
}
// ...and container registrations like this...
builder.RegisterType<BigComponent>().SingleInstance();
You might try inverting it a bit:
// Consuming class like this...
public class BigComponent
{
private SubComponent _c;
public BigComponent(SubComponent c)
{
_c = c;
}
public void DoSomethingCool()
{
_c.DoWork();
}
}
// ...and container registrations like this...
builder.RegisterType<BigComponent>().InstancePerDependency();
builder.RegisterType<SubComponent>().InstancePerLifetimeScope();
The idea is to not have to do the on-the-fly registration-and-immediate-resolution thing.
If you're stuck doing service location, you'll need to use AutofacDependencyResolver.Current.ApplicationContainer if you need the absolute innermost scope, but keep in mind any objects you register scoped to InstancePerHttpRequest will not be resolvable if you do that, so you could get into trouble. It really is recommended to use the AutofacDependencyResolver.Current.RequestLifetimeScope instead. That would make your method:
var requestScope = AutofacDependencyResolver.Current.RequestLifetimeScope;
using (var scope = requestScope.BeginLifetimeScope(cb => {
cb.RegisterType<X>().InstancePerLifetimeScope();
// ...
}))
{
var comp = scope.Resolve<X>();
// ...
}
In a testing environment, the AutofacDependencyResolver lets you swap in the provider that dictates how request lifetimes get generated. You can implement a simple/stub one like this:
public class TestLifetimeScopeProvider : ILifetimeScopeProvider
{
readonly ILifetimeScope _container;
private ILifetimeScope _lifetimeScope = null;
public TestLifetimeScopeProvider(ILifetimeScope container)
{
if (container == null) throw new ArgumentNullException("container");
_container = container;
}
public ILifetimeScope ApplicationContainer
{
get { return _container; }
}
public ILifetimeScope GetLifetimeScope()
{
if (_lifetimeScope == null)
{
_lifetimeScope = ApplicationContainer.BeginLifetimeScope("httpRequest")
}
return _lifetimeScope;
}
public void EndLifetimeScope()
{
if (_lifetimeScope != null)
_lifetimeScope.Dispose();
}
}
Again, just a stub for unit testing, not something you'd ever use in production.
Then when you wire up the DependencyResolver in your test, you provide your lifetime scope provider:
var lsProvider = new TestLifetimeScopeProvider(container);
var resolver = new AutofacDependencyResolver(container, lsProvider);
DependencyResolver.SetResolver(resolver);
This lets you use InstancePerHttpRequest and such inside unit tests without actually having a real request context. It also means you should be able to use the request lifetime scope in your registration/resolution method and not have to fall back on the application container.
For those who are searching for ASP.NET WebApi:
You can use GetRequestLifetimeScope() method of AutofacWebApiDependencyResolver.
By default, the solution generated from Sharp Architecture's templify package configures NHibernate using an NHibernate.config file in the {SolutionName}.Web project. I would like to replace it with a fluent configuration of my own and still have the rest of Sharp Architecture work correctly.
Any help will be much appreciated. :)
Solution: Here's how I got it to work:
IPersistenceConfigurer configurer = OracleClientConfiguration.Oracle10
.AdoNetBatchSize(500)
.ShowSql()
.ConnectionString(c => c.FromConnectionStringWithKey("NHibernate.Localhost"))
.DefaultSchema("MySchema")
.ProxyFactoryFactory("NHibernate.ByteCode.Castle.ProxyFactoryFactory, NHibernate.ByteCode.Castle")
.UseReflectionOptimizer();
NHibernateSession.Init(
webSessionStorage,
new string[] { Server.MapPath("~/bin/MyProject.Data.dll") },
new AutoPersistenceModelGenerator().Generate(),
null,
null,
null,
configurer);
iirc the NhibernateSession class that is used to configure nhibernate has a bunch of overloads one of them giving you the ability to configure it via code.
Very old post. I'll leave it here in case someone else is interested. On SharpArch 1.9.6.0 you can add two methods to NHibernateSession.cs. This will let you pass-in a FluentConfiguration object.
public static FluentConfiguration Init(ISessionStorage storage, FluentConfiguration fluentConfiguration)
{
InitStorage(storage);
try
{
return AddConfiguration(DefaultFactoryKey, fluentConfiguration);
}
catch
{
// If this NHibernate config throws an exception, null the Storage reference so
// the config can be corrected without having to restart the web application.
Storage = null;
throw;
}
}
private static FluentConfiguration AddConfiguration(string defaultFactoryKey, FluentConfiguration fluentConfiguration)
{
var sessionFactory = fluentConfiguration.BuildSessionFactory();
Check.Require(!sessionFactories.ContainsKey(defaultFactoryKey),
"A session factory has already been configured with the key of " + defaultFactoryKey);
sessionFactories.Add(defaultFactoryKey, sessionFactory);
return fluentConfiguration;
}