WPF EF Core 6 DP Question
-
I'm working on an app that has two repository projects. The first is part of my framework and is called ApplicationSecurity. It has Users, Rights, Encryption, etc. It's designed to be generic and used in any app. It expects an instance of an EF DBContext to be passed into the CTOR The second repo project is for my app itself. Its tables are app specific. The DBContext resides in this project So, I build the DBContext in this second project, and used it in both. In my App.cs CTOR I have:
public App()
{
AppHost = Host.CreateDefaultBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
})
.Build();
}How do I include my DBContext here? [ ] My ApplicationSecurity class needs an instance of the DBContext class. How can I pass a reference to a DBContext into the ApplicationSecurity CTOR?In theory, theory and practice are the same. But in practice, they never are.” If it's not broken, fix it until it is. Everything makes sense in someone's mind.
-
I'm working on an app that has two repository projects. The first is part of my framework and is called ApplicationSecurity. It has Users, Rights, Encryption, etc. It's designed to be generic and used in any app. It expects an instance of an EF DBContext to be passed into the CTOR The second repo project is for my app itself. Its tables are app specific. The DBContext resides in this project So, I build the DBContext in this second project, and used it in both. In my App.cs CTOR I have:
public App()
{
AppHost = Host.CreateDefaultBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
services.AddSingleton();
})
.Build();
}How do I include my DBContext here? [ ] My ApplicationSecurity class needs an instance of the DBContext class. How can I pass a reference to a DBContext into the ApplicationSecurity CTOR?In theory, theory and practice are the same. But in practice, they never are.” If it's not broken, fix it until it is. Everything makes sense in someone's mind.
Either services.AddDbContext<TContext>(...)[^], or services.AddDbContextPool<TContext>(...)[^] - as per the remarks, pooling probably won't add much benefit for most small applications.
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
-
Either services.AddDbContext<TContext>(...)[^], or services.AddDbContextPool<TContext>(...)[^] - as per the remarks, pooling probably won't add much benefit for most small applications.
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
OK, so I now have
AppHost = Host.CreateDefaultBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddDbContext(options =>
{
options.UseSqlServer(_connString);
});services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); }) .Build();
}
But the ApplicationSecurity class needs an instance of MyDbContext. How do I pass that in?
In theory, theory and practice are the same. But in practice, they never are.” If it's not broken, fix it until it is. Everything makes sense in someone's mind.
-
OK, so I now have
AppHost = Host.CreateDefaultBuilder()
.ConfigureServices((hostContext, services) =>
{
services.AddDbContext(options =>
{
options.UseSqlServer(_connString);
});services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); }) .Build();
}
But the ApplicationSecurity class needs an instance of MyDbContext. How do I pass that in?
In theory, theory and practice are the same. But in practice, they never are.” If it's not broken, fix it until it is. Everything makes sense in someone's mind.
Add it as a constructor parameter. Or are you saying that the
ApplicationSecurity
is defined in an assembly which doesn't have a reference to the assembly where theMooseDBContext
is defined? If that's the case, then you probably want the context to implement an interface:// ApplicationSecurity assembly:
public interface IApplicationSecurityContext
{
DbSet<YourEntityType> YourEntityTypes { get; }
}public class ApplicationSecurity : IApplicationSecurity
{
public ApplicationSecurity(IApplicationSecurityContext context)
{
ArgumentNullException.ThrowIfNull(context);
Context = context;
}private IApplicationSecurityContext Context { get; } ...
}
// MooseDBContext assembly:
public class MooseDBContext : DbContext, IApplicationSecurityContext
{
...
}Then register the interfaces as services:
services.AddDbContext(options =>
{
options.UseSqlServer(_connString);
});// When we request the IApplicationSecurityContext service,
// return the registered MooseDBContext service:
services.AddScoped(sp => sp.GetRequiredService());You could even create a utility method to automatically register all interfaces implemented by your context:
public static IServiceCollection AddContextInterfaces(this IServiceCollection services) where TContext : DbContext
{
ArgumentNullException.ThrowIfNull(services);foreach (Type t in typeof(TContext).GetInterfaces().Except(typeof(DbContext).GetInterfaces())) { services.AddScoped(t, sp => sp.GetRequiredService()); } return services;
}
...
services.AddDbContext(options =>
{
options.UseSqlServer(_connString);
});services.AddContextInterfaces();
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
-
Add it as a constructor parameter. Or are you saying that the
ApplicationSecurity
is defined in an assembly which doesn't have a reference to the assembly where theMooseDBContext
is defined? If that's the case, then you probably want the context to implement an interface:// ApplicationSecurity assembly:
public interface IApplicationSecurityContext
{
DbSet<YourEntityType> YourEntityTypes { get; }
}public class ApplicationSecurity : IApplicationSecurity
{
public ApplicationSecurity(IApplicationSecurityContext context)
{
ArgumentNullException.ThrowIfNull(context);
Context = context;
}private IApplicationSecurityContext Context { get; } ...
}
// MooseDBContext assembly:
public class MooseDBContext : DbContext, IApplicationSecurityContext
{
...
}Then register the interfaces as services:
services.AddDbContext(options =>
{
options.UseSqlServer(_connString);
});// When we request the IApplicationSecurityContext service,
// return the registered MooseDBContext service:
services.AddScoped(sp => sp.GetRequiredService());You could even create a utility method to automatically register all interfaces implemented by your context:
public static IServiceCollection AddContextInterfaces(this IServiceCollection services) where TContext : DbContext
{
ArgumentNullException.ThrowIfNull(services);foreach (Type t in typeof(TContext).GetInterfaces().Except(typeof(DbContext).GetInterfaces())) { services.AddScoped(t, sp => sp.GetRequiredService()); } return services;
}
...
services.AddDbContext(options =>
{
options.UseSqlServer(_connString);
});services.AddContextInterfaces();
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
Thanks, that seems to work fine. My class now looks like this:
using Marois.Framework.Core.AppSecurity.Repository;
using Marois.Framework.Core.Crypto;
using Marois.Framework.Core.Shared;
using Microsoft.EntityFrameworkCore;namespace Marois.Framework.Core.AppSecurity.Service
{
public class ApplicationSecurity : IApplicationSecurity
{
#region Private Fields
private IApplicationSecurityContext _context { get; }
#endregion#region CTOR public ApplicationSecurity(IApplicationSecurityContext context) { ArgumentNullException.ThrowIfNull(nameof(context)); \_context = context; } #endregion #region Public Methods public Task LoginAsync(CredentialsEntity credentials) { UserEntity? results = null; ArgumentNullException.ThrowIfNull(nameof(credentials)); var t = Task.Run(() => { var userRepo = new AppSecurityRepository((DbContext)\_context); var user = userRepo.Find(x => x.UserName == credentials.UserName); if (Cryptography.VerifyHash(credentials.Password, user.Password, user.Hash)) { results = user; } return results; }); return t; } #endregion }
}
In the Login method, would you create an instance of the repo, or create it in the CTOR and persist it?
In theory, theory and practice are the same. But in practice, they never are.” If it's not broken, fix it until it is. Everything makes sense in someone's mind.
-
Thanks, that seems to work fine. My class now looks like this:
using Marois.Framework.Core.AppSecurity.Repository;
using Marois.Framework.Core.Crypto;
using Marois.Framework.Core.Shared;
using Microsoft.EntityFrameworkCore;namespace Marois.Framework.Core.AppSecurity.Service
{
public class ApplicationSecurity : IApplicationSecurity
{
#region Private Fields
private IApplicationSecurityContext _context { get; }
#endregion#region CTOR public ApplicationSecurity(IApplicationSecurityContext context) { ArgumentNullException.ThrowIfNull(nameof(context)); \_context = context; } #endregion #region Public Methods public Task LoginAsync(CredentialsEntity credentials) { UserEntity? results = null; ArgumentNullException.ThrowIfNull(nameof(credentials)); var t = Task.Run(() => { var userRepo = new AppSecurityRepository((DbContext)\_context); var user = userRepo.Find(x => x.UserName == credentials.UserName); if (Cryptography.VerifyHash(credentials.Password, user.Password, user.Hash)) { results = user; } return results; }); return t; } #endregion }
}
In the Login method, would you create an instance of the repo, or create it in the CTOR and persist it?
In theory, theory and practice are the same. But in practice, they never are.” If it's not broken, fix it until it is. Everything makes sense in someone's mind.
It's a dependency, so I'd be inclined to inject the repo rather than the context. :) NB: Entity Framework supports asynchronous database queries, so it would be better to offer a
FindAsync
method on the repo, and get rid of theTask.Run
call.public class ApplicationSecurity : IApplicationSecurity
{
private AppSecurityRepository _userRepo { get; }public ApplicationSecurity(AppSecurityRepository userRepo) { ArgumentNullException.ThrowIfNull(nameof(userRepo)); \_userRepo = userRepo; } public async Task LoginAsync(CredentialsEntity credentials) { var user = await \_userRepo.FindAsync(x => x.UserName == credentials.UserName); if (user is null) return null; return Cryptography.VerifyHash(credentials.Password, user.Password, user.Hash) ? user : null; }
}
NB2: Why does your user entity have both a
Password
and aHash
property? I'd expect to see a "protected password" property, and possibly a "salt" property, depending on whether the salt is stored as a separate column or combined with the password hash.
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer