Reputation: 2431
Hi I am using Unity to manage my service layers, which in turn speak to UnitOfWork which manages all the repositories.
Some of my services call other services, my question is how can i pass the same UnitOfWork between service layers?
In my case all controller actions are initiated from a GUI on each button action or event on a timer, this is why I have a factory to create UnitOfWork on demand, but it is causing issues as i dont know how to pass this UnitOfWork between services.
Especially difficult is knowing how to get this specific UnitOfWork instance injected into the service constructor. Please note that some of the services may be long running (10 minutes or so on a background thread), i don't know if that has any impact on the design or not.
Currently the service that is called from the other service is then creating its own UnitOfWork which is causing issues for both transactional design, and Entity framework entity tracking.
Suggestions very welcome!
class OtherService : IOtherService
{
public OtherService(IUnitOfWorkFactory unitOfworkFactory,
ISettingsService settingsService)
{
UnitOfWorkFactory = unitOfworkFactory;
SettingsService = settingsService;
}
IUnitOfWorkFactory UnitOfWorkFactory;
ISettingsService SettingsService;
function SomeSeviceCall()
{
// Perhaps one way is to use a factory to instantiate a
// SettingService, and pass in the UnitOfWork here?
// Ideally it would be nice for Unity to handle all of
// the details regardless of a service being called from
// another service or called directly from a controller
// ISettingsService settingsService =
// UnityContainer.Resolve<ISettingService>();
using (var uow = UnitOfWorkFactory.CreateUnitOfWork())
{
var companies = uow.CompaniesRepository.GetAll();
foreach(Company company in companies)
{
settingsService.SaveSettings(company, "value");
company.Processed = DateTime.UtcNow();
}
uow.Save();
}
}
}
class SettingsService : ISettingsService
{
public SettingsService(IUnitOfWorkFactory unitOfworkFactory)
{
UnitOfWorkFactory = unitOfworkFactory;
}
IUnitOfWorkFactory UnitOfWorkFactory;
// ISettingsService.SaveSettings code in another module...
function void ISettingsService.SaveSettings(Company company,
string value)
{
// this is causing an issue as it essentially creates a
// sub-transaction with the new UnitOfWork creating a new
// Entiy Framework context
using (var uow = UnitOfWorkFactory.CreateUnitOfWork())
{
Setting setting = new Setting();
setting.CompanyID = company.CompanyID;
setting.SettingValue = value;
uow.Insert(setting);
uow.Save();
}
}
}
Upvotes: 1
Views: 1585
Reputation: 1
You be the judge... I think you are double doing it.
Point 1: http://www.britannica.com/topic/Occams-razor
Point 2: From the F2 object browser description of EF main object, the DBContext...
public class DbContext Member of System.Data.Entity
Summary: A DbContext instance represents a combination of the Unit Of Work and Repository patterns such that it can be used to query from a database and group together changes that will then be written back to the store as a unit.
Upvotes: -1
Reputation: 4283
Hi I've been battling with this problem this is what I've come up with...
public class UnitOfWorkFactory
{
private static readonly Hashtable _threads = new Hashtable();
private const string HTTPCONTEXTKEY =
"AboutDbContext.UnitOfWorkFactory";
public static IUnitOfWork Create()
{
IUnitOfWork unitOfWork = GetUnitOfWork();
if (unitOfWork == null || unitOfWork.IsDisposed)
{
unitOfWork = new UnitOfWork();
SaveUnitOfWork(unitOfWork);
}
return unitOfWork;
}
public static IUnitOfWork GetUnitOfWork()
{
if (HttpContext.Current != null)
{
if (HttpContext.Current.Items.Contains(HTTPCONTEXTKEY))
{
return (IUnitOfWork)HttpContext
.Current.Items[HTTPCONTEXTKEY];
}
return null;
}
var thread = Thread.CurrentThread;
if (string.IsNullOrEmpty(thread.Name))
{
thread.Name = Guid.NewGuid().ToString();
return null;
}
lock (_threads.SyncRoot)
{
return (IUnitOfWork)_threads[Thread.CurrentThread.Name];
}
}
private static void SaveUnitOfWork(IUnitOfWork unitOfWork)
{
if (HttpContext.Current != null)
{
HttpContext.Current.Items[HTTPCONTEXTKEY] = unitOfWork;
}
else
{
lock (_threads.SyncRoot)
{
_threads[Thread.CurrentThread.Name] = unitOfWork;
}
}
}
public static void DisposeUnitOfWork(IUnitOfWork unitOfWork)
{
if (HttpContext.Current != null)
{
HttpContext.Current.Items.Remove(HTTPCONTEXTKEY);
}
else
{
lock (_threads.SyncRoot)
{
_threads.Remove(Thread.CurrentThread.Name);
}
}
}
}
public interface IUnitOfWork : IDisposable
{
void Commit();
bool IsDisposed { get; }
}
public class UnitOfWork : MyContext
{
}
public abstract class Repository<T>
: IRepository<T>, IDisposable where T : class
{
private UnitOfWork _context;
private UnitOfWork Context
{
get
{
if (_context == null || _context.IsDisposed)
return _context = GetCurrentUnitOfWork<UnitOfWork>();
return _context;
}
}
public TUnitOfWork GetCurrentUnitOfWork<TUnitOfWork>()
where TUnitOfWork : IUnitOfWork
{
return (TUnitOfWork)UnitOfWorkFactory.GetUnitOfWork();
}
public IEnumerable<T> Get(Expression<Func<T, bool>> predicate)
{
return Context.Set<T>().Where(predicate).ToList();
}
public bool Exists(Expression<Func<T, bool>> predicate)
{
return Context.Set<T>().Any(predicate);
}
public T First(Expression<Func<T, bool>> predicate)
{
return Context.Set<T>().Where(predicate).FirstOrDefault();
}
public IEnumerable<T> GetAll()
{
return Context.Set<T>().ToList();
}
public IEnumerable<T> GetAllOrderBy(Func<T, object> keySelector)
{
return Context.Set<T>().OrderBy(keySelector).ToList();
}
public IEnumerable<T> GetAllOrderByDescending(Func<T, object> keySelector)
{
return Context.Set<T>().OrderByDescending(keySelector).ToList();
}
public void Commit()
{
Context.SaveChanges();
}
public void Add(T entity)
{
Context.Set<T>().Add(entity);
}
public void Update(T entity)
{
Context.Entry(entity).State = EntityState.Modified;
}
public void Delete(T entity)
{
Context.Set<T>().Remove(entity);
}
public void Dispose()
{
if (Context != null)
{
Context.Dispose();
}
GC.SuppressFinalize(this);
}
}
public class MyContext : DbContext, IUnitOfWork
{
public DbSet<Car> Cars { get; set; }
public void Commit()
{
SaveChanges();
}
protected override void Dispose(bool disposing)
{
IsDisposed = true;
UnitOfWorkFactory.DisposeUnitOfWork(this);
base.Dispose(disposing);
}
public bool IsDisposed { get; private set; }
}
Then I can do:
using (var unitOfWork = UnitOfWorkFactory.Create())
{
_carRepository.Add(new Car
{
Make = "Porshe", Name = "Boxter"
});
_carRepository.Commit();
}
Upvotes: 2
Reputation: 927
You could use some kind of "current" unit of work which is tied to current thread and explicitly resolved in service code. You need class to hold thread static instance of UoW to achieve this. However, this is not very good solution.
Upvotes: 1