How to implement outbox in multi-tenant environment with separated databases (EF + SqlServer)? - multi-tenant

I have very simillar problem as in this StackOverflow question
We have multi-tenant environment with significant number of tenants. Each of them has physically separated database (with different connection string). Solution proposed in above SO question is not optimal in our case because for large num of tenants (for example 100) it will require to have 200 additional BackgroundServices.
In our project we use EntityFramework Core 7 and SQL server.
To outline our context a bit: connection string is stored within TenantContext class which is registered in DI.
public class TenantContext
{
public string CurrentTenantId { get; set; } = "no-tenant";
public string? ConnectionStr { get; set; }
}
TenantContext is set while creation of IServiceScope specific for tenant.
internal class TenantServiceScopeFactory : ITenantServiceScopeFactory
{
private readonly IServiceScopeFactory _serviceScopeFactory;
public TenantServiceScopeFactory(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public IServiceScope CreateScope(string tenantId)
{
var scope = _serviceScopeFactory.CreateScope();
var tenantContext = scope.ServiceProvider.GetRequiredService<TenantContext>();
tenantContext.CurrentTenantId = tenantId;
return scope;
}
}
DbContext is registered with factory method based on connection string present in TenantContext.
builder.Services.AddDbContext<AppDbContext>((sp, optionsBuilder) =>
{
var tenantContext = sp.GetRequiredService<TenantContext>();
optionsBuilder.UseSqlServer(tenantContext.ConnectionStr);
});
That means that if we want to do anything for specific tenant, we need to create IServiceScope with TenantContext set to specific tenant, and based on that DbContext is created.
If we add MassTransit outbox feature
configurator.AddEntityFrameworkOutbox<AppDbContext>(outboxConfigurator => { … });
then it will do his job for no-tenant TenantContext which always will throw exception because there is no connection string for no-tenant database.
We would like to have single outbox background job (BackgroundService) for all those tenants with logic like this:
protected override async Task ExecuteAsync(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
var tenants = new[]
{
"tenant1",
"tenant2"
};
foreach (var tenant in tenants)
{
var tenantScope = _tenantServiceScopeFactory.CreateScope(tenant);
// do MassTransit outbox delivery based on AppDbContext resolved within tenantScope
}
await Task.Delay(Interval, cancellationToken);
}
}
Is it even achievable in current shape of MassTransit?
Tried solution proposed in: Is it possible to use MassTransit Transactional Outbox in a Multi-Tenant per DB architecture?

If the only issue you're experiencing is the need to have a delivery service for each tenant, you should be able to copy the existing delivery service code and modify it so that it delivers for all of your tenants. This isn't something that would be built into MassTransit. Consulting is available if you need help or would like this built for you in your application.

Related

How to only audit [Audited] methods without having [DisabledAudited] everywhere?

My Audit logs are getting out of hand so I decided I want to only audit all requests which basically are not a Get request. Is there a very simply way to do this from configuration?
The documentation here:
https://aspnetboilerplate.com/Pages/Documents/Audit-Logging
Says:
Note: In addition to the standard audit configuration, MVC and ASP.NET
Core modules define configurations to enable/disable audit logging for
actions.
But I could not find more information about what exactly this means.
As a last resort, I know it would work if I went to every class and added [DisableAuditing] and then [Audited] on the non-Get endpoints, but that seems a bit messy.
Best soltuion: I just want to have a simply way to select only non-GET requests and audit them.
Second best solution:
I just want to have only [Audited] methods audited. I don't want to have to go and write [DisabledAuditing] on every class.
You can create an AuditStore to do that, and then replace the original AuditStore in service YourAplicationNameCoreModule
Here is the example
public class YourAuditStore : AuditingStore
{
public ILogger<AuditingStore> Logger { get; set; }
private readonly IRepository<AuditLog, long> _auditLogRepository;
private readonly ISettingManager _settingManager;
public YourAuditStore(IRepository<AuditLog, long> auditLogRepository, ISettingManager settingManager) : base(auditLogRepository)
{
_auditLogRepository = auditLogRepository;
_settingManager = settingManager;
}
public override async Task SaveAsync(AuditInfo auditInfo)
{
AuditLog auditLog = new AuditLog();
bool logErrorsOnly = await _settingManager.GetSettingValueAsync<bool>(AppSettings.Logging.LogOnErrorsOnly);
var exceptionMessage = auditInfo.Exception != null ? auditInfo.Exception.ToString() : null;
if ((logErrorsOnly && exceptionMessage != null) || !logErrorsOnly)
{
auditLog = await _auditLogRepository.InsertAsync(AuditLog.CreateFromAuditInfo(auditInfo));
}
}
}
As you can see, you can filter whatever you want in SaveAsync method as it recieve the AuditInfo, you can check if method is different to Get then save
Add the next code to YourApplicationNameCoreModule on PreInitialize method
public override void PreInitialize()
{
Configuration.ReplaceService<IAuditingStore, YourAuditStore>();
}

How can we achieve multi-tenant option in the Spring scheduler?

we have implemented multi-tenant option in our application. Each tenant have each separate DB. using application filter i can manage or assign the each tenant from the request. same how can we do it in the spring boot scheduler?
#component
public class scheduler{
#Scheduled(fixedRate = 5000)
public void reminderEmail() {
//how can we fetch the exact data from exact tenant DB?
//since there is no request how can we get the tenant name for
fetching exact tenant db?
}
}
Please let me know how can we achieve this?
Something like:
...
public class TenantContext {
private static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();
public static void setTenantId(String tenantId) {
CONTEXT.set(tenantId);
}
public static String getTenantId() {
return CONTEXT.get();
}
...
}
then your Filter or Spring MVC interceptor could do this just before chaining the request:
String tenantId = request.getHeader(TENANT_HEADER_NAME);
TenantContext.setTenantId(tenantId);
and reset it on the way back:
TenantContext.setTenantId(null);
To use it in a thread not related to an http request you could just do:
TenantContext.setTenantId("tenant_1");
More could be found in my blog post Multi-tenant applications using Spring Boot, JPA, Hibernate and Postgres
If you are using a multitenant setup similar to the one at this link: https://www.ricston.com/blog/multitenancy-jpa-spring-hibernate-part-1/ and/or you have a default tenant. The easiest way to accomplish this is to add a static method to your CurrentTenantIdentifierResolverImpl class that changes the default tenant for asynchronous tasks that have no session. This is because that the scheduled task will always use the default tenant.
CurrentTenantIdentifierResolverImpl.java
private static String DEFAULT_TENANTID = "tenantId1";
public static void setDefaultTenantForScheduledTasks(String tenant) {
DEFAULT_TENANT = tenant;
}
ScheduledTask.java
#Scheduled(fixedRate=20000)
public void runTasks() {
CurrentTenantIdentifierResolverImpl.setDefaultTenantForScheduledTasks("tenantId2");
//do something
CurrentTenantIdentifierResolverImpl.setDefaultTenantForScheduledTasks("tenantId1");
}
Then after the scheduled task is complete change it back. That is how we accomplished it and it works for our needs.
If you're using a request to determine which tenant is currently active and using tenant to determine database connections, then it's impossible to do anything involving the database from a scheduled task since the scheduled task has no tenant id

Where can I load the user information to the session in ASP.NET MVC 5 with windows authentication?

I want to use the ASP.NET MVC 5 for my web app. I need use the windows authentication.
If I use the windows authentication where is the best place for reading user information (userid and roles) and store its to the Session?
I have the method for getting the user information by username from the database like this:
public class CurrentUser
{
public int UserId { get; set; }
public string UserName { get; set; }
public Roles Roles { get; set; }
}
public enum Roles
{
Administrator,
Editor,
Reader
}
public class AuthService
{
public CurrentUser GetUserInfo(string userName)
{
var currentUser = new CurrentUser();
//load from DB
return currentUser;
}
}
You've asked two questions (1) the best place to obtain user information and (2) how to store it in the Session. I'll answer (1) and in so doing perhaps show that you need not put any additional information in the session.
You've stated that your application is using Windows Authentication, so that means the hard work of authenticating the user has already been done by IIS/HttpListener before your app receives the request. When you receive the request there will be a WindowsPrincipal in HttpContext.User. This will have the windows username and AD roles already established, but you wish to use additional roles stored in the database...
You could access your AuthService from anywhere in your application, but probably the best approach is to register an IAuthorizationFilter and do the work there. By following this approach, the additional roles and other information you fetch from the database will be available in your controller methods and, perhaps more importantly, from any additional library code that needs to check user credentials.
Prior to .Net 4.5, if you wanted to add additional information to the WindowsPrincipal I think your only choice was to replace the system-provided User with another object that implemented the IPrincipal interface. This approach is still available (and what I recommend), but since the introduction of Windows Identity Foundation (WIF) in .Net 4.5, WindowsPrincipal is derived from  System.Security.Claims.ClaimsIdentityClaimsIdentity, which supports adding additional roles (and other useful information) to the system-provided principal. However, as several people have found, there is a bug/feature in Windows which can cause an exception The trust relationship between the primary domain and the trusted domain failed to be thrown when checking roles that have been added programmatically. We have found that a simple and reliable way to avoid this is to replace the User with a GenericPrincipal.
Steps required:
(1) create an IAuthorizationFilter.
class MyAuthorizationFilter : IAuthorizationFilter
{
AuthService _authService;
public MyAuthorizationFilter(AuthService authService)
{
_authService = authService;
}
public void OnAuthorization(AuthorizationContext filterContext)
{
var principal = filterContext.HttpContext.User;
if (principal.Identity != null && principal.Identity.IsAuthenticated)
{
// Add username (and other details) to session if you have a need
filterContext.HttpContext.Session["User"] = principal.Identity.Name;
// get user info from DB and embue the identity with additional attributes
var user = _authService.GetUserInfo(principal.Identity.Name);
// Create a new Principal and add the roles belonging to the user
GenericPrincipal gp = new GenericPrincipal(principal.Identity, user.RoleNames.ToArray());
filterContext.HttpContext.User = gp;
}
}
}
(2) Register your filter. This can be registered at the controller level or globally. Typically you will do this in App_Start\FilterConfig.cs:
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new MyAuthorizationFilter(new AuthService()));
}
}
(3) Use the provided GenericPrincipal in your application code to answer questions about the user identification and other credentials. e.g. in your controller method you can access the username or any other "claims" (e.g. email address) stored in the GenericPrincipal by your filter.
public ActionResult Index()
{
ViewBag.Name = HttpContext.User.Identity.Name;
if(HttpContext.User.IsInRole("Administrator"))
{
// some role-specific action
}
return View();
}
Because you've used the built-in mechanism to record Principal roles, you can access user details from anywhere using HttpContext.User or System.Threading.Thread.CurrentPrincipal. Also you can use the AuthorizeAttribute in you controller methods to declare which actions are available to certain roles or users. e.g.
public class HomeController : Controller
{
[Authorize(Roles = "Administrator")]
public ActionResult Admin()
{
return View();
}
See MSDN for further details about ClaimsIdentity
I hope this helps
-Rob
First and foremost: never, never, never store user details in the session. Seriously. Just don't do it.
If you're using Windows Auth, the user is in AD. You have use AD to get the user information. Microsoft has an MSDN article describing how this should be done.
The long and short is that you create a subclass of UserIdentity and extend it with the additional properties you want to return on the user:
[DirectoryRdnPrefix("CN")]
[DirectoryObjectClass("inetOrgPerson")]
public class InetOrgPerson : UserPrincipal
{
// Inplement the constructor using the base class constructor.
public InetOrgPerson(PrincipalContext context) : base(context)
{
}
// Implement the constructor with initialization parameters.
public InetOrgPerson(PrincipalContext context,
string samAccountName,
string password,
bool enabled)
: base(context,
samAccountName,
password,
enabled)
{
}
InetOrgPersonSearchFilter searchFilter;
new public InetOrgPersonSearchFilter AdvancedSearchFilter
{
get
{
if ( null == searchFilter )
searchFilter = new InetOrgPersonSearchFilter(this);
return searchFilter;
}
}
// Create the mobile phone property.
[DirectoryProperty("mobile")]
public string MobilePhone
{
get
{
if (ExtensionGet("mobile").Length != 1)
return null;
return (string)ExtensionGet("mobile")[0];
}
set
{
ExtensionSet( "mobile", value );
}
}
...
}
In the example code above, a property is added to bind to the AD's user's mobile field. This is done by implementing the property as shown utilizing ExtensionSet, and then annotating the property with the DirectoryProperty attribute to tell it what field it binds to.
The DirectoryRdnPrefix and DirectoryObjectClass attributes on the class need to line up with how your AD is set up.
Once this is implemented, then you will be able to get at the values simply by referencing them off User.Identity. For example, User.Identity.MobilePhone would return the mobile field from AD for the user.

Is it possible to have multiple dependency resolvers in ASP.NET MVC 3?

Is it possible to have more than one dependency resolver in ASP.NET MVC 3 (similar to the case of ModelBinders and Providers)?
There is one scenario that I could think of where having multiple 'containers' or 'resolvers' is useful and that is multi-tenancy. With multi tenancy you run multiple customers (organizations, with their own set of users) in the same web application, and dynamically switch based on the login, request info, or domain info.
Still, DependencyResolver.Current is -as Darin noted- static, so there's nothing you can (or should do about this). However, you could hide multiple containers behind a single IDependencyResolver abstraction and return an implementation based on some criteria. It might look like this:
public class MultiTenantDependencyResolver
: IDependencyResolver
{
Func<int> tenantIdSelector,;
IDictionary<int, IDependencyResolver> tenantResolvers;
public MultiTenantDependencyResolver(
Func<int> tenantIdSelector,
IDictionary<int, IDependencyResolver> tenantResolvers)
{
this.tenantIdSelector = tenantIdSelector;
this.tenantResolvers= tenantResolvers;
}
private IDependencyResolver CurrentResolver
{
get { return this.tenantResolvers[tenantIdSelector()]; }
}
public object GetService(Type serviceType)
{
return this.CurrentResolver.GetService(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return this.CurrentResolver.GetAllInstances(serviceType);
}
}
The following code snippet shows the usage of this MultiTenantDependencyResolver:
var tenantResolvers = new Dictionary<int, IDependencyResolver>
{
{ Tenants.AbcId, BuildResolver(RegisterForTenantAbc) },
{ Tenants.KlmId, BuildResolver(RegisterForTenantKlm) },
{ Tenants.XyzId, BuildResolver(RegisterForTenantXyz) },
};
var multiTenantResolver = new MultiTenantResolver(
() => GetTenantIdFromUrl(), tenantResolvers);
DependencyResolver.SetResolver(multiTenantResolver);
private static int GetTenantIdFromUrl()
{
// TODO: return tenant id
}
private static IDependencyResolver BuildResolver(
Action<IKernel> tenantSpecificRegistrations)
{
var kernel = new Kernel();
// TODO: Tenant agnostic registrations. For instance
kernel.Bind<ITimeProvider>().To<SystemTimeProvider>();
tenantSpecificRegistrations(kernel);
return new NinjectDependencyResolver(kernel);
}
private static void RegisterForTenantAbc(IKernel kernel)
{
// TODO: regisrations for ABC tenant. For instance
kernel.Bind<ILogger>().To<AbcTenantLogger>();
}
Is it possible to have more than one dependency resolver in ASP.NET
MVC 3 (similar to the case of ModelBinders and Providers)?
No, this isn't possible. The DependencyResolver.Current is a static property that could be assigned only one resolver. This being said having more than one dependency resolver in an application hardly makes any sense. The idea is that all your dependencies are managed by a dependency injection framework such as Unity, Ninject or StructureMap. You would then have a custom dependency resolver wrapping your DI framework of choice that will be used by ASP.NET MVC to inject dependencies in various objects of the execution pipeline.
You are comparing it with model binders in your question but this comparison is unfair because a model binder is related to a specific type that it is designed to bind. Basically you could have many custom model binders for multiple view models.
You also seem to have mentioned some providers in your question but unfortunately you haven't beeen very specific so it's a bit harder to comment on this one.

EF and repository pattern - ending up with multiple DbContexts in one controller - any issues (performance, data integrity)?

Most of my knowledge of ASP.NET MVC 3 comes from reading through the book Pro ASP.NET MVC 3 Framework by Adam Freeman and Steven Senderson. For my test application I have tried to stick to their examples very closely. I am using the repository pattern plus Ninject and Moq which means that unit testing work quite well (i.e. without needing to pull data from the database).
In the book repositories are used like this:
public class EFDbTestChildRepository
{
private EFDbContext context = new EFDbContext();
public IQueryable<TestChild> TestChildren
{
get { return context.TestChildren; }
}
public void SaveTestChild(TestChild testChild)
{
if (testChild.TestChildID == 0)
{
context.TestChildren.Add(testChild);
}
else
{
context.Entry(testChild).State = EntityState.Modified;
}
context.SaveChanges();
}
}
And here is the DbContext that goes with it:
public class EFDbContext : DbContext
{
public DbSet<TestParent> TestParents { get; set; }
public DbSet<TestChild> TestChildren { get; set; }
}
Please note: to keep things simple in this extracted example I have left out the interface ITestChildRepository here which Ninject would then use.
In other sources I have seen a more general approach for the repository where one single repository is enough for the whole application. Obviously in my case I end up with quite a list of repositories in my application - basically one for each entity in my domain model. Not sure about the pros and cons about the two approaches - I just followed the book to be on the safe side.
To finally get to my question: each repository has its own DbContext - private EFDbContext context = new EFDbContext();. Do I risk ending up with multiple DbContexts within one request? And would that lead to any significant performance overhead? How about a potential for conflicts between the contexts and any consequences to the data integrity?
Here is an example where I ended up with more than one repository within a controller.
My two database tables are linked with a foreign key relationship. My domain model classes:
public class TestParent
{
public int TestParentID { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
public virtual ICollection<TestChild> TestChildren { get; set; }
}
public class TestChild
{
public int TestChildID { get; set; }
public int TestParentID { get; set; }
public string Name { get; set; }
public string Comment { get; set; }
public virtual TestParent TestParent { get; set; }
}
The web application contains a page that allows the user to create a new TestChild. On it there is a selectbox that contains a list of available TestParents to pick from. This is what my controller looks like:
public class ChildController : Controller
{
private EFDbTestParentRepository testParentRepository = new EFDbTestParentRepository();
private EFDbTestChildRepository testChildRepository = new EFDbTestChildRepository();
public ActionResult List()
{
return View(testChildRepository.TestChildren);
}
public ViewResult Edit(int testChildID)
{
ChildViewModel cvm = new ChildViewModel();
cvm.TestChild = testChildRepository.TestChildren.First(tc => tc.TestChildID == testChildID);
cvm.TestParents = testParentRepository.TestParents;
return View(cvm);
}
public ViewResult Create()
{
ChildViewModel cvm = new ChildViewModel();
cvm.TestChild = new TestChild();
cvm.TestParents = testParentRepository.TestParents;
return View("Edit", cvm);
}
[HttpPost]
public ActionResult Edit(TestChild testChild)
{
try
{
if (ModelState.IsValid)
{
testChildRepository.SaveTestChild(testChild);
TempData["message"] = string.Format("Changes to test child have been saved: {0} (ID = {1})",
testChild.Name,
testChild.TestChildID);
return RedirectToAction("List");
}
}
catch (DataException)
{
//Log the error (add a variable name after DataException)
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists see your system administrator.");
}
// something wrong with the data values
return View(testChild);
}
}
It's not enough to have an EFDbTestChildRepository available but I also need an EFDbTestParentRepository. Both of them are assigned to private variables of the controller - and voila, it seems to me that two DbContexts have been created. Or is that not correct?
To avoid the issue I tried using EFDbTestChildRepository to get to the TestParents. But that obviously will only bring up those that are already hooked up to at least one TestChild - so not what I want.
Here is the code for the view model:
public class ChildViewModel
{
public TestChild TestChild { get; set; }
public IQueryable<TestParent> TestParents { get; set; }
}
Please let me know if I forgot to include some relevant code. Thanks so much for your advice!
There won't be a performance problem (unless we are talking about nanoseconds, instantiating a context is very cheap) and you won't have damaged your data integrity (before that happens you'll get exceptions).
But the approach is very limited and will work only in very simple situations. Multiple contexts will lead to problems in many scenarios. As an example: Suppose you want to create a new child for an existing parent and would try it with the following code:
var parent = parentRepo.TestParents.Single(p => p.Id == 1);
var child = new Child { TestParent = parent };
childrenRepo.SaveTestChild(child);
This simple code won't work because parent is already attached to the context inside of parentRepo but childrenRepo.SaveTestChild will try to attach it to the context inside of childrenRepo which will cause an exception because an entity must not be attached to another context. (Here is actually a workaround because you could set the FK property instead of loading the parent: child.TestParentID = 1. But without a FK property it would be a problem.)
How to solve such a problem?
One approach could be to extend the EFDbTestChildRepository by a new property:
public IQueryable<TestParent> TestParents
{
get { return context.TestParents; }
}
In the example code above you could then use only one repository and the code would work. But as you can see, the name "EFDbTest Child Repository" doesn't really fit anymore to the purpose of the new repository. It should be now "EFDbTest ParentAndChild Repository".
I would call this the Aggregate Root approach which means that you create one repository not for only one entity but for a few entities which are closely related to each other and have navigation properties between them.
An alternative solution is to inject the context into the repositories (instead of creating it in the repositories) to make sure that every repository uses the same context. (The context is often abstracted into a IUnitOfWork interface.) Example:
public class MyController : Controller
{
private readonly MyContext _context;
public MyController()
{
_context = new MyContext();
}
public ActionResult SomeAction(...)
{
var parentRepo = new EFDbTestParentRepository(_context);
var childRepo = new EFDbTestChildRepository(_context);
//...
}
protected override void Dispose(bool disposing)
{
_context.Dispose();
base.Dispose(disposing);
}
}
This gives you a single context per controller you can use in multiple repositories.
The next step might be to create a single context per request by dependency injection, like...
private readonly MyContext _context;
public MyController(MyContext context)
{
_context = context;
}
...and then configuring the IOC container to create a single context instance which gets injected into perhaps multiple controllers.
Do I risk ending up with multiple DbContexts within one request?
Yes. Each instance of a repository is going to instantiate its own DbContexts instances. Depending on the size and use of the application, this may not be a problem although it is not a very scalable approach. There are several ways of handling this though. In my web projects I add the DbContext(s) to the Request's Context.Item collection, this way it is available to all classes that require it. I use Autofac (similar to Ninject) to control what DbContexts are created within specific scenarios and how they are stored, e.g. I have a different 'session manager' for a WCF context to the one for a Http context.
And would that lead to any significant performance overhead?
Yes, but again not massively if the application is relatively small. As it grows though, you may notice the overhead.
How about a potential for conflicts between the contexts and any
consequences to the data integrity?
One of the reasons for using an ORM like this is so that changes can be maintained within the DbContext. If you are instantiating multiple context instances per request you lose this benefit. You wouldn't notice conflicts or any impact of the integrity per se unless you were handling a lot of updates asynchronously.
As promised I post my solution.
I came across your question because I was having trouble with the IIS application pool memory growing beyond limits and having multiple DBContexts was one of my suspects. In retrospect it is fair to say that there were other causes for my trouble. However, it challenged me to find a better layer based design for my repository.
I found this excellent blog: Correct use of Repository and Unit Of Work patterns in ASP.NET MVC leading me to the right direction. The redesign is based on the UnitOfWork pattern. It enables me to have just one constructor parameter for all my controllers instead of "never ending constructor parameters". And after that, I was able to introduce proactive caching as well, which solved a great deal of the earlier mentioned trouble I was having.
Now I only have these classes:
IUnitOfWork
EFUnitOfWork
IGenericRepository
EFGenericRepository
See the referred blog for complete information and implementation of these classes. Just to give an example, IUnitOfWork contains repository definitions for all entities that I need, like:
namespace MyWebApp.Domain.Abstract
{
public interface IUnitOfWork : IDisposable
{
IGenericRepository<AAAAA> AAAAARepository { get; }
IGenericRepository<BBBBB> BBBBBRepository { get; }
IGenericRepository<CCCCC> CCCCCRepository { get; }
IGenericRepository<DDDDD> DDDDDRepository { get; }
// etc.
string Commit();
}
}
The Dependency Injection (DI) is just one statement (I use Ninject):
ninjectKernel.Bind<IUnitOfWork>().To<EFUnitOfWork>();
The Controllers-constructors are maintainable:
public class MyController : BaseController
{
private MyModel mdl = new MyModel();
private IUnitOfWork _context;
public MyController(IUnitOfWork unitOfWork)
{
_context = unitOfWork;
// intialize whatever needs to be exposed to the View:
mdl.whatever = unitOfWork.SomeRepository.AsQueryable();
}
// etc.
Within the Controller I can use _context to access all repositories, if needed. The nice part of it, is that it needs just a single Commit()-call to save changed data for all repositories:
_context.Commit();

Resources