Injecting a DbContext with an async constructor dependency

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
1
down vote

favorite
1












The goal is to construct a DbContext with a connection that uses an access token. The access token is acquired with ADAL (Active Directory Authentication Library).



The problem is that acquiring an access token is an async operation. Luckily, ADAL uses ConfigureAwait(false) for its async calls, so it should be safe to do sync-over-async without risking deadlocks.



Full code is provided below. The focus for this question is on the sync-over-async code and method of registering the DbContext, but remarks for the other code are welcome too.



Container configuration



private Container CreateContainer(AppSettings.Root appSettings)

var container = new Container();
/*
* AsyncScopedLifestyle is recommended for Web API applications.
* https://simpleinjector.readthedocs.io/en/latest/lifetimes.html#asyncscoped-vs-webrequest
*/
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();

container.Register<ITokenProvider>(
() => new TokenProvider(appSettings.Azure.TenantId, appSettings.Azure.ClientId, appSettings.Azure.ClientCertificateSDN),
Lifestyle.Singleton
);

container.Register<IConnectionFactory, ConnectionFactory>(Lifestyle.Singleton);

container.Register(
() => new FooDbContext(container.GetInstance<IConnectionFactory>().CreateConnection(appSettings.Foo.Database.ConnectionString)),
Lifestyle.Scoped
);

/*
* Calling Verify is not required, but is highly encouraged.
* https://simpleinjector.readthedocs.io/en/latest/using.html#verifying-the-container-s-configuration
*/
container.Verify();

return container;



Token provider



public interface ITokenProvider

Task<string> GetDatabaseAccessToken();


public sealed class TokenProvider
: ITokenProvider

/*
* Asynchronous calls have ConfigureAwait(false) to allow sync-over-async without risking a deadlock, e.g. when a token is required during construction.
* ADAL supports this as per https://github.com/AzureAD/azure-activedirectory-library-for-dotnet/issues/504.
*/

private readonly string tenantId;

private readonly string clientId;

private readonly X509Certificate2 clientCertificate;

public TokenProvider(
string tenantId,
string clientId,
string clientCertificateSDN
)

this.tenantId = tenantId ?? throw new ArgumentNullException(nameof(tenantId));
this.clientId = clientId ?? throw new ArgumentNullException(nameof(clientId));

using (var certificateStore = new X509Store(StoreName.My, StoreLocation.CurrentUser))

certificateStore.Open(OpenFlags.ReadOnly);
this.clientCertificate = certificateStore.Certificates
.Find(X509FindType.FindBySubjectDistinguishedName, clientCertificateSDN ?? throw new ArgumentNullException(nameof(clientCertificateSDN)), false)
[0];



private async Task<string> GetAccessToken(string resource)

var authenticationContext = new AuthenticationContext($"https://login.microsoftonline.com/tenantId");
var clientAssertionCertificate = new ClientAssertionCertificate(this.clientId, this.clientCertificate);
var authenticationResult = await authenticationContext.AcquireTokenAsync(resource, clientAssertionCertificate).ConfigureAwait(false);

return authenticationResult.AccessToken;


public async Task<string> GetDatabaseAccessToken()

return await this.GetAccessToken("https://database.windows.net/").ConfigureAwait(false);




Connection factory



public interface IConnectionFactory

SqlConnection CreateConnection(string connectionString);


public sealed class ConnectionFactory
: IConnectionFactory

private readonly ITokenProvider tokenProvider;

public ConnectionFactory(ITokenProvider tokenProvider)

this.tokenProvider = tokenProvider ?? throw new ArgumentNullException(nameof(tokenProvider));


public SqlConnection CreateConnection(string connectionString)

return new SqlConnection(connectionString)

AccessToken = this.tokenProvider.GetDatabaseAccessToken().Result
;




DbContext



public partial class FooDbContext
: DbContext

public FooDbContext(SqlConnection connection)
: base(connection, false)









share|improve this question





















  • Can someone with tag creation privileges add the simple-injector tag for me? Thanks!
    – Stijn
    Feb 5 at 12:24






  • 2




    It seems your TokenProvider class has a dependency on a X509Certificate2. Is this intended to be lazy loaded? I would probably generate the certificate inside your CreateContainer method and add that to the container as a singleton. This way if it fails, it "fails fast" on startup, and potentially other future classes have access to that certificate.
    – Brad M
    Feb 5 at 14:20











  • @BradM Good point, the certificate will eventually be used by other parts of the application, so indeed it makes sense to retrieve it in the container. Thanks!
    – Stijn
    Feb 5 at 14:43
















up vote
1
down vote

favorite
1












The goal is to construct a DbContext with a connection that uses an access token. The access token is acquired with ADAL (Active Directory Authentication Library).



The problem is that acquiring an access token is an async operation. Luckily, ADAL uses ConfigureAwait(false) for its async calls, so it should be safe to do sync-over-async without risking deadlocks.



Full code is provided below. The focus for this question is on the sync-over-async code and method of registering the DbContext, but remarks for the other code are welcome too.



Container configuration



private Container CreateContainer(AppSettings.Root appSettings)

var container = new Container();
/*
* AsyncScopedLifestyle is recommended for Web API applications.
* https://simpleinjector.readthedocs.io/en/latest/lifetimes.html#asyncscoped-vs-webrequest
*/
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();

container.Register<ITokenProvider>(
() => new TokenProvider(appSettings.Azure.TenantId, appSettings.Azure.ClientId, appSettings.Azure.ClientCertificateSDN),
Lifestyle.Singleton
);

container.Register<IConnectionFactory, ConnectionFactory>(Lifestyle.Singleton);

container.Register(
() => new FooDbContext(container.GetInstance<IConnectionFactory>().CreateConnection(appSettings.Foo.Database.ConnectionString)),
Lifestyle.Scoped
);

/*
* Calling Verify is not required, but is highly encouraged.
* https://simpleinjector.readthedocs.io/en/latest/using.html#verifying-the-container-s-configuration
*/
container.Verify();

return container;



Token provider



public interface ITokenProvider

Task<string> GetDatabaseAccessToken();


public sealed class TokenProvider
: ITokenProvider

/*
* Asynchronous calls have ConfigureAwait(false) to allow sync-over-async without risking a deadlock, e.g. when a token is required during construction.
* ADAL supports this as per https://github.com/AzureAD/azure-activedirectory-library-for-dotnet/issues/504.
*/

private readonly string tenantId;

private readonly string clientId;

private readonly X509Certificate2 clientCertificate;

public TokenProvider(
string tenantId,
string clientId,
string clientCertificateSDN
)

this.tenantId = tenantId ?? throw new ArgumentNullException(nameof(tenantId));
this.clientId = clientId ?? throw new ArgumentNullException(nameof(clientId));

using (var certificateStore = new X509Store(StoreName.My, StoreLocation.CurrentUser))

certificateStore.Open(OpenFlags.ReadOnly);
this.clientCertificate = certificateStore.Certificates
.Find(X509FindType.FindBySubjectDistinguishedName, clientCertificateSDN ?? throw new ArgumentNullException(nameof(clientCertificateSDN)), false)
[0];



private async Task<string> GetAccessToken(string resource)

var authenticationContext = new AuthenticationContext($"https://login.microsoftonline.com/tenantId");
var clientAssertionCertificate = new ClientAssertionCertificate(this.clientId, this.clientCertificate);
var authenticationResult = await authenticationContext.AcquireTokenAsync(resource, clientAssertionCertificate).ConfigureAwait(false);

return authenticationResult.AccessToken;


public async Task<string> GetDatabaseAccessToken()

return await this.GetAccessToken("https://database.windows.net/").ConfigureAwait(false);




Connection factory



public interface IConnectionFactory

SqlConnection CreateConnection(string connectionString);


public sealed class ConnectionFactory
: IConnectionFactory

private readonly ITokenProvider tokenProvider;

public ConnectionFactory(ITokenProvider tokenProvider)

this.tokenProvider = tokenProvider ?? throw new ArgumentNullException(nameof(tokenProvider));


public SqlConnection CreateConnection(string connectionString)

return new SqlConnection(connectionString)

AccessToken = this.tokenProvider.GetDatabaseAccessToken().Result
;




DbContext



public partial class FooDbContext
: DbContext

public FooDbContext(SqlConnection connection)
: base(connection, false)









share|improve this question





















  • Can someone with tag creation privileges add the simple-injector tag for me? Thanks!
    – Stijn
    Feb 5 at 12:24






  • 2




    It seems your TokenProvider class has a dependency on a X509Certificate2. Is this intended to be lazy loaded? I would probably generate the certificate inside your CreateContainer method and add that to the container as a singleton. This way if it fails, it "fails fast" on startup, and potentially other future classes have access to that certificate.
    – Brad M
    Feb 5 at 14:20











  • @BradM Good point, the certificate will eventually be used by other parts of the application, so indeed it makes sense to retrieve it in the container. Thanks!
    – Stijn
    Feb 5 at 14:43












up vote
1
down vote

favorite
1









up vote
1
down vote

favorite
1






1





The goal is to construct a DbContext with a connection that uses an access token. The access token is acquired with ADAL (Active Directory Authentication Library).



The problem is that acquiring an access token is an async operation. Luckily, ADAL uses ConfigureAwait(false) for its async calls, so it should be safe to do sync-over-async without risking deadlocks.



Full code is provided below. The focus for this question is on the sync-over-async code and method of registering the DbContext, but remarks for the other code are welcome too.



Container configuration



private Container CreateContainer(AppSettings.Root appSettings)

var container = new Container();
/*
* AsyncScopedLifestyle is recommended for Web API applications.
* https://simpleinjector.readthedocs.io/en/latest/lifetimes.html#asyncscoped-vs-webrequest
*/
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();

container.Register<ITokenProvider>(
() => new TokenProvider(appSettings.Azure.TenantId, appSettings.Azure.ClientId, appSettings.Azure.ClientCertificateSDN),
Lifestyle.Singleton
);

container.Register<IConnectionFactory, ConnectionFactory>(Lifestyle.Singleton);

container.Register(
() => new FooDbContext(container.GetInstance<IConnectionFactory>().CreateConnection(appSettings.Foo.Database.ConnectionString)),
Lifestyle.Scoped
);

/*
* Calling Verify is not required, but is highly encouraged.
* https://simpleinjector.readthedocs.io/en/latest/using.html#verifying-the-container-s-configuration
*/
container.Verify();

return container;



Token provider



public interface ITokenProvider

Task<string> GetDatabaseAccessToken();


public sealed class TokenProvider
: ITokenProvider

/*
* Asynchronous calls have ConfigureAwait(false) to allow sync-over-async without risking a deadlock, e.g. when a token is required during construction.
* ADAL supports this as per https://github.com/AzureAD/azure-activedirectory-library-for-dotnet/issues/504.
*/

private readonly string tenantId;

private readonly string clientId;

private readonly X509Certificate2 clientCertificate;

public TokenProvider(
string tenantId,
string clientId,
string clientCertificateSDN
)

this.tenantId = tenantId ?? throw new ArgumentNullException(nameof(tenantId));
this.clientId = clientId ?? throw new ArgumentNullException(nameof(clientId));

using (var certificateStore = new X509Store(StoreName.My, StoreLocation.CurrentUser))

certificateStore.Open(OpenFlags.ReadOnly);
this.clientCertificate = certificateStore.Certificates
.Find(X509FindType.FindBySubjectDistinguishedName, clientCertificateSDN ?? throw new ArgumentNullException(nameof(clientCertificateSDN)), false)
[0];



private async Task<string> GetAccessToken(string resource)

var authenticationContext = new AuthenticationContext($"https://login.microsoftonline.com/tenantId");
var clientAssertionCertificate = new ClientAssertionCertificate(this.clientId, this.clientCertificate);
var authenticationResult = await authenticationContext.AcquireTokenAsync(resource, clientAssertionCertificate).ConfigureAwait(false);

return authenticationResult.AccessToken;


public async Task<string> GetDatabaseAccessToken()

return await this.GetAccessToken("https://database.windows.net/").ConfigureAwait(false);




Connection factory



public interface IConnectionFactory

SqlConnection CreateConnection(string connectionString);


public sealed class ConnectionFactory
: IConnectionFactory

private readonly ITokenProvider tokenProvider;

public ConnectionFactory(ITokenProvider tokenProvider)

this.tokenProvider = tokenProvider ?? throw new ArgumentNullException(nameof(tokenProvider));


public SqlConnection CreateConnection(string connectionString)

return new SqlConnection(connectionString)

AccessToken = this.tokenProvider.GetDatabaseAccessToken().Result
;




DbContext



public partial class FooDbContext
: DbContext

public FooDbContext(SqlConnection connection)
: base(connection, false)









share|improve this question













The goal is to construct a DbContext with a connection that uses an access token. The access token is acquired with ADAL (Active Directory Authentication Library).



The problem is that acquiring an access token is an async operation. Luckily, ADAL uses ConfigureAwait(false) for its async calls, so it should be safe to do sync-over-async without risking deadlocks.



Full code is provided below. The focus for this question is on the sync-over-async code and method of registering the DbContext, but remarks for the other code are welcome too.



Container configuration



private Container CreateContainer(AppSettings.Root appSettings)

var container = new Container();
/*
* AsyncScopedLifestyle is recommended for Web API applications.
* https://simpleinjector.readthedocs.io/en/latest/lifetimes.html#asyncscoped-vs-webrequest
*/
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();

container.Register<ITokenProvider>(
() => new TokenProvider(appSettings.Azure.TenantId, appSettings.Azure.ClientId, appSettings.Azure.ClientCertificateSDN),
Lifestyle.Singleton
);

container.Register<IConnectionFactory, ConnectionFactory>(Lifestyle.Singleton);

container.Register(
() => new FooDbContext(container.GetInstance<IConnectionFactory>().CreateConnection(appSettings.Foo.Database.ConnectionString)),
Lifestyle.Scoped
);

/*
* Calling Verify is not required, but is highly encouraged.
* https://simpleinjector.readthedocs.io/en/latest/using.html#verifying-the-container-s-configuration
*/
container.Verify();

return container;



Token provider



public interface ITokenProvider

Task<string> GetDatabaseAccessToken();


public sealed class TokenProvider
: ITokenProvider

/*
* Asynchronous calls have ConfigureAwait(false) to allow sync-over-async without risking a deadlock, e.g. when a token is required during construction.
* ADAL supports this as per https://github.com/AzureAD/azure-activedirectory-library-for-dotnet/issues/504.
*/

private readonly string tenantId;

private readonly string clientId;

private readonly X509Certificate2 clientCertificate;

public TokenProvider(
string tenantId,
string clientId,
string clientCertificateSDN
)

this.tenantId = tenantId ?? throw new ArgumentNullException(nameof(tenantId));
this.clientId = clientId ?? throw new ArgumentNullException(nameof(clientId));

using (var certificateStore = new X509Store(StoreName.My, StoreLocation.CurrentUser))

certificateStore.Open(OpenFlags.ReadOnly);
this.clientCertificate = certificateStore.Certificates
.Find(X509FindType.FindBySubjectDistinguishedName, clientCertificateSDN ?? throw new ArgumentNullException(nameof(clientCertificateSDN)), false)
[0];



private async Task<string> GetAccessToken(string resource)

var authenticationContext = new AuthenticationContext($"https://login.microsoftonline.com/tenantId");
var clientAssertionCertificate = new ClientAssertionCertificate(this.clientId, this.clientCertificate);
var authenticationResult = await authenticationContext.AcquireTokenAsync(resource, clientAssertionCertificate).ConfigureAwait(false);

return authenticationResult.AccessToken;


public async Task<string> GetDatabaseAccessToken()

return await this.GetAccessToken("https://database.windows.net/").ConfigureAwait(false);




Connection factory



public interface IConnectionFactory

SqlConnection CreateConnection(string connectionString);


public sealed class ConnectionFactory
: IConnectionFactory

private readonly ITokenProvider tokenProvider;

public ConnectionFactory(ITokenProvider tokenProvider)

this.tokenProvider = tokenProvider ?? throw new ArgumentNullException(nameof(tokenProvider));


public SqlConnection CreateConnection(string connectionString)

return new SqlConnection(connectionString)

AccessToken = this.tokenProvider.GetDatabaseAccessToken().Result
;




DbContext



public partial class FooDbContext
: DbContext

public FooDbContext(SqlConnection connection)
: base(connection, false)











share|improve this question












share|improve this question




share|improve this question








edited Apr 11 at 8:11









t3chb0t

32.1k54195




32.1k54195









asked Feb 5 at 12:20









Stijn

196315




196315











  • Can someone with tag creation privileges add the simple-injector tag for me? Thanks!
    – Stijn
    Feb 5 at 12:24






  • 2




    It seems your TokenProvider class has a dependency on a X509Certificate2. Is this intended to be lazy loaded? I would probably generate the certificate inside your CreateContainer method and add that to the container as a singleton. This way if it fails, it "fails fast" on startup, and potentially other future classes have access to that certificate.
    – Brad M
    Feb 5 at 14:20











  • @BradM Good point, the certificate will eventually be used by other parts of the application, so indeed it makes sense to retrieve it in the container. Thanks!
    – Stijn
    Feb 5 at 14:43
















  • Can someone with tag creation privileges add the simple-injector tag for me? Thanks!
    – Stijn
    Feb 5 at 12:24






  • 2




    It seems your TokenProvider class has a dependency on a X509Certificate2. Is this intended to be lazy loaded? I would probably generate the certificate inside your CreateContainer method and add that to the container as a singleton. This way if it fails, it "fails fast" on startup, and potentially other future classes have access to that certificate.
    – Brad M
    Feb 5 at 14:20











  • @BradM Good point, the certificate will eventually be used by other parts of the application, so indeed it makes sense to retrieve it in the container. Thanks!
    – Stijn
    Feb 5 at 14:43















Can someone with tag creation privileges add the simple-injector tag for me? Thanks!
– Stijn
Feb 5 at 12:24




Can someone with tag creation privileges add the simple-injector tag for me? Thanks!
– Stijn
Feb 5 at 12:24




2




2




It seems your TokenProvider class has a dependency on a X509Certificate2. Is this intended to be lazy loaded? I would probably generate the certificate inside your CreateContainer method and add that to the container as a singleton. This way if it fails, it "fails fast" on startup, and potentially other future classes have access to that certificate.
– Brad M
Feb 5 at 14:20





It seems your TokenProvider class has a dependency on a X509Certificate2. Is this intended to be lazy loaded? I would probably generate the certificate inside your CreateContainer method and add that to the container as a singleton. This way if it fails, it "fails fast" on startup, and potentially other future classes have access to that certificate.
– Brad M
Feb 5 at 14:20













@BradM Good point, the certificate will eventually be used by other parts of the application, so indeed it makes sense to retrieve it in the container. Thanks!
– Stijn
Feb 5 at 14:43




@BradM Good point, the certificate will eventually be used by other parts of the application, so indeed it makes sense to retrieve it in the container. Thanks!
– Stijn
Feb 5 at 14:43










1 Answer
1






active

oldest

votes

















up vote
1
down vote













If you don't mind me asking, any reason you want to register the DbContext directly? I know .NET Core seems to want you to do that, but I don't like IoC containers deciding when to leave open or close my DB connections. Insetead, I'd just do IFooDbContextFactory and have that use a "CreateAsync" method. This factory can take your IAppSettings or IDbContextSettings or something to get the connection string. Then you can call the provider async just fine and in your specific code you'd just do:



public class FooDbContextFactory : IFooDbContextFactory

private readonly IFooDbContextSettings _settings;
private readonly ITokenProvider _tokenProvider;

public FooDbContextFactory(IFooDbContextSettings settings, ITokenProvider tokenProvider)

_settings = settings;
_tokenProvider = tokenProvider;


public async Task<FooDbContext> CreateAsync()

var token = await _tokenPRovider.GetToken(...).ConfigureAwait(false);
return new FooDbContext(...);



public class SomeRepository

private reaonly IFooDbContextFactory _factory;
public SomeRepository(IFooDbContextFactory factory)

_factory = factory;


public async Task<ICollection<SomeEntity>> GetSomeList()

using (var context = await _factory.CreateAsync().ConfigureAwait(false))

return await context.Somes().ToListAsync().ConfigureAwait(false);








share|improve this answer





















  • I don't like IoC containers deciding when to leave open or close my DB connections and they don't if you tell them not to. Four ways to dispose IDisposables in ASP.NET Core or with Autofac you can mark a dependency as ExternallyOwned.
    – t3chb0t
    Apr 11 at 8:07











  • People can make mistakes with this configuration. With a factory and using statement there is no question when this gets disposed and you don't have to worry about other applications using this code configure it incorrectly😓
    – Daniel Lorenz
    Apr 11 at 20:28










  • This is a very good argument... to kill every idea because people can make mistakes with just anything :-| With a properly configured DI container I have less worries then by maintaining my own solutions. I like the smiley ;-)
    – t3chb0t
    Apr 12 at 12:19











  • I just like having control over my database connections. I know when I'm done so I kill it as soon as I'm done with it.
    – Daniel Lorenz
    Apr 13 at 12:01










  • Plus if you are doing calls in parallel you'd need multiple instances of dbcontext since you can't share them. Thus you'd need a factory for that anyway. Plus you can have more control over the configuration on it.
    – Daniel Lorenz
    Apr 13 at 12:04










Your Answer




StackExchange.ifUsing("editor", function ()
return StackExchange.using("mathjaxEditing", function ()
StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
);
);
, "mathjax-editing");

StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "196"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: false,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);








 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f186812%2finjecting-a-dbcontext-with-an-async-constructor-dependency%23new-answer', 'question_page');

);

Post as a guest






























1 Answer
1






active

oldest

votes








1 Answer
1






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
1
down vote













If you don't mind me asking, any reason you want to register the DbContext directly? I know .NET Core seems to want you to do that, but I don't like IoC containers deciding when to leave open or close my DB connections. Insetead, I'd just do IFooDbContextFactory and have that use a "CreateAsync" method. This factory can take your IAppSettings or IDbContextSettings or something to get the connection string. Then you can call the provider async just fine and in your specific code you'd just do:



public class FooDbContextFactory : IFooDbContextFactory

private readonly IFooDbContextSettings _settings;
private readonly ITokenProvider _tokenProvider;

public FooDbContextFactory(IFooDbContextSettings settings, ITokenProvider tokenProvider)

_settings = settings;
_tokenProvider = tokenProvider;


public async Task<FooDbContext> CreateAsync()

var token = await _tokenPRovider.GetToken(...).ConfigureAwait(false);
return new FooDbContext(...);



public class SomeRepository

private reaonly IFooDbContextFactory _factory;
public SomeRepository(IFooDbContextFactory factory)

_factory = factory;


public async Task<ICollection<SomeEntity>> GetSomeList()

using (var context = await _factory.CreateAsync().ConfigureAwait(false))

return await context.Somes().ToListAsync().ConfigureAwait(false);








share|improve this answer





















  • I don't like IoC containers deciding when to leave open or close my DB connections and they don't if you tell them not to. Four ways to dispose IDisposables in ASP.NET Core or with Autofac you can mark a dependency as ExternallyOwned.
    – t3chb0t
    Apr 11 at 8:07











  • People can make mistakes with this configuration. With a factory and using statement there is no question when this gets disposed and you don't have to worry about other applications using this code configure it incorrectly😓
    – Daniel Lorenz
    Apr 11 at 20:28










  • This is a very good argument... to kill every idea because people can make mistakes with just anything :-| With a properly configured DI container I have less worries then by maintaining my own solutions. I like the smiley ;-)
    – t3chb0t
    Apr 12 at 12:19











  • I just like having control over my database connections. I know when I'm done so I kill it as soon as I'm done with it.
    – Daniel Lorenz
    Apr 13 at 12:01










  • Plus if you are doing calls in parallel you'd need multiple instances of dbcontext since you can't share them. Thus you'd need a factory for that anyway. Plus you can have more control over the configuration on it.
    – Daniel Lorenz
    Apr 13 at 12:04














up vote
1
down vote













If you don't mind me asking, any reason you want to register the DbContext directly? I know .NET Core seems to want you to do that, but I don't like IoC containers deciding when to leave open or close my DB connections. Insetead, I'd just do IFooDbContextFactory and have that use a "CreateAsync" method. This factory can take your IAppSettings or IDbContextSettings or something to get the connection string. Then you can call the provider async just fine and in your specific code you'd just do:



public class FooDbContextFactory : IFooDbContextFactory

private readonly IFooDbContextSettings _settings;
private readonly ITokenProvider _tokenProvider;

public FooDbContextFactory(IFooDbContextSettings settings, ITokenProvider tokenProvider)

_settings = settings;
_tokenProvider = tokenProvider;


public async Task<FooDbContext> CreateAsync()

var token = await _tokenPRovider.GetToken(...).ConfigureAwait(false);
return new FooDbContext(...);



public class SomeRepository

private reaonly IFooDbContextFactory _factory;
public SomeRepository(IFooDbContextFactory factory)

_factory = factory;


public async Task<ICollection<SomeEntity>> GetSomeList()

using (var context = await _factory.CreateAsync().ConfigureAwait(false))

return await context.Somes().ToListAsync().ConfigureAwait(false);








share|improve this answer





















  • I don't like IoC containers deciding when to leave open or close my DB connections and they don't if you tell them not to. Four ways to dispose IDisposables in ASP.NET Core or with Autofac you can mark a dependency as ExternallyOwned.
    – t3chb0t
    Apr 11 at 8:07











  • People can make mistakes with this configuration. With a factory and using statement there is no question when this gets disposed and you don't have to worry about other applications using this code configure it incorrectly😓
    – Daniel Lorenz
    Apr 11 at 20:28










  • This is a very good argument... to kill every idea because people can make mistakes with just anything :-| With a properly configured DI container I have less worries then by maintaining my own solutions. I like the smiley ;-)
    – t3chb0t
    Apr 12 at 12:19











  • I just like having control over my database connections. I know when I'm done so I kill it as soon as I'm done with it.
    – Daniel Lorenz
    Apr 13 at 12:01










  • Plus if you are doing calls in parallel you'd need multiple instances of dbcontext since you can't share them. Thus you'd need a factory for that anyway. Plus you can have more control over the configuration on it.
    – Daniel Lorenz
    Apr 13 at 12:04












up vote
1
down vote










up vote
1
down vote









If you don't mind me asking, any reason you want to register the DbContext directly? I know .NET Core seems to want you to do that, but I don't like IoC containers deciding when to leave open or close my DB connections. Insetead, I'd just do IFooDbContextFactory and have that use a "CreateAsync" method. This factory can take your IAppSettings or IDbContextSettings or something to get the connection string. Then you can call the provider async just fine and in your specific code you'd just do:



public class FooDbContextFactory : IFooDbContextFactory

private readonly IFooDbContextSettings _settings;
private readonly ITokenProvider _tokenProvider;

public FooDbContextFactory(IFooDbContextSettings settings, ITokenProvider tokenProvider)

_settings = settings;
_tokenProvider = tokenProvider;


public async Task<FooDbContext> CreateAsync()

var token = await _tokenPRovider.GetToken(...).ConfigureAwait(false);
return new FooDbContext(...);



public class SomeRepository

private reaonly IFooDbContextFactory _factory;
public SomeRepository(IFooDbContextFactory factory)

_factory = factory;


public async Task<ICollection<SomeEntity>> GetSomeList()

using (var context = await _factory.CreateAsync().ConfigureAwait(false))

return await context.Somes().ToListAsync().ConfigureAwait(false);








share|improve this answer













If you don't mind me asking, any reason you want to register the DbContext directly? I know .NET Core seems to want you to do that, but I don't like IoC containers deciding when to leave open or close my DB connections. Insetead, I'd just do IFooDbContextFactory and have that use a "CreateAsync" method. This factory can take your IAppSettings or IDbContextSettings or something to get the connection string. Then you can call the provider async just fine and in your specific code you'd just do:



public class FooDbContextFactory : IFooDbContextFactory

private readonly IFooDbContextSettings _settings;
private readonly ITokenProvider _tokenProvider;

public FooDbContextFactory(IFooDbContextSettings settings, ITokenProvider tokenProvider)

_settings = settings;
_tokenProvider = tokenProvider;


public async Task<FooDbContext> CreateAsync()

var token = await _tokenPRovider.GetToken(...).ConfigureAwait(false);
return new FooDbContext(...);



public class SomeRepository

private reaonly IFooDbContextFactory _factory;
public SomeRepository(IFooDbContextFactory factory)

_factory = factory;


public async Task<ICollection<SomeEntity>> GetSomeList()

using (var context = await _factory.CreateAsync().ConfigureAwait(false))

return await context.Somes().ToListAsync().ConfigureAwait(false);









share|improve this answer













share|improve this answer



share|improve this answer











answered Feb 9 at 21:13









Daniel Lorenz

10610




10610











  • I don't like IoC containers deciding when to leave open or close my DB connections and they don't if you tell them not to. Four ways to dispose IDisposables in ASP.NET Core or with Autofac you can mark a dependency as ExternallyOwned.
    – t3chb0t
    Apr 11 at 8:07











  • People can make mistakes with this configuration. With a factory and using statement there is no question when this gets disposed and you don't have to worry about other applications using this code configure it incorrectly😓
    – Daniel Lorenz
    Apr 11 at 20:28










  • This is a very good argument... to kill every idea because people can make mistakes with just anything :-| With a properly configured DI container I have less worries then by maintaining my own solutions. I like the smiley ;-)
    – t3chb0t
    Apr 12 at 12:19











  • I just like having control over my database connections. I know when I'm done so I kill it as soon as I'm done with it.
    – Daniel Lorenz
    Apr 13 at 12:01










  • Plus if you are doing calls in parallel you'd need multiple instances of dbcontext since you can't share them. Thus you'd need a factory for that anyway. Plus you can have more control over the configuration on it.
    – Daniel Lorenz
    Apr 13 at 12:04
















  • I don't like IoC containers deciding when to leave open or close my DB connections and they don't if you tell them not to. Four ways to dispose IDisposables in ASP.NET Core or with Autofac you can mark a dependency as ExternallyOwned.
    – t3chb0t
    Apr 11 at 8:07











  • People can make mistakes with this configuration. With a factory and using statement there is no question when this gets disposed and you don't have to worry about other applications using this code configure it incorrectly😓
    – Daniel Lorenz
    Apr 11 at 20:28










  • This is a very good argument... to kill every idea because people can make mistakes with just anything :-| With a properly configured DI container I have less worries then by maintaining my own solutions. I like the smiley ;-)
    – t3chb0t
    Apr 12 at 12:19











  • I just like having control over my database connections. I know when I'm done so I kill it as soon as I'm done with it.
    – Daniel Lorenz
    Apr 13 at 12:01










  • Plus if you are doing calls in parallel you'd need multiple instances of dbcontext since you can't share them. Thus you'd need a factory for that anyway. Plus you can have more control over the configuration on it.
    – Daniel Lorenz
    Apr 13 at 12:04















I don't like IoC containers deciding when to leave open or close my DB connections and they don't if you tell them not to. Four ways to dispose IDisposables in ASP.NET Core or with Autofac you can mark a dependency as ExternallyOwned.
– t3chb0t
Apr 11 at 8:07





I don't like IoC containers deciding when to leave open or close my DB connections and they don't if you tell them not to. Four ways to dispose IDisposables in ASP.NET Core or with Autofac you can mark a dependency as ExternallyOwned.
– t3chb0t
Apr 11 at 8:07













People can make mistakes with this configuration. With a factory and using statement there is no question when this gets disposed and you don't have to worry about other applications using this code configure it incorrectly😓
– Daniel Lorenz
Apr 11 at 20:28




People can make mistakes with this configuration. With a factory and using statement there is no question when this gets disposed and you don't have to worry about other applications using this code configure it incorrectly😓
– Daniel Lorenz
Apr 11 at 20:28












This is a very good argument... to kill every idea because people can make mistakes with just anything :-| With a properly configured DI container I have less worries then by maintaining my own solutions. I like the smiley ;-)
– t3chb0t
Apr 12 at 12:19





This is a very good argument... to kill every idea because people can make mistakes with just anything :-| With a properly configured DI container I have less worries then by maintaining my own solutions. I like the smiley ;-)
– t3chb0t
Apr 12 at 12:19













I just like having control over my database connections. I know when I'm done so I kill it as soon as I'm done with it.
– Daniel Lorenz
Apr 13 at 12:01




I just like having control over my database connections. I know when I'm done so I kill it as soon as I'm done with it.
– Daniel Lorenz
Apr 13 at 12:01












Plus if you are doing calls in parallel you'd need multiple instances of dbcontext since you can't share them. Thus you'd need a factory for that anyway. Plus you can have more control over the configuration on it.
– Daniel Lorenz
Apr 13 at 12:04




Plus if you are doing calls in parallel you'd need multiple instances of dbcontext since you can't share them. Thus you'd need a factory for that anyway. Plus you can have more control over the configuration on it.
– Daniel Lorenz
Apr 13 at 12:04












 

draft saved


draft discarded


























 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f186812%2finjecting-a-dbcontext-with-an-async-constructor-dependency%23new-answer', 'question_page');

);

Post as a guest













































































Popular posts from this blog

Chat program with C++ and SFML

Function to Return a JSON Like Objects Using VBA Collections and Arrays

Will my employers contract hold up in court?