I'm trying to make my code be intelligent enough to only open and dispose of an Entity Framework DBcontext if it's only needed within the Current execution lifetime of the method in question.
Basically, if a real context is being passed into the method then I do Not want to dispose of it.
However, if it's only needed for the Current execution lifetime of the method in question then it will disposed in the finally block
public int UpSertCompanyStockInfo( Guid companyId, string companyName, float stockPrice, float peRatio, CommunicationEngineDatabase context)
{
bool isContextOnlyCreatedForCurrentMethodExecutionRun = false;
if (context == null)
{
isContextOnlyCreatedForCurrentMethodExecutionRun = true;
context = new CommunicationEngineDatabase();
}
try
{
CompanyStockInfo companyStockInfo = new CompanyStockInfo();
companyStockInfo.CompanyName = companyName;
companyStockInfo.StockPrice = stockPrice;
context.CompanyStockInfoTable.Add(companyStockInfo);
context.SaveChanges();
}
catch (Exception _ex)
{
}
finally
{
if (isContextOnlyCreatedForCurrentMethodExecutionRun == true && context != null)
{
((IDisposable)context).Dispose();
}
}
return 0;
}
The problem is that I feel the aforementioned code is too much in terms of lines of code. Could someone please tell me how to shorten it( maybe even do it with the using statement)?
You can encapsulate the logic inside a helper disposable (so you can utilize using) class (even struct) like this:
class DbContextScope : IDisposable
{
public static DbContextScope Open<TContext>(ref TContext context) where TContext : DbContext, new()
=> context != null ? NullScope : new DbContextScope(context = new TContext());
static readonly DbContextScope NullScope = new DbContextScope(null);
private DbContextScope(DbContext context) => this.context = context;
readonly DbContext context;
public void Dispose() => context?.Dispose();
}
And the usage with your sample would be:
public int UpSertCompanyStockInfo( Guid companyId, string companyName, float stockPrice, float peRatio, CommunicationEngineDatabase context)
{
using (DbContextScope.Open(ref context))
{
CompanyStockInfo companyStockInfo = new CompanyStockInfo();
companyStockInfo.CompanyName = companyName;
companyStockInfo.StockPrice = stockPrice;
context.CompanyStockInfoTable.Add(companyStockInfo);
context.SaveChanges();
}
return 0;
}
Related
I am using a shared database fixture for my tests, but when running multiple tests at the same time, I get the following error message:
System.InvalidOperationException: A second operation was started on this context before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext. For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.
This is my code of my Fixture:
public class SharedDatabaseFixture : IDisposable
{
public static readonly object _lock = new object();
private static bool _databaseInitialized;
private const string postgresConnectionString = "Host=localhost;Database=IntegrationTests; Username=postgres;Password=password";
public SharedDatabaseFixture()
{
Connection = new NpgsqlConnection(postgresConnectionString);
Seed();
Connection.Open();
}
public DbConnection Connection { get; }
public AppDbContext CreateContext(DbTransaction transaction = null!)
{
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkNpgsql()
.AddMediatR(typeof(IAggregateRoot).Assembly)
.AddScoped(typeof(IAsyncRepository<>), typeof(EfRepository<>))
.AddDbContext<AppDbContext>(options => options.UseNpgsql(Connection))
.BuildServiceProvider();
ServiceLocator.SetLocatorProvider(serviceProvider);
DomainEvents.Mediator = () => ServiceLocator.Current.GetInstance<IMediator>();
var builder = new DbContextOptionsBuilder<AppDbContext>();
builder.UseNpgsql(Connection).UseInternalServiceProvider(serviceProvider);
var context = new AppDbContext(builder.Options);
if (transaction != null)
{
context.Database.UseTransaction(transaction);
}
return context;
}
private void Seed()
{
lock (_lock)
{
if (!_databaseInitialized)
{
using (var context = CreateContext())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var appDbContextSeed = new AppDbContextSeed(context);
appDbContextSeed.SeedAsync().Wait();
}
_databaseInitialized = true;
}
}
}
public void Dispose() => Connection.Dispose();
}
The code I am testing uses events and those events do queries to the database. Therefore, I am registering some services and also a DbContext.
The problem is, when I run multiple tests at the same time, events are raised at the same time as well and because they are all using the same DbContext, it throws an exception when two handlers try to use the DbContext at the same time.
So, my question is: how can I instantiate a DbContext for each test (but using the same connection) or prevent it from using the DbContext at the same time?
An Example of one of my tests:
public class Project_Create : IClassFixture<SharedDatabaseFixture>
{
public SharedDatabaseFixture Fixture { get; }
public Project_Create(SharedDatabaseFixture fixture) => Fixture = fixture;
[Fact]
public void Creates_succesfully()
{
var project = new Project(SeedConstants.TEST_COMPANY_ID, "ABC", "Hallo123", "2018-123");
Assert.Equal(SeedConstants.TEST_COMPANY_ID, project.CompanyId);
Assert.Equal("ABC", project.Code);
Assert.Equal("Hallo123", project.Description);
Assert.Equal("2018-123", project.Number);
}
}
Project.cs:
public class Project : BaseEntity<Guid, ProjectValidator, Project>, IAggregateRoot
{
public Guid CompanyId { get; private set; }
public string Code { get; private set; }
public string Description { get; private set; }
public string Number { get; private set; }
public Project(Guid companyId, string code, string description, string number)
{
CompanyId = companyId;
Code = code;
Description = description;
Number = number;
Validate(this);
DomainEvents.Raise(new SetCompanyIdEvent(companyId)).GetAwaiter().GetResult();
}
}
As you can see, this project class raises an event. This event has a handler and looks like this:
public class CheckIfProjectIdExistsHandler : INotificationHandler<SetProjectIdEvent>
{
private readonly IAsyncRepository<Project> _projectRepository;
public CheckIfProjectIdExistsHandler(IAsyncRepository<Project> projectRepository)
{
_projectRepository = projectRepository;
}
public async Task Handle(SetProjectIdEvent notification, CancellationToken cancellationToken)
{
var project = await _projectRepository.GetByIdAsync(notification.ProjectId, cancellationToken);
if (project == null)
{
throw new ProjectDoesNotExistsException($"The project with ID {notification.ProjectId} does not exist.");
}
}
}
I hope this illustrates what I am testing
The answer is always simpler than you think.
When adding the DbContext in the Service Provider, I didn't specify the ServiceLifetime, so it is a singleton by default. Changing this to Transient solves the issue. Then the Connection should also be changed by the connectionString, so there are no multiple operations on the same connection.
So, this line:
.AddDbContext<AppDbContext>(options => options.UseNpgsql(Connection))
Should be change like so:
.AddDbContext<AppDbContext>(options => options.UseNpgsql(postgresConnectionString), ServiceLifetime.Transient)
Also, The registration of the repository should be as Transient and not Scoped.
I know this question has been asked before, but in those questions there aren't enough details about the actual implementation, so I decided to search for some information about how can this be achieve and this is what I've got so far:
The IUnitOfWork Interface:
public interface IUnitOfWork : IDisposable
{
IDomainTableRepository DomainTables { get; }
IVariableRepository Variables { get; }
IModelRepository Models { get; }
IStructureRepository Structures { get; }
ISentenceRepository Sentences { get; }
IExpressionRepository Expressions { get; }
IReturnRepository Returns { get; }
void Commit();
}
The implementation for IUnitOfWork:
public class SqlUnitOfWork : IUnitOfWork
{
const string ConnectionStringName = "DefaultConnection";
private AdoNetContext _context;
private DomainTableRepository _domainTables;
private VariableRepository _variables;
private ModelRepository _models;
private StructureRepository _structures;
private SentenceRepository _sentences;
private ExpressionRepository _expressions;
private ReturnRepository _returns;
public SqlUnitOfWork()
{
var connectionString =
ConfigurationManager
.ConnectionStrings[ConnectionStringName]
.ConnectionString;
_context = new AdoNetContext(connectionString, true);
}
public IDomainTableRepository DomainTables
{
get
{
if (_domainTables == null)
{
_domainTables = new DomainTableRepository(_context);
}
return _domainTables;
}
}
//...getters for the remaining repositories
public void Commit()
{
_context.SaveChanges();
}
#region IDisposable Support
private bool disposedValue = false;
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
_context.Dispose();
}
disposedValue = true;
}
}
public void Dispose()
{
Dispose(true);
}
#endregion
}
The AdoNetContext class
All the credit for the implementation of this class goes to #jgauffin.
His original blog post can be found here.
public class AdoNetContext : IDisposable
{
private IDbConnection _connection;
private bool _ownsConnection;
private IDbTransaction _transaction;
public AdoNetContext(string connectionString, bool ownsConnection)
{
_connection = new SqlConnection(connectionString);
_connection.Open();
_ownsConnection = ownsConnection;
_transaction = _connection.BeginTransaction();
}
public IDbCommand CreateCommand()
{
var command = _connection.CreateCommand();
command.Transaction = _transaction;
return command;
}
public void SaveChanges()
{
if (_transaction == null)
{
throw new InvalidOperationException("Transaction have already been already been commited. Check your transaction handling.");
}
_transaction.Commit();
_transaction = null;
}
public void Dispose()
{
if (_transaction != null)
{
_transaction.Rollback();
_transaction = null;
}
if (_connection != null && _ownsConnection)
{
_connection.Close();
_connection = null;
}
}
}
The implementation of a repository(DomainTableRepository) that uses AdoNetContext:
public class DomainTableRepository : IDomainTableRepository
{
private AdoNetContext _context;
public DomainTableRepository(AdoNetContext context)
{
_context = context;
}
public DomainTable Get(int id)
{
DomainTable table;
using (var commandTable = _context.CreateCommand())
{
commandTable.CommandType = CommandType.StoredProcedure;
commandTable.CommandText = "up_DomainTable_GetById";
commandTable.Parameters.Add(commandTable.CreateParameter("#pId", id));
table = ToList(commandTable).FirstOrDefault();
}
return table;
}
public IEnumerable<DomainTable> GetAll(int pageIndex = 1, int pageSize = 10)
{
using (var command = _context.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "up_DomainTable_GetAll";
command.Parameters.Add(command.CreateParameter("#pPageIndex", pageIndex));
command.Parameters.Add(command.CreateParameter("#pPageSize", pageSize));
return ToList(command).ToList();
}
}
public int Remove(DomainTable entity)
{
using (var command = _context.CreateCommand())
{
command.CommandType = CommandType.StoredProcedure;
command.CommandText = "up_DomainTable_Delete";
command.Parameters.Add(command.CreateParameter("#pId", entity.Id));
return command.ExecuteNonQuery();
}
}
}
I apologize for posting so much code,but I want to make sure you understand everything about what I want to ask.
First, I created an interface for the UoW(IUnitOfWork), because in the future I might want to change to an ORM like EF, but since I'm just taking my first step into the the world of back-end programming I wanted to start with ADO.NET and stored procedures.
So here are my questions regarding the implementation of the UoW pattern with ADO.NET and its usage in ASP.NET MVC:
1)Given the code for the AdoNetContext(the one that manages the db connection and transactions), is there any performance penalty for always creating a transaction?
This is what I read in a SQL book :"Executing a SELECT
statement within a transaction can create locks on the referenced tables, which can in turn block other users or sessions from performing work or reading data" Do the transactions created in C# behave exactly as their counterparts in SQL?
2)As you can see in IUnitOfWork and its implementation (UnitOfWork), I have readonly properties for every repository I have in my application. This a common "pattern" I've seen in lots of tutorials on EF and I found it very convenient since every time I new up a instance of IUnitOfWork I already have access to all its repositories and I don't have to clutter my code with the instantiation of the repositories I need in a specific Action (from a Controller).
Do you think it would too much overhead to instantiate all the repositories (as SQLUnitOfWork does) every time I create/inject a new instance of IUnitOfWork? There are some scenarios in which I might work with 4 or 5 repositories at the same time, but in most cases I'll be using just 1 or 2 in the same action.
3) I would like to use DI (maybe Ninject) to inject an instance of IUnitOfWork into my Controllers. This way later when I use another persistence framework, I will only have to change this line kernel.Bind<IUnitOfWork>().To<SqlUnitOfWork>(); From SqlUnitOfWork, to let's say EFUnitOfWork.
The problem I've encountered here is that the Dispose methods in SqlUnitOfWork and AdoNetContext are never called and as much as I want to use DI I'd rather wrap the instantiation of SqlUnitOfWork in an using statement in every Action instead of be leaking memory just so that I can use DI.
So this is what I wanted to ask you guys.
I'm trying to learn so please before clicking the close link or marking the question as a duplicate take some time to read my question.
Thanks.
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();
}
}
}
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();
}
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.
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.
Here is a image from the ANTS memory profiler. It seens that there are a lot of objects hold in memory. How can I find what I am doing wrong?
**UPDATE**
Here is my repository classes:
public class Repository<T> : IRepository<T> where T : class, IDataEntity
{
ObjectContext _context;
IObjectSet<T> _objectSet;
readonly string _entitySetName;
readonly string[] _keyNames;
private ObjectContext Context
{
get
{
if (_context == null)
{
_context = GetCurrentUnitOfWork<EFUnitOfWork>().Context;
}
return _context;
}
}
private IObjectSet<T> ObjectSet
{
get
{
if (_objectSet == null)
{
_objectSet = this.Context.CreateObjectSet<T>();
}
return _objectSet;
}
}
public TUnitOfWork GetCurrentUnitOfWork<TUnitOfWork>() where TUnitOfWork : IUnitOfWork
{
return (TUnitOfWork)UnitOfWork.Current;
}
public virtual IEnumerable<T> GetQuery()
{
return ObjectSet;
}
public virtual IEnumerable<T> GetQuery(params Expression<Func<T, object>>[] includes)
{
return ObjectSet.IncludeMultiple(includes);
}
public virtual IEnumerable<T> GetQuery(
IEnumerable<Expression<Func<T, bool>>> filters,
Func<IQueryable<T>, IOrderedQueryable<T>> orderBy,
IEnumerable<Expression<Func<T, object>>> includes)
{
IQueryable<T> _query = ObjectSet;
if (filters != null)
{
foreach (var filter in filters)
{
_query = _query.Where(filter);
}
}
if (includes != null && includes.Count() > 0)
{
_query = _query.IncludeMultiple(includes.ToArray());
}
if (orderBy != null)
{
_query = orderBy(_query);
}
return _query;
}
public virtual IPaged<T> GetQuery(
IEnumerable<Expression<Func<T, bool>>> filters,
Func<IQueryable<T>, IOrderedQueryable<T>> orderBy,
int pageNumber, int pageSize,
IEnumerable<Expression<Func<T, object>>> includes)
{
IQueryable<T> _query = ObjectSet;
if (filters != null)
{
foreach (var filter in filters)
{
_query = _query.Where(filter);
}
}
if (orderBy != null)
{
_query = orderBy(_query);
}
IPaged<T> page = new Paged<T>(_query, pageNumber, pageSize, includes);
return page;
}
public virtual void Insert(T entity)
{
this.ObjectSet.AddObject(entity);
}
public virtual void Delete(T entity)
{
if (entity is ISoftDeletable)
{
((ISoftDeletable)entity).IsDeleted = true;
//Update(entity);
}
else
{
this.ObjectSet.DeleteObject(entity);
}
}
public virtual void Attach(T entity)
{
ObjectStateEntry entry = null;
if (this.Context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry) == false)
{
this.ObjectSet.Attach(entity);
}
}
public virtual void Detach(T entity)
{
ObjectStateEntry entry = null;
if (this.Context.ObjectStateManager.TryGetObjectStateEntry(entity, out entry) == true)
{
this.ObjectSet.Detach(entity);
}
}
}
Now, if I have class A that holds records from table A, I also create class:
public class ARepository:BaseRepository<A> {
// Implementation of A's queries and specific db operations
}
Here is my EFUnitOfWork class:
public class EFUnitOfWork : IUnitOfWork, IDisposable
{
public ObjectContext Context { get; private set; }
public EFUnitOfWork(ObjectContext context)
{
Context = context;
context.ContextOptions.LazyLoadingEnabled = true;
}
public void Commit()
{
Context.SaveChanges();
}
public void Dispose()
{
if (Context != null)
{
Context.Dispose();
}
GC.SuppressFinalize(this);
}
}
And UnitOfWork class:
public static class UnitOfWork
{
private const string HTTPCONTEXTKEY = "MyProj.Domain.Business.Repository.HttpContext.Key";
private static IUnitOfWorkFactory _unitOfWorkFactory;
private static readonly Hashtable _threads = new Hashtable();
public static void Commit()
{
IUnitOfWork unitOfWork = GetUnitOfWork();
if (unitOfWork != null)
{
unitOfWork.Commit();
}
}
public static IUnitOfWork Current
{
get
{
IUnitOfWork unitOfWork = GetUnitOfWork();
if (unitOfWork == null)
{
_unitOfWorkFactory = ObjectFactory.GetInstance<IUnitOfWorkFactory>();
unitOfWork = _unitOfWorkFactory.Create();
SaveUnitOfWork(unitOfWork);
}
return unitOfWork;
}
}
private static IUnitOfWork GetUnitOfWork()
{
if (HttpContext.Current != null)
{
if (HttpContext.Current.Items.Contains(HTTPCONTEXTKEY))
{
return (IUnitOfWork)HttpContext.Current.Items[HTTPCONTEXTKEY];
}
return null;
}
else
{
Thread thread = Thread.CurrentThread;
if (string.IsNullOrEmpty(thread.Name))
{
thread.Name = Guid.NewGuid().ToString();
return null;
}
else
{
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;
}
}
}
}
Here is how I use this:
public class TaskPriceRepository : BaseRepository<TaskPrice>
{
public void Set(TaskPrice entity)
{
TaskPrice taskPrice = GetQuery().SingleOrDefault(x => x.TaskId == entity.TaskId);
if (taskPrice != null)
{
CommonUtils.CopyObject<TaskPrice>(entity, ref taskPrice);
}
else
{
this.Insert(entity);
}
}
}
public class BranchRepository : BaseRepository<Branch>
{
public IList<Branch> GetBranchesList(Guid companyId, long? branchId, string branchName)
{
return Repository.GetQuery().
Where(b => companyId == b.CompanyId).
Where(b => b.IsDeleted == false).
Where(b => !branchId.HasValue || b.BranchId.Equals(branchId.Value)).
Where(b => branchName == null || b.BranchName.Contains(branchName)).
ToList();
}
}
[WebMethod]
public void SetTaskPrice(TaskPriceDTO taskPrice)
{
TaskPrice tp = taskPrice.ToEntity();
TaskPriceRepository rep = new TaskPriceRepository();
rep.Set(tp);
UnitOfWork.Commit();
}
[WebMethod]
public IList<Branch> GetBranchesList()
{
BranchRepository rep = new BranchRepository();
return rep.GetBranchesList(m_User.UserCompany.CompanyId, null, null).ToList();
}
I hope this is enough info to help me solving the problem. Thanks.
UPDATE 2
There is also UnitOfWorkFactory that initializes UnitOfWork:
public class UnitOfWorkFactory : IUnitOfWorkFactory
{
private static Func<ObjectContext> _objectContextDelegate;
private static readonly Object _lockObject = new object();
public static void SetObjectContext(Func<ObjectContext> objectContextDelegate)
{
_objectContextDelegate = objectContextDelegate;
}
public IUnitOfWork Create()
{
ObjectContext context;
lock (_lockObject)
{
context = _objectContextDelegate();
}
return new EFUnitOfWork(context);
}
}
In order to use this, in the application startup I use structuremap:
ObjectFactory.Initialize(x =>
{
x.For<IUnitOfWorkFactory>().Use<UnitOfWorkFactory>();
x.For(typeof(IRepository<>)).Use(typeof(Repository<>));
});
I have a hunch you don't dispose the context.
I suggest disposing the context whenever you done interacting with database.
Use using statement whenever you create the context.
[Edit]
As far as I can see, you cache and don't dispose your EFUnitOfWork object. It is disposable, which is correct, but I don't see when disposable is called. Seems like you hold a reference to the context for all application run time. Moreover, you create and hold one context per thread, which will make it even worse.
I can't tell you for sure where you should put Dispose or using, as I don't know the usages.
You could put it probably to your Commit method, but I don't know if the Commit called only once during database interaction session.
Also, your design might be overcomplicated.
If I were you, I would:
Find the way to dispose the context using current code, as a short-term solution
Simplify the design, as the long-term solution
If I had time I would do long-term solution right away.
But again, I can't tell if the complexity of your design is justified, as I don't know how big your application is and what it does and what the requirements are.
Couple of things come to my mind:
You aren't probably Disposing the ObjectContext. Make sure all your database codes are within using(var context = CreateObjectContext()) block
You have an N-tier architecture and you are passing entities from the data access layer to upper layer without Detaching the entities from ObjectContext. You need to call ObjectContext.Detach(...)
You are most likely returning a full collection of entities, instead of returning a single enity for single Get operations. For ex, you have queries like from customer in context.Customers select customer instead of doing from customer in context.Customers select customer.FirstOrDefault()
I have had hard time making Entity Framework to work in an N-tier application. It's just not suitable for using in N-tier apps as is. Only EF 4.0 is. You can read about all my adventure in making EF 3 work in an N-tier app.
http://www.codeproject.com/KB/linq/ef.aspx
Does this answer your question?
Do you clear the ObjectContext once in a while. If you keep an ObjectContext alive for a long time this will consume memory related to the size of the EntityDataModel and the number of Entities loaded into this ObjectContext.
I had the same problem in a class which uses dependency injection, so the using() option was not an alternative. My solution was to add DbContextOptions<Context> to the constructor and as a private field to the class. Then, you can call
_db.Dispose();
_db = new BlockExplorerContext(_dBContextOptions);
at appropriate times. This fixed my problem where I was running out of RAM and the application was killed by the OS.
I was wondering what the best way to use transations with the entity framework.
Say I have three repositories:
Repo1(ObjectContext context)
Repo2(ObjectContext context)
Repo3(ObjectContext context)
and a service object that takes the three repositories:
Service(Repo1 repo1,Repo2 repo2, Repo3 repo3)
Serive.CreateNewObject <- calls repo1, repo2, repo3 to do stuff.
So when I create the service I create three repositories first and pass them down, each repositry takes a object context so my code looks something like this:
MyObjectContext context = new MyObjectContext();
Repo1 repo = new Repo1(context);
// etc
Now I have a controller class that is responsible for calling different services and compants of my application, showing the right forms etc. Now what I want to be able to do is wrap everything that happens in one of the controller methods in a transaction so that if some thing goes wrong I can rollback back.
The controller takes a few different Service objects, but doesn't know anything about the object context.
My questions are:
Should the context be passed in to the service layer also.
How do I implement a transaction in the controller so that anything that happens in the service
layers arn't commited untill everything has passed.
Sorry if it's a bit hard to understand..
Why doesn't your controller know about the ObjectContext?
This is where I would put it. Check out - http://msdn.microsoft.com/en-us/magazine/dd882510.aspx - here the Command is what will commit/rollback the UnitOfWork(ObjectContext).
If you don't want to have your Controller know exactly about the EF (good design) then you want to abstract your ObjectContext into an interface similar to the approach in the above link.
How about using a custom TransactionScope, one that commits when all of your services have committed?
public class TransactionScope : Scope<IDbTransaction>
{
public TransactionScope()
{
InitialiseScope(ConnectionScope.CurrentKey);
}
protected override IDbTransaction CreateItem()
{
return ConnectionScope.Current.BeginTransaction();
}
public void Commit()
{
if (CurrentScopeItem.UserCount == 1)
{
TransactionScope.Current.Commit();
}
}
}
So the transaction is only committed when the UserCount is 1, meaning the last service has committed.
The scope classes are (shame we can't do attachements...):
public abstract class Scope<T> : IDisposable
where T : IDisposable
{
private bool disposed = false;
[ThreadStatic]
private static Stack<ScopeItem<T>> stack = null;
public static T Current
{
get { return stack.Peek().Item; }
}
internal static string CurrentKey
{
get { return stack.Peek().Key; }
}
protected internal ScopeItem<T> CurrentScopeItem
{
get { return stack.Peek(); }
}
protected void InitialiseScope(string key)
{
if (stack == null)
{
stack = new Stack<ScopeItem<T>>();
}
// Only create a new item on the stack if this
// is different to the current ambient item
if (stack.Count == 0 || stack.Peek().Key != key)
{
stack.Push(new ScopeItem<T>(1, CreateItem(), key));
}
else
{
stack.Peek().UserCount++;
}
}
protected abstract T CreateItem();
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
if (disposing)
{
// If there are no users for the current item
// in the stack, pop it
if (stack.Peek().UserCount == 1)
{
stack.Pop().Item.Dispose();
}
else
{
stack.Peek().UserCount--;
}
}
// There are no unmanaged resources to release, but
// if we add them, they need to be released here.
}
disposed = true;
}
}
public class ScopeItem<T> where T : IDisposable
{
private int userCount;
private T item;
private string key;
public ScopeItem(int userCount, T item, string key)
{
this.userCount = userCount;
this.item = item;
this.key = key;
}
public int UserCount
{
get { return this.userCount; }
set { this.userCount = value; }
}
public T Item
{
get { return this.item; }
set { this.item = value; }
}
public string Key
{
get { return this.key; }
set { this.key = value; }
}
}
public class ConnectionScope : Scope<IDbConnection>
{
private readonly string connectionString = "";
private readonly string providerName = "";
public ConnectionScope(string connectionString, string providerName)
{
this.connectionString = connectionString;
this.providerName = providerName;
InitialiseScope(string.Format("{0}:{1}", connectionString, providerName));
}
public ConnectionScope(IConnectionDetailsProvider connectionDetails)
: this(connectionDetails.ConnectionString, connectionDetails.ConnectionProvider)
{
}
protected override IDbConnection CreateItem()
{
IDbConnection connection = DbProviderFactories.GetFactory(providerName).CreateConnection();
connection.ConnectionString = connectionString;
connection.Open();
return connection;
}
}
Wrap the operation in a TransactionScope.
You might want to implement the transaction model used by the Workflow Foundation. It basically has an interface that all "components" implement. After each does the main work successfully, then the host calls the "commit" method on each. If one failed, it calls the "rollback" method.