Closed g-adolph closed 6 years ago
@g-adolph, hi and thanks for kind words.
It looks like you're passing the context to your repository methods, which is a bad thing to do because 1) the context is managed by the library and managing it manually can lead to unexpected results like you're seeing here and 2) passing the context around explicitly defeats the purpose of using the ambient context pattern. You could just as easily pass around IDbConnection
and call it a day.
Have you looked at the wiki to see how to set things up?
Ok I got it.
Only two more questions. 1) Doing this how can i create dynamics contexts? My business demands a db per client and one global db.
2) I want segregate transaction and non transactions contexts, doing like you said I still have this ?
Can you provide a sample project? I think it will be easier to understand.
If you maintain connections to multiple databases than you'll probably need multiple ambient contexts for each database. I haven't tried this myself, but you could probably implement a factory around AmbientDbContextFactory
(yes, factory for a factory... i know) which returns a client-specific instance of AmbientDbContextFactory
. You'll also need an IDbConnectionFactory
implementation for each respective database.
So if you take this generic setup below...
var connectionFactory = new SqlServerConnectionFactory("connection-string");
using (var context = new AmbientDbContextFactory(connectionFactory).Create())
{
new MyRepository(new AmbientDbContextLocator()).DoSomething();
context.Commit();
}
You'd end up with something similar to this
public class ClientSpecificAmbientDbContextFactory
{
public IAmbientDbContextFactory Create(clientId)
{
// Get connection string from client id and build a connection factory
var connectionFactory = new SqlServerConnectionFactory("client-specific-connection-string");
// create AmbientDbContextFactory for the client
var ambientContextFactory = new AmbientDbContextFactory(connectionFactory);
return ambientContextFactory;
}
}
// Somewhere else where you need to query you'd do this
var ambientDbContextFactory = new ClientSpecificAmbientDbContextFactory().Create("some-client-id");
using (var context = factory.Create())
{
new MyRepository(new AmbientDbContextLocator()).DoSomething();
context.Commit();
}
You can create 2 different contexts (one with and one without transactional support) support. You can nest them, but they cannot be joined and have to be committed individually. It'd look similar to this:
// Create context with transactional support
using (var contextWithTransaction = new AmbientDbContextFactory(connectionFactory).Create())
{
// Do Work with contextWithTransaction
// Create new context without transaction support (suppress = true suppresses transactions) separate from outer context
using (var contextWitoutTransaction = new AmbientDbContextFactory(connectionFactory).Create(join: false, suppress: true))
{
// Do Work with contextWitoutTransaction
contextWitoutTransaction.Commit(); // Commits only itself
}
contextWithTransaction.Commit(); // Commits only itself
}
I'll try to add some sample projects when I can get around to it. Hopefully soon :)
Thank you a lot. I'll try this. When I done I'll try to create some sample.
@sokopov, I think your solution to create specificContexts have some problem. My troubles begins when I have simultaneous connections to different clients. Shows me that my connection still open when I do something like this.
var contractAmbientContext = new ContractAmbientDbContextFactory(this.connectionOptions.Value).Create(contract.Id);
Scenarios scenario = null;
Projects project = null;
using (var context = contractAmbientContext.Create(false))
{
var scenariosRepository = new ScenariosRepository(this.connectionOptions, new AmbientDbContextLocator());
scenario = await scenariosRepository.GetScenarioByIdAsync(message.ContractId, message.ScenarioId)
.ConfigureAwait(false);
var projectsRepository = new ProjectsRepository(this.connectionOptions, new AmbientDbContextLocator());
project = await projectsRepository.GetProjectByIdAsync(message.ContractId, scenario.ProjectId)
.ConfigureAwait(false);
}
This is a pretty off-the-cuff solution, which I haven't actually tested. The library was originally designed to handle a single database connection only. I ultimately don't know if it will work for more than 1 connection in its current state. Having said that I don't think there is any connection leaks, per se. The original issue was due to incorrect use of the library and the exception that was thrown indicated that. I'll consider adding handling of multiple connections as an enhancement.
Hi, first of all i want to glad you. This project is amazing.
I'm having a connection leak because i can't dispose my context when i use a async methods. When i dispose show me this message: "Could not dispose ambient database context because it is not the active ambient database context. This could occur because ambient database context is being disposed out of order."
This is a example of my usage.
When i have multiples connections simultaneous my problem happen.