I have a LogContext Model :
using System.Data.Entity;
namespace Logging.Models
{
public class LogContext : DbContext
{
// You can add custom code to this file. Changes will not be overwritten.
//
// If you want Entity Framework to drop and regenerate your database
// automatically whenever you change your model schema, add the following
// code to the Application_Start method in your Global.asax file.
// Note: this will destroy and re-create your database with every model change.
//
// System.Data.Entity.Database.SetInitializer(new System.Data.Entity.DropCreateDatabaseIfModelChanges<Logging.Models.ProductContext>());
public LogContext() : base("name=LogContext")
{
Database.SetInitializer<LogContext>(null);
}
public DbSet<Log> Logs { get; set; }
}
}
but when I try to reference the Logs in my other LogContext class under App_code I'm getting an error trying to reference the context.Logs.Load();
"cannot be accessed with an instance reference; qualify it with a type name"
How do I reference and render all the rows in my table? What am i doing wrong?
Thanks
using System;
using System.Collections.Generic;
using System.Linq;
using Logging.Controllers;
using Logging.Models;
namespace Logging
{
public class LogContext : IDisposable
{
private static readonly List<Log> Logs = new List<Log>();
static LogContext()
{
using (var context = new LogContext())
{
**context.Logs.Load();**
}
//Logs.Add(new Log() { Id = 1, LoggerName = "TESTSYS1", InnerException = "InnerException", LogText = "LogText", ThreadID = 1, StackTrace = "Stack Trace", eLevel = "INFO" });
//Logs.Add(new Log() { Id = 2, LoggerName = "TESTSYS2", InnerException = "InnerException", LogText = "LogText", ThreadID = 2, StackTrace = "Stack Trace", eLevel = "ERROR" });
//Logs.Add(new Log() { Id = 3, LoggerName = "TESTSYS3", InnerException = "InnerException", LogText = "LogText", ThreadID = 3, StackTrace = "Stack Trace", eLevel = "WARN" });
}
void IDisposable.Dispose()
{
}
public void GetLoggies()
{
using (var context = new LogContext())
{
foreach (var log in context.GetLogs())
{
Logs.Add(log);
}
}
}
public Log GetLog(int id)
{
var log = Logs.Find(p => p.Id == id);
return log;
}
public IEnumerable<Log> GetLogs()
{
return LogContext.Logs;
}
public Log AddLog(Log p)
{
Logs.Add(p);
return p;
}
public void Delete(int id)
{
var product = Logs.FirstOrDefault(p => p.Id == id);
if (product != null)
{
Logs.Remove(product);
}
}
public bool Update(int id, Log log)
{
Log rLog = Logs.FirstOrDefault(p => p.Id == id);
if (rLog != null)
{
rLog = log;
return true;
}
return false;
}
}
}
The problem is frankly very bad design.
Your class here has the same name as your context and also has a member with the same name as a member on your context, i.e. Logs. This is a case study in how intelligent the compiler is, in that the only reason the whole thing doesn't explode, is because it's able to make some sense out of which you want in which place, given context. Still, it might guess wrong, and you will certainly get confused at some point. If you insist on maintaining it this way, you should fully-qualify all uses of your actual context class, i.e. new Namespace.To.LogContext(), so the compiler isn't just guessing.
Using using around a context is a hugely bad idea. A context instance should ideally be request-scoped. Among other things, the context employs change tracking, and when you start passing entities between different context instances, you're going to run headlong into a brick wall. Instead, you should inject your context into this class and save it as a field on the class.
Implementing IDisposable is not something you should do lightly. There's a very particular way it needs to be implemented or you're actually causing more harm than good.
public class Base: IDisposable
{
private bool disposed = false;
//Implement IDisposable.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// Free other state (managed objects).
}
// Free your own state (unmanaged objects).
// Set large fields to null.
disposed = true;
}
}
// Use C# destructor syntax for finalization code.
~Base()
{
// Simply call Dispose(false).
Dispose (false);
}
}
See: https://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.100).aspx
However, if you inject your context, this class will no longer own the context, and therefore wouldn't need to even implement IDisposable. And, for the love of everything good and holy, don't implement IDisposable when you're injecting dependencies. I see far too many developers do this and end up with strange bugs because resources are being disposed incorrectly.
Finally, just throw this class away completely. What you're essentially trying to create here (incorrectly) is a repository, and you don't need that. Entity Framework already implements the repository and unit of work patterns. As you can see from your methods here, all you're doing is basically proxying from your method to a nearly equivalent method on the DbSet. You're buying yourself nothing but just an additional layer that now has to be maintained, more entropy for your application code, and technical debt. For a more detail description of why this is the wrong approach see: https://softwareengineering.stackexchange.com/a/220126/65618
Related
I have a controller in a .NET Core application:
public FriendsController(IFriendRepository friendRepository)
{
this.friendRepository= friendRepository;
}
The IFriendRepository is an interface which is implemented with the class:
public class FriendRepository : IFriendRepository {
...
}
In Startup I set it up by using the following line in ConfigureServices() :
services.AddScoped<IFriendRepository , FriendRepository >();
However, when the controller is used, FriendRepository is has the lifetime set as a singleton instead of scoped. The reason I was able to find was on this page:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1
Under Service lifetimes, Scoped. It shows:
I do not understand how to use Invoke instead of a constructor. The example they use is for a custom middleware, which I at least can't wrap my head on how to interpret it for a constructor.
public class FriendRepository : IFriendRepository
{
private readonly ManagementDbContext dbContext;
public FriendRepository(ManagementDbContext dbContext)
{
this.dbContext = dbContext;
}
public void Add(Friend friend)
{
this.dbContext.Friends.Add(friend);
}
public void Remove(Friend friend)
{
this.dbContext.Remove(friend);
}
public void Update(Friend friend)
{
this.dbContext.Update(friend);
}
}
The following is "GetFriends", inside FriendRepository:
public async Task<QueryResult<Friend>> GetFriendsAsync(FriendQuery queryObj)
{
var result = new QueryResult<Friend>();
var query = dbContext.Friends
.Include(c => c.Type)
.AsQueryable();
if(queryObj.TypeId.HasValue)
{
query = query.Where(c => c.Type.Id == queryObj.TypeId);
}
if(queryObj.Name != null && queryObj.Name.Length > 0)
{
query = query.Where(c => c.Name.Contains(queryObj.Name));
}
// todo add total price here
var columnsMap = new Dictionary<string, Expression<Func<Calculation, object>>>()
{
["id"] = c => c.Id,
["name"] = c => c.Name,
["type"] = c => c.Type,
["totalFriends"] = c => c.TotalFriends,
["createdTime"] = c => c.CreatedTime
};
query = query.ApplyOrdering(queryObj, columnsMap);
result.TotalItems = await query.CountAsync();
query = query.ApplyPaging(queryObj);
result.Items = await query.ToListAsync();
return result;
}
I solved it, I will first explain my assumption, since the fix might be very much limited to my scenario.
I have all of my DBContext used in 3 repositories. They all use async functions however they all contain awaits inside for any of the async functions used inside of them.
The issue seemed to only occur once I started using these repositories as before I was accessing the dbContext directly in the Controller. This made me consider the problems in the link, which I also posted a picture of in the question:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.1
Even though it specified middle ware only, I assumed it was worth a chance since I couldn't figure any other problem.
Now as for the actual problem. One of my functions in the UserRepository, GetUser() is an async method, and even though the error seemed to be in the FriendRepository methods, since they were always the ones crashing, it turns out that the GetUser() function was used once in startup under AddJwtBearer without await.
I had assumed that since it had an await inside of it, it would not create a problem. I also had not noticed this was a problem since I was so focused on the other repository. My hope was that maybe I was missing something as simple as the dependency injection through a constructor in middleware switching lifetime regardless of what the lifetime was already set to.
For anyone else in the future, I ended up doing 2 things which allowed me to clearly debug my application step by step.
I created a Logger static class which allows me to save text to file easily. I use this to log functions being used, constructors etc. This let me ensure that I could track the amount of times constructors and functions were called, in what order and which ones would not be reached. Here is the Logger for anyone else:
public static class Logger
{
public static void Log(string text, string fileName)
{
string path = System.IO.Path.GetDirectoryName(Assembly.GetEntryAssembly().Location) + "/" + fileName;
bool done = false;
while (!done)
{
done = true;
try
{
FileStream fileStream = null;
fileStream = System.IO.File.Open(path, System.IO.File.Exists(path) ? FileMode.Append : FileMode.OpenOrCreate);
using (StreamWriter fs = new StreamWriter(fileStream))
{
fs.WriteLine(text);
};
fileStream.Close();
}
catch (IOException)
{
done = false;
}
}
}
public static void Log(string text)
{
Log(text, "logger.txt");
}
}
I added a string to the DBContext and whenever I use it in any function I would add the name of the function after the name of the class it is used in. So if my FriendsRepository would use it in a function GetTypes, it would look like:
myDbContext.methodUsing = "FriendsRepository>GetTypes()";
Thank you to #GuruStron for being patient and giving me advice on how to take this step by step, explaining to me that the middleware error idea had no leg to stand on and suggesting to me on how to approach debugging.
I am facing a little problem related to dependency injection. I have a program, using respectfully state of the art rules of dependency injection, used from example of an ASP.NET MVC project, and some console programs.
But it also contains some kind of "service locator" anti pattern.
I'll attempt to illustrate it with a very simple console project :
using System;
using Autofac;
using System.Collections.Generic;
using System.Linq;
namespace AutofacIssue
{
class Program
{
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterModule<MyModule>();
var container = builder.Build();
using (var scope = container.BeginLifetimeScope())
{
var service = scope.Resolve<UserService>();
var users = service.CreateSomeUsers();
var info = users.First().GetSomeInfo;
Console.WriteLine(info.Something);
}
}
}
internal class MyModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
base.Load(builder);
builder.RegisterType<UserService>();
builder.RegisterType<UserRelatedInfoService>();
}
}
internal class UserRelatedInfoService
{
public UserInfo GetForUser(int id)
{
return new UserInfo("Various infos for " + id);
}
}
internal class UserService
{
public IEnumerable<User> CreateSomeUsers()
{
return Enumerable.Range(1, 10).Select(r => new User(r)); // Remark "new User()" is called from many various spaces !
}
}
internal class UserInfo
{
// Some things
public string Something;
public UserInfo(string someThings)
{
this.Something = someThings;
}
}
/// <summary>
/// "Service locator" antipattern
/// </summary>
class DependencyLocator
{
public static IContainer Container { get; }
static DependencyLocator()
{
var builder = new ContainerBuilder();
builder.RegisterModule<MyModule>();
Container = builder.Build();
}
}
internal class User
{
public User(int id)
{
this.Id = id;
}
public int Id { get; private set; }
/// <summary>
/// Here's my problematic property using the DependencyLocator
/// </summary>
public UserInfo GetSomeInfo
{
get
{
UserRelatedInfoService userRelatedInfoService = DependencyLocator.Container.Resolve<UserRelatedInfoService>();
return userRelatedInfoService.GetForUser(Id);
}
}
}
}
The anti pattern allows to write very small code working perfectly well, but violating some of the principles of DI (due to "service locator" + duplicated Container, having each their own lifetime).
This implementation also have the advantage to instantiate UserRelatedInfoService only when it's actually needed, if the related property of User is actually called (please keep in mind the real world example is much more complicated and some of the operations related to this may have a cost)
In the real world example, I have this situation in many assemblies, each of them needing to be able to resolve dependencies by the same way.
My question is: without to modify the User constructor, and the constructors of all objects instantiating some User, is there a clean way to avoid this?
By some kind of "dynamic resolving" of dependencies for example?
Please note that User is not in the same assembly as my Program class, so I can't access the original Container as a public property.
One solution I thought was to keep the class DependencyLocator but to remove its content, and just assign its Container property with the one created in main.
edit : FYI, so far I just followed my own suggestion and modified DependencyLocator to avoid it to rebuild its own container, and just set on it the final container built at entry point of application. It was an easy change to do and it avoids most of the problems pointed in original question.
At least, the code will always use the same container.
Thanks for reading!
For edge cases like this where you need runtime resolution by type, you can register IServiceProvider or a Func (Or a Func with the object[] being input parameters)
builder.Register(ctx => ctx as IServiceProvider ??
ctx.Resolve<ILifetimeScope>() as IServiceProvider)
.InstancePerLifetimeScope().AsSelf();
or
builder.Register(c =>
{
var scope = c.Resolve<ILifetimeScope>();
return (Func<Type, object>)(t => scope.Resolve(t));
}).As<Func<Type, object>>();
For some background - I'm writing an abstraction of some Entity Framework functionality to make the work of those using the Data Access Layer a little easier. I'm doing this via proxies / wrappers, and I am at the point where I'd like to test the successful use of EF's Include(). (But non of that is relevant for the specific problem here (just want to avoid people suggesting "don't test EF functionality" (I'm delegating the Include method to EF, which is what I'm actually testing)))
Ideally, I'd like to define a block (maybe via using) and have that block count the number of queries that happen within that block.
Using some psuedo-code, here is the behavior I would like:
var user = new User(id);
using(var queryCounter = new QueryCounter()){
user.Books.SelectMany(b => b.Pages);
Assert.Equal(2, queryCounter.NumberOfDetectedQueries);
// the above assert would fail, due to not using the `Include` keyword.
// as the two queries should be to select books and page ids and
// then to actually select the pages
}
Is there a way to achieve something like the above query counting?
UPDATE:
Thanks to #Ilya Chumakov for providing insight in to query interceptors. I've been able to get the syntax in the above example via an additional class:
public class QueryCounter : IDisposable
{
public int Count => GlobalCounter.QueryCount;
public QueryCounter()
{
GlobalCounter.QueryCount = 0;
GlobalCounter.Active = true;
}
public void Dispose()
{
GlobalCounter.Active = false;
GlobalCounter.QueryCount = 0; //
}
}
and then just adding an active field to the GlobalCounter
public static class GlobalCounter
{
public static int QueryCount = 0;
public static bool Active = false;
}
and modifying each of the interceptor methods like so:
#if DEBUG
if (!GlobalCounter.Active) return;
GlobalCounter.QueryCount++;
// or whatever output class/method works for you
Trace.Write("Current Query Count: " + GlobalCounter.QueryCount + " -- ");
Trace.WriteLine(command.CommandText);
#endif
Now, my tests look like this:
using (var counter = new QueryCounter())
{
var pages = user.Books.First().Pages;
Assert.Equal(1, counter.Count);
}
It may be achieved with interceptor:
class EfCommandInterceptor : IDbCommandInterceptor
{
public void NonQueryExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
GlobalCounter.QueryCount++;
}
public void ReaderExecuted(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
GlobalCounter.QueryCount++;
}
public void ScalarExecuted(System.Data.Common.DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
GlobalCounter.QueryCount++;
}
//other methods are empty
}
GlobalCounter class keeps the global variable:
static class GlobalCounter
{
public static int QueryCount { get; set; }
}
Register the interceptor:
public class EntityConfigiration : DbConfiguration
{
public EntityConfigiration()
{
this.AddInterceptor(new EfCommandInterceptor());
}
}
The EntityConfigiration class will be registered automatically. You may register interceptors in config file instead.
Entity Framework Tutorial: Interception
Code-Based Configuration (EF6 onwards)
Then use:
[Test]
public void CalculateQueryCount()
{
GlobalCounter.QueryCount = 0;
using (var context = new YourContext())
{
//queries
}
int actual = GlobalCounter.QueryCount;
}
This EfCommandInterceptor code is single-threaded. For a multi-threaded test the locking (lock, Interlocked.Increment etc.) should be used instead of ++.
I have inherited a Windows service where all the dependencies are created when the service starts and are injected in the transient scope.
We are having a number of problems with this service, not least we have a DbContext which lives for the whole time the service is running, and different instances of it are injected each time.
I would like to refactor so that each worker thread gets it’s own DbContext injected which will live for just the duration of each tick.
I have looked at the custom scope. It looks fine for a single threaded app, but not multi-threaded. I also considered InThreadScope. Whilst that would give each thread it’s own instance, they are singletons as far as the thread is concerned so it does not fulfil the per tick requirement.
My current thinking is to use the named scope extension and to inject a scope factory which I can use to create a new scope on every tick.
Is this the way to go? Any suggestions, tips or alternatives would be appreciated.
UPDATE
Due to a time constraint we ended up using the named scope, but it wasn't as clean as #BatteryBackupUnit's solution. There were some dependencies further down the graph which needed a DbContext and we had to inject the scope factory again to get it. Using #BatteryBackupUnit's solution we could have reused the same instance from the ThreadLocal storage instead.
Regarding Named Scope: Consider that when you are creating a DbContext from the same thread but from an object (p.Ex. factory) which was created before the scope was created, it won't work. Either it will fail because there is no scope, or it will inject another instance of DbContext because there is a different scope.
If you don't do this, then a scope like named scope or call scope can work for you.
We are doing the following instead:
When a DbContext is requested, we check a ThreadLocal
(http://msdn.microsoft.com/de-de/library/dd642243%28v=vs.110%29.aspx) whether there is already one. In case there is, we use that one. Otherwise, we create a new one and assign it to the ThreadLocal<DbContext>.Value.
Once all operations are done, we release the DbContext and reset the ThreadLocal<DbContext>.Value.
See this (simplified, not perfect) code for an example:
public interface IUnitOfWork
{
IUnitOfWorkScope Start();
}
internal class UnitOfWork : IUnitOfWork
{
public static readonly ThreadLocal<IUnitOfWorkScope> LocalUnitOfWork = new ThreadLocal<IUnitOfWorkScope>();
private readonly IResolutionRoot resolutionRoot;
public UnitOfWork(IResolutionRoot resolutionRoot)
{
this.resolutionRoot = resolutionRoot;
}
public IUnitOfWorkScope Start()
{
if (LocalUnitOfWork.Value == null)
{
LocalUnitOfWork.Value = this.resolutionRoot.Get<IUnitOfWorkScope>();
}
return LocalUnitOfWork.Value;
}
}
public interface IUnitOfWorkScope : IDisposable
{
Guid Id { get; }
}
public class UnitOfWorkScope : IUnitOfWorkScope
{
public UnitOfWorkScope()
{
this.Id = Guid.NewGuid();
}
public Guid Id { get; private set; }
public void Dispose()
{
UnitOfWork.LocalUnitOfWork.Value = null;
}
}
public class UnitOfWorkIntegrationTest : IDisposable
{
private readonly IKernel kernel;
public UnitOfWorkIntegrationTest()
{
this.kernel = new StandardKernel();
this.kernel.Bind<IUnitOfWork>().To<UnitOfWork>();
this.kernel.Bind<IUnitOfWorkScope>().To<UnitOfWorkScope>();
}
[Fact]
public void MustCreateNewScopeWhenOldOneWasDisposed()
{
Guid scopeId1;
using (IUnitOfWorkScope scope = this.kernel.Get<IUnitOfWork>().Start())
{
scopeId1 = scope.Id;
}
Guid scopeId2;
using (IUnitOfWorkScope scope = this.kernel.Get<IUnitOfWork>().Start())
{
scopeId2 = scope.Id;
}
scopeId1.Should().NotBe(scopeId2);
}
[Fact]
public void NestedScope_MustReuseSameScope()
{
Guid scopeId1;
Guid scopeId2;
using (IUnitOfWorkScope scope1 = this.kernel.Get<IUnitOfWork>().Start())
{
scopeId1 = scope1.Id;
using (IUnitOfWorkScope scope2 = this.kernel.Get<IUnitOfWork>().Start())
{
scopeId2 = scope2.Id;
}
}
scopeId1.Should().Be(scopeId2);
}
[Fact]
public void MultipleThreads_MustCreateNewScopePerThread()
{
var unitOfWork = this.kernel.Get<IUnitOfWork>();
Guid scopeId1;
Guid scopeId2 = Guid.Empty;
using (IUnitOfWorkScope scope1 = unitOfWork.Start())
{
scopeId1 = scope1.Id;
Task otherThread = Task.Factory.StartNew(() =>
{
using (IUnitOfWorkScope scope2 = unitOfWork.Start())
{
scopeId2 = scope2.Id;
}
},
TaskCreationOptions.LongRunning);
if (!otherThread.Wait(TimeSpan.FromSeconds(5)))
{
throw new TimeoutException();
}
}
scopeId2.Should().NotBeEmpty();
scopeId1.Should().NotBe(scopeId2);
}
public void Dispose()
{
this.kernel.Dispose();
}
}
Note: i'm using nuget packages: ninject, xUnit.Net, Fluent Assertions
Also note, that you can replace the IUnitOfWork.Start with a ToProvider<IUnitOfWorkScope>() binding. Of course you need to implement the corresponding logic in the provider.
A proper unit-of-work scope, implemented in Ninject.Extensions.UnitOfWork, solves this problem.
Setup:
_kernel.Bind<IService>().To<Service>().InUnitOfWorkScope();
Usage:
using(UnitOfWorkScope.Create()){
// resolves, async/await, manual TPL ops, etc
}
Well in a web application a unit of work is responsible for the transaction management.
But what about a windows application?
As far as I know the repository is the connector between my data access layer and my business layer.
It hides all the data access stuff from my business layer.
Using this fact let me think of taking all the transaction stuff into the repository.
But I read that having Commit/RollBack methods on the repository is violating the repository's intent.
I ask myself who is responsible for transaction management in a non web application and how do I hide the transaction/Nhibernate stuff from the business layer?
The general answer is "Whoever instantiates the ISession should dispose of it. If the transaction has not been committed, this is effectively a rollback."
I've had success by using the command pattern to define an operation that I want to perform on a unit of work. Say we have a Person entity and one of the things we can do is change a person's name. Let's start with the entity:
public class Person
{
public virtual int Id { get; private set; }
public virtual string Name { get; private set; }
public virtual void ChangeName(string newName)
{
if (string.IsNullOrWhiteSpace(newName))
{
throw new DomainException("Name cannot be empty");
}
if (newName.Length > 20)
{
throw new DomainException("Name cannot exceed 20 characters");
}
this.Name = newName;
}
}
Define a simple POCO Command like this:
public class ChangeNameCommand : IDomainCommand
{
public ChangeNameCommand(int personId, string newName)
{
this.PersonId = personId;
this.NewName = newName;
}
public int PersonId { get; set; }
public string NewName { get; set; }
}
...and a Handler for the command:
public class ChangeNameCommandHandler : IHandle<ChangeNameCommand>
{
ISession session;
public ChangeNameCommandHandler(ISession session)
{
// You could demand an IPersonRepository instead of using the session directly.
this.session = session;
}
public void Handle(ChangeNameCommand command)
{
var person = session.Load<Person>(command.PersonId);
person.ChangeName(command.NewName);
}
}
The goal is that code that exists outside of a Session/Work scope can do something like this:
public class SomeClass
{
ICommandInvoker invoker;
public SomeClass(ICommandInvoker invoker)
{
this.invoker = invoker;
}
public void DoSomething()
{
var command = new ChangeNameCommand(1, "asdf");
invoker.Invoke(command);
}
}
The invocation of the command implies "do this command on a unit of work." This is what we want to happen when we invoke the command:
Begin an IoC nested scope (the "Unit of Work" scope)
Start an ISession and Transaction (this is probably implied as part of step 3)
Resolve an IHandle<ChangeNameCommand> from the IoC scope
Pass the command to the handler (the domain does its work)
Commit the transaction
End the IoC scope (the Unit of Work)
So here's an example using Autofac as the IoC container:
public class UnitOfWorkInvoker : ICommandInvoker
{
Autofac.ILifetimeScope scope;
public UnitOfWorkInvoker(Autofac.ILifetimeScope scope)
{
this.scope = scope;
}
public void Invoke<TCommand>(TCommand command) where TCommand : IDomainCommand
{
using (var workScope = scope.BeginLifetimeScope("UnitOfWork")) // step 1
{
var handler = workScope.Resolve<IHandle<TCommand>>(); // step 3 (implies step 2)
handler.Handle(command); // step 4
var session = workScope.Resolve<NHibernate.ISession>();
session.Transaction.Commit(); // step 5
} // step 6 - When the "workScope" is disposed, Autofac will dispose the ISession.
// If an exception was thrown before the commit, the transaction is rolled back.
}
}
Note: The UnitOfWorkInvoker I've shown here is violating SRP - it is a UnitOfWorkFactory, a UnitOfWork, and an Invoker all in one. In my actual implementation, I broke them out.
When I use repositories, they are contained within a unit of work. The unit of work tracks changes to the repositories and handles transaction management.
Why would it be valid to use a unit of work to handle transaction management in a web application and not in a windows application? If it's an N-Tier application, your business layer would actually be shared between both.