Reputation: 1313
My services take a DbContext in their constructor, and I have created a UnitOfWork class that contains all my services in order to make sure the same DbContext is used between them all.
Sample of unitofwork class
private myEntities myContext
public UnitOfWork()
{
myContext = new myEntities();
}
private RequestService requestService;
public RequestService RequestService
{
get
{
if (requestService == null)
requestService = new RequestService(myContext);
return requestService;
}
}
By Using this unitofwork class all the DbContext for my services are now consistent and a change made in one service will appear in another.
However if i need to change the actual Entity context class then that does not get persisted across each service.
Below i have a "Refresh" method that re-initializes it (I need to refresh the context so i can have this class work with some legacy code).
public void Refresh()
{
myContext = new myEntities();
}
However my service classes DbContext objects aren't passed by ref so the context is not set to a new instance of my entity class and this results in the context not being refreshed.
So I think i can solve this by passing by ref as shown below
Service class sample
MyEntities myContext;
public RequestService(ref MyEntities myContext)
{
this.myContext = myContext;
}
However i have seen people say you should not pass context classes by ref so i am curious if there is a better way out there and i am looking at this the wrong way?
Edit
Sorry turns out my proposed solution of passing by ref does not solve my problem, but i am still interested as to how i can update the entity context on the UnitOfWork class e.g. setting it to null and have that effect the service classes.
Upvotes: 1
Views: 1992
Reputation: 4797
Never ever should you share DbContext, by reference or as reference. It is not thread safe.
http://msdn.microsoft.com/en-us/data/jj729737.aspx
If you need an easy way to generate multiple DbContext, use ObjectPool from Parallel Extensions Extras.
Update 1
@tia is correct in saying that the private instance will not be updated when original changes:
class Program
{
static void Main(string[] args)
{
var pool1 = new ObjectPool<IDbConnection>(() => new SqlConnection("Data Source=server1"));
var service = new Service(ref pool1);
pool1 = new ObjectPool<IDbConnection>(() => new SqlConnection("Data Source=server2"));
Console.WriteLine(service.Pool.GetObject().ConnectionString);
}
}
class Service
{
private ObjectPool<IDbConnection> connectionPool;
public Service(ref ObjectPool<IDbConnection> pool) { this.connectionPool = pool; }
public ObjectPool<IDbConnection> Pool { get { return connectionPool; } }
}
Will print "Data Source=server 1", even if it would be a static field.
Enter Monostate, a wicked pattern, very similar to Singleton.
class Program
{
static void Main(string[] args)
{
var mop = new MonoObjectPool();
mop.Pool = new ObjectPool<IDbConnection>(() => new SqlConnection("Data Source=server1"));
var service = new Service();
mop.Pool = new ObjectPool<IDbConnection>(() => new SqlConnection("Data Source=server2"));
Console.WriteLine(service.Pool.GetObject().ConnectionString);
}
}
internal class MonoObjectPool
{
private static ObjectPool<IDbConnection> pool1;
public ObjectPool<IDbConnection> Pool
{
get { return pool1; }
set { pool1 = value; }
}
}
class Service
{
public ObjectPool<IDbConnection> Pool { get { return new MonoObjectPool().Pool; } }
}
I am getting rid of the constructor for service, as I can always get the current IDbConnection generator. There will always be only one instance of it, regardless how many times someone instantiates the MonoObjectPool.
Update 2
The other option might be to use Autofac, but I am not too familiar with it, yet, so I can't give you an example how a type could get resolved in a service instance. Here is a simple example:
class Program
{
private static IContainer container { get; set; }
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterType<DbCtx1>().As<IDbCtx>();
container = builder.Build();
using (var scope = container.BeginLifetimeScope())
{
var dbctx = scope.Resolve<IDbCtx>();
Console.WriteLine(dbctx.GetType());
}
builder = new ContainerBuilder();
builder.RegisterType<DbCtx2>().As<IDbCtx>();
container = builder.Build();
using (var scope = container.BeginLifetimeScope())
{
var dbctx = scope.Resolve<IDbCtx>();
Console.WriteLine(dbctx.GetType());
}
}
}
interface IDbCtx
{
}
class DbCtx1 : IDbCtx { }
class DbCtx2 : IDbCtx { }
Update 3
So going back to the Monostate, this works as expected:
class Program
{
static void Main(string[] args)
{
var mop = new MonoObjectPool();
mop.Pool = new ObjectPool<IDbConnection>(() => new SqlConnection("Data Source=server1"));
var service = new Service(mop);
mop.Pool = new ObjectPool<IDbConnection>(() => new SqlConnection("Data Source=server2"));
Console.WriteLine(service.Pool.GetObject().ConnectionString);
}
}
internal class MonoObjectPool
{
private static ObjectPool<IDbConnection> pool1;
public ObjectPool<IDbConnection> Pool
{
get { return pool1; }
set { pool1 = value; }
}
}
class Service
{
private MonoObjectPool myPool;
public Service(MonoObjectPool pool) { myPool = pool; }
public ObjectPool<IDbConnection> Pool { get { return myPool.Pool; } }
}
I hope this helps.
Upvotes: 3