In the startup.cs we have this:
public void ConfigureServices(IServiceCollection services)
{
services.AddLocalization(options => options.ResourcesPath = "Resources");
}
And so our Index Razor page can have this:
public IndexModel(IStringLocalizer<Strings> localizer) {
I want to wrap the localizer in another class and replace it's presence in the IoC container with a singleton of the wrapper.
The problem is that it does not appear possible to retrieve or remove entries from the container while in the COnfigureServices method.
Essentially, I want to replace the registered instance provided by the AddLocalization call so that I don't need to replace every instance of the wrapper class's injection in the solution.
Is this possible?
You can solve this problem with Decorator pattern.
First of all configure the services so that you can access the StringLocalizer<> directly. This is for the MyLocalizer<> class, because it needs a direct instance of StringLocalizer<> type, not the interface (IStringLocalizer<>). If you don't register it MyLocalizer<> would not get resolved.
services.AddTransient(typeof(StringLocalizer<>));
Then register the decorator (the dependency that you want to be replaced). Note that I assumed AddLocalization() is called before this line of code. This is the rule of the DI container; it always resolves the last registered type. So, after this line of code all dependents of IStringLocalizer<> will get MyLocalizer<> instead of the standard StringLocalizer<>.
// be careful about using Singleton scope
services.AddSingleton(typeof(IStringLocalizer<>), typeof(MyLocalizer<>));
Decorator Implementation:
Decorator pattern allows you to add extra features to an existing object. Suppose the IStringLocalizer<T> object returns a simple string that I need to just make it upper-case.
public class MyLocalizer<T> : IStringLocalizer<T>
{
public MyLocalizer(StringLocalizer<T> original)
{
_original = original;
}
private readonly StringLocalizer<T> _original;
// the decorator behavior is the same for all other methods.
// But for this particular method it adds a little feature to the original one! Beautiful :)
public LocalizedString this[string name] =>
new LocalizedString(name, _original[name].Value.ToUpper());
public LocalizedString this[string name, params object[] arguments] =>
_original[name, arguments];
public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures) =>
_original.GetAllStrings(includeParentCultures);
public IStringLocalizer WithCulture(CultureInfo culture) =>
_original.WithCulture(culture);
}
Now, nothing in your dependent classes will change. They just use MyLocalizer<T> instead of MVC's StingLocalizer<T>.
Wish that helps!
Related
I have a Dictionary that will be populated with data from the database at startup, with a method that takes the key as a parameter, and returns the value. How to make the dictionary publicly accessible to all controllers? After searching, I learned that I would need to use Dependency Injection, but I'm failing at implementing it. Any resource that can get me on track is highly appreciated.
There are many ways to implement your question with/without DI. One of which is to write a static class that will be filled upon app startup.
No dependency injection:
Declare a static class that contains your dictionary. By being static there would only be 1 instance on app start.
public static class StaticDictionary {
public Dictionary<string,int> MyDictionary {get;set;}
}
In your Startup.cs - Configure method, append your db context in the parameters.
public void Configure(..., YourDbContext dbContext)
In the Configure method again, append your code that fills the dictionary.
public void Configure(..., YourDbContext dbContext){
...
// no need to modify the code above this, just append the fill dictionary code
foreach(var item in dbContext.TableName.ToList()){
StaticDictionary.MyDictionary.Add(...);
}
}
In your controllers, you could access StaticDictionary without DI.
public IActionResult Index{
var something = StaticDictionary.MyDictionary["Something"];
return View();
}
I am relatively new to working with DI containers and have hit a bit of a roadblock.
SimpleInjector has a method with the following signature:
Container.RegisterInitializer<TService>(Action<TService>)
In our code base we do use it like this:
// this is a property injection of the abstract file system
container.RegisterInitializer<IFileSystemInjection>(
fs => fs.FileSystem = new FileSystem());
I am wondering how I would achieve the same using the IServiceCollection parameter in the ConfigureServices method in the Startup.cs class. So far I have been able to register all my types using the services.AddTransient(); but I am not sure how what the equivalent simpleinjector.RegisterInitializer is within the IServiceCollection.
You'd use the factory overload(s) of AddSingleton, AddScoped, and AddTransient. I'm not sure what scope IFileSystemInjection should be in, but it sounds like something that could be a singleton. If not, change the method you call appropriately:
service.AddSingleton<IFileSystemInjection>(p =>
{
var fs = new FileSystemInjection();
fs.FileSystem = new FileSystem();
});
In short, if you provide a factory, then you're responsible for the entire object initialization, hence the new FileSystemInjection(), which I'm subbing as the actual implementation of IFileSystemInjection your using.
If that implementation has dependencies that need to be injected in order to create it, you can pull those from p, which is an instance of IServiceProvider:
var myDep = p.GetRequiredService<MyDep>();
var fs = new FileSystemImplementation(myDep);
You can use this nuget package, that extends standard Microsoft Dependency Injection and adds property injection:
https://www.nuget.org/packages/DJMJ.Extensions.DependencyInjection.Property/1.1.0
Mark property for injection
using Microsoft.Extensions.DependencyInjection;
public class FooService
{
[Inject]
public IBooService BooService { get; set; }
public void Foo()
{
// just start using injected property
BooService...
}
}
Add services scan method in ConfigureServices
using Microsoft.Extensions.DependencyInjection;
...
host.ConfigureServices((services)=>
{
services.AddTransient<IBooService, BooService>();
services.AddTransient<IFooService, FooService>();
// scan method
services.AddPropertyInjectedServices();
});
If you using this extension in asp net and want add property injection support in controllers too, you should add in ConfigureServices this statement:
services.AddControllers().AddControllersAsServices()
Is it possible to use IServiceProvider inside IServiceCollection.Configure()?
I see no overload on Configure() to accept something like Func<IServiceProvider, T>. Other extension methods, like IServiceCollection.AddScoped() have an overload which accepts Func<IServiceProvider, T>.
I would like to do something like this:
public static void AddCommandHandlers(this IServiceCollection services, Assembly assembly)
{
// CommandExecutor has a dependency on CommandHandlerFactory
services.AddScoped<CommandExecutor>();
services.Configure<CommandHandlerFactory>(myFactory =>
{
// How can I use IServiceProvider here? Example scenario:
foreach(Type t in FindHandlers(assembly))
myFactory.AddHandler(serviceProvider => serviceProvider.GetService(t));
});
}
The goal is to be able to call AddCommandHandlers extension method multiple times, for different assemblies, and append found handlers (using DI) to the same CommandHandlerFactory, so that CommandExecutor can just call the factory to obtain a handler.
Or maybe there is another way?
Any help appreciated.
Thanks.
You can call the BuildServiceProvider() extension method on the IServiceCollection at any time to build a ServiceProvider. You'll need
Microsoft.Extensions.DependencyInjection
It will obviously only include any services which have already been added to the collection so you'll need to call things in the correct order.
IServiceProvider sp = services.BuildServiceProvider();
You could register each command handler against a common interface:
foreach(Type t in FindHandlers(assembly))
services.AddScoped<ICommandHandler>(t);
Then you could make the factory accept IEnumerable<ICommandHandler> as a constructor parameter.
public class CommandHandlerFactory
{
public CommandHandlerFactory(IEnumerable<ICommandHandler> handlers)
{
foreach(var handler in handlers)
AddHandler(handler);
}
// The rest of the factory
}
Or, if you can't change the constructor, you could setup the factory like this:
services.AddSingleton(serviceProvider =>
{
var factory = new CommandHandlerFactory();
foreach(var handler in serviceProvider.GetServices<ICommandHandler>();
factory.AddHandler(handler);
return factory;
});
I've implemented Unit of Work/Repository pattern, as described here, but I'm also using autofac and constructor injection, so I registered UnitOfWork and DbContext (PsyProfContext) class like this:
builder.Register(context => new PsyProfContext()).InstancePerHttpRequest();
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerHttpRequest();
And everything works great!
Except for one thing: I'm also using enterprise library logging block, and I have implemented CustomTraceListener which is using Entity Framework to write log entry into the database.
My controller looks like this (it is empty because at the moment I just tried to verify if all the things (IoC, logging, entity framework) are working):
public class HomeController : Controller
{
private readonly UnitOfWork unitOfWork;
public HomeController(IUnitOfWork unitOfWork)
{
this.unitOfWork = (UnitOfWork) unitOfWork;
}
//
// GET: /Home/
public ActionResult Index()
{
throw new HttpException();
return View();
}
protected override void Dispose(bool disposing)
{
unitOfWork.Dispose();
base.Dispose(disposing);
}
}
And in the Write method of the CustomTraceListener class, I've tried to Resolve UnitOfWork:
DependencyResolver.Current.GetService<IUnitOfWork>() as UnitOfWork;
But I get an instance which is already disposed! so I've put some breakpoints and found out that Dispose method of the controller is called before the Write method of the CustomTraceListener class, so in the end I didn't found other solution than using DbContext (PsyProfContext) directly:
public override void Write(object o)
{
using (var conext = new PsyProfContext())
{
var customLogEntry = o as CustomLogEntry;
if (customLogEntry != null)
{
var logEntry = new LogEntry
{
//a bunch of properties
};
conext.Exceptions.Add(logEntry);
conext.SaveChanges();
}
}
}
But I don't like this solution! What's the point to use UnitOfWork and Repository pattern if you access DbContext object directly. Or what's the point in using DI in project if you create a registered object manually in some cases.
So I wanted to hear your opinion, about how to deal with this kind of situations? Is my current implementation fine, or it is definitely wrong and I should think about another one.
Any help will be greatly appreciated and any ideas are welcome!
It looks like you may have a couple of problems.
First, if you're manually disposing the unit of work object in your controller, your controller should take an Owned<IUnitOfWork> in the constructor. When the request lifetime is disposed it will automatically dispose of any IDisposable components - including the controller and any resolved dependencies - unless you specify somehow that you're going to take over ownership of the lifetime. You can do that by using Owned<T>.
public class HomeController : Controller
{
Owned<IUnitOfWork> _uow;
public HomeController(Owned<IUnitOfWork> uow)
{
this._uow = uow;
}
protected override void Dispose(bool disposing)
{
if(disposing)
{
this._uow.Dispose();
}
base.Dispose(disposing);
}
}
(Note a minor logic fix in the Dispose override there - you need to check the value of disposing so you don't double-dispose your unit of work.)
Alternatively, you could register your units of work as ExternallyOwned, like
builder
.RegisterType<UnitOfWork>()
.As<IUnitOfWork>()
.ExternallyOwned()
.InstancePerHttpRequest();
ExternallyOwned also tells Autofac that you'll take control of disposal. In that case, your controller will look like it does already. (Generally I like to just let Autofac do the work, though, and not take ownership if I can avoid it.)
In fact, looking at the way things are set up, you might be able to avoid the disposal problem altogether if you let Autofac do the disposal for you - the call to DependencyResolver would return the unit of work that isn't disposed yet and it'd be OK.
If that doesn't fix it... you may want to add some detail to your question. I see where your controller is using the unit of work class, but I don't see where it logs anything, nor do I see anything in the listener implementation that's using the unit of work.
(Also, as noted in the first comment on your question, in the constructor of your controller you shouldn't be casting your service from IUnitOfWork to UnitOfWork - that's breaking the abstraction that the interface was offering in the first place.)
here is my problem: I'm building a desktop application, with the following tools:
Caliburn
Ninject
NHibernate
All my view models and repositories are instanciated with Ninject. My repositories all need an ISession in their constructor.
I'd like to follow ayende's advice concerning the ViewModels: each ViewModel opens a new session.
Is it possible to configure Ninject to open a new session when a ViewModel is created, and use this session inside the repositories used by this view model?
I had a look to the InScope function of Ninject, as well as the ICurrentSessionContext interface in NHibernate, but I don't know how to model all of that to get what I want...
Did someone make something like that before?
Thanks in advance
Mike
I solved a similar scenario leveraging the ViewModel lifecycle: I created an ISessionAware interface (with a SetSession method) to be implemented by repositories, then I initialized the repositories through ISessionAware in the OnInitialize method of the ViewModel (which is enforced by Caliburn when the VM is managed by a ScreenConductor).
Using reflection to inspect the properties holding the repositories, I could put all the infrastructure on a BaseDataVM class.
Using a scope in the container would be more elegant, I think, but I don't know Ninject.
I have a very similar project (except I'm not using Caliburn) and have been trying to figure out how to do this as well. I did come up with one method that works well for constructor injection using Ninject's InScope() method.
I have a static class called IoC that wraps access to Ninject's kernel. Since the dependencies are all injected into the constructor, the context is only relevant when the object is being created. So it doesn't matter what is supplied for context, but a Guid feels like the safe choice. Program.OpenSession() is a static method to open a new ISession.
public static class Ioc
{
private static readonly IKernel _kernel;
static IoC()
{
_kernel = new StandardKernel();
_kernel.Load(new ContextModule());
}
private static object _context;
public static T ResolveInContext<T>(object context)
{
_context = context;
var result = _kernel.Get<T>();
_context = null;
return result;
}
private class ContextModule : NinjectModule
{
public override void Load()
{
Bind<ISession>().ToMethod(x => Program.OpenSession()).InScope(x => _context);
Bind<frmCompanyViewer>().ToSelf().InScope(x => _context);
}
}
}
Usage is:
var frm = IoC.ResolveInContext<frmCompanyViewer>(Guid.NewGuid());
The form's constructor signature is:
public frmCompanyViewer(ISession session, ICompanyRepository companyRepository)
I verified that with InScope on the bindings, the same ISession that is used to construct frmCompanyViewer is also used to construct companyRepository. If I remove InScope then two ISessions are used.
Edited to add: This would also work, see comments. This should be made thread safe for a real application. I changed the method name to ConstructInContext to clarify that the context only applies during object construction.
public static T ConstructInContext<T>()
{
_context = Guid.NewGuid();
var result = _kernel.Get<T>();
_context = null;
return result;
}
We have this with AOP, in unhaddins.
Is called "Conversation per Business Transaction".
search in google
here you have ;)
http://groups.google.com/group/unhaddins/browse_thread/thread/29eca74a83df5faf/d9fab4062d4cb4c4?lnk=gst&q=ninject#d9fab4062d4cb4c4
Well, I've found a solution thanks to the ninject group.
The solution here is to use the function InScope when I bind ISession, and browse in the IContext variable to inspect the services. If one service in the request hierarchy is assignable to the base class of my view models, I use the context as scope.
So the first time an ISession will be injected in the constructor of my ViewModel, a new scope is used. And all subsequent calls to ISession inside the constructor of the ViewModel will be resolved with the same scope. And then only one session is created for my ViewModel.
Here is the code:
Bind<ISession>().ToMethod(ctx =>
{
var session = ctx.Kernel.Get<INHibernateSessionFactoryBuilder>()
.GetSessionFactory()
.OpenSession();
session.FlushMode = FlushMode.Commit;
return session;
})
.InScope(ctx =>
{
var request = ctx.Request;
if (request.Service is IScreen)
return request;
while ((request = request.ParentRequest) != null)
if (typeof(IScreen).IsAssignableFrom(request.Service))
return request;
return new object();
});
And the constructor of the viewmodel must contains all the injected dependencies which rely on the ISession:
[Inject]
public PlayersManagementViewModel(ISession session, IPlayersRepository playersRepository)
{
}
Hope that helps