Filter ApplyConfigurationsFromAssembly - c#

I have a requirement to create db context with a specific type of entity since i have multiple Db context on the solution.my problem is OnModelCreating we apply Configuration from the assembly builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly()). So i want to apply configuration with only BaseEntityConfig that inheritance from IEntityTypeConfiguration
this is one of my Config class
public class UnitEntityConfig : BaseEntityConfig<Unit,long>
{
public override void Configure(EntityTypeBuilder<Unit> builder)
{
base.Configure(builder);
builder.ToTable("TB_UNIT");
builder.Property(a => a.Name)
.HasColumnName("NAME")
.HasMaxLength(300)
.IsRequired();
}
}
and this my BaseEntityConfig
public class BaseEntityConfig<T,TId> : IEntityTypeConfiguration<T> where T : BaseEntity<TId>
{
public virtual void Configure(EntityTypeBuilder<T> builder)
{
// some code
}
}

Related

I can not add migration when I want add Identity to my project in ASP.NET Core

I want to use identity in my project. I did create identity DbContext and also I did add Identity services to my startup file
But when I want create a new migration I see this error:
Unable to create an object of type context name for the deferent design patterns supported at design time
My context code here:
public class websitecontext : IdentityDbContext
{
public websitecontext(DbContextOptions<websitecontext> options) : base(options)
{
}
}
My startup codes here
services.AddDbContext<websitecontext>(s =>
s.UseSqlServer(Configuration.GetConnectionString("websiteconnectionstring"))
);
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<websitecontext>()
.AddDefaultTokenProviders();
Please helps me I did search in google but I didn't find any results
I solved the problem
I did add seed data in my context but I did not pass this to identity dbcontext and now I did pass this with :
base.OnModelCreating(modelBuilder);
I Resolved this by just adding a plain constructor to my Context
public class DataContext : DbContext
{
public DataContext()
{
}
public DataContext(DbContextOptions options) : base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
if (!options.IsConfigured)
{
options.UseSqlServer("A FALLBACK CONNECTION STRING");
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}

EF Core InMemoryDatabase - How to test with Keyless Entity (mapped to Sql View) - XUnit

Environment: .Net Core 3.1 REST API / EntityFrameworkCore.InMemory 3.1.6 / XUnit 2.4.1
In a Database First Setup I have a model mapped to a Sql View.
During Code Generation (with EF Core PowerTools 2.4.51) this entity is marked in DbContext with .HasNoKey()
When I try to test the endpoint accessing the DbSet mapped to the Sql View it throws exception:
Unable to track an instance of type '*' because it does not have a primary key. Only entity types with primary keys may be tracked.
Follows some code snippets with highlights of what I have tries so far.
Auto generated DbContext: ViewDemoAccountInfo is the entity mapped to a Sql View. Other entities are mapped to regular Sql Tables
// <auto-generated> This file has been auto generated by EF Core Power Tools. </auto-generated>
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata;
namespace Demo.Data.Entities
{
public partial class DemoDbContext : DbContext
{
public DemoDbContext(){}
public DemoDbContext(DbContextOptions<DemoDbContext> options): base(options){}
public virtual DbSet<ViewDemoAccountInfo> ViewDemoAccountInfo { get; set; }
// multiple other entities
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<ViewDemoAccountInfo>(entity =>
{
entity.HasNoKey();
entity.ToView("ViewDemoAccountInfo");
entity.Property(e => e.AccountType).IsUnicode(false);
});
OnModelCreatingPartial(modelBuilder);
}
partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
}
}
Attempt #1:
The test
public class MyIntegrationTests : BaseIntegrationTest {
// throws "Unable to track an instance of type 'ViewDemoAccountInfo'
// because it does not have a primary key. Only entity types with primary keys may be tracked."
[Fact]
public async Task SetupData_WhenKeylessEntity_ThenShouldNotThrow() {
using (var scope = _testServer.Host.Services.CreateScope()) {
var dbContext = scope.ServiceProvider.GetService<DemoDbContext>();
await dbContext.ViewDemoAccountInfo.AddAsync(MockedAccountInfo); // setup some data
await dbContext.SaveChangesAsync();
}
var endpointUrl = $"{ControllerRootUrl}/account-info";
var response = await _testClient.GetAsync(endpointUrl);
// Assertions
}
}
Helpers
public class BaseIntegrationTest {
protected readonly HttpClient _testClient;
protected readonly TestServer _testServer;
public BaseIntegrationTest() {
var builder = new WebHostBuilder()
.UseEnvironment("Test")
.ConfigureAppConfiguration((builderContext, config) => {
config.ConfigureSettings(builderContext.HostingEnvironment);
});
builder.ConfigureServices(services => {
services.ConfigureInMemoryDatabases(new InMemoryDatabaseRoot());
});
builder.UseStartup<Startup>();
_testServer = new TestServer(builder);
_testClient = _testServer.CreateClient();
}
}
// Regular DbContext
internal static class IntegrationExtensions {
public static void ConfigureInMemoryDatabases(this IServiceCollection services, InMemoryDatabaseRoot memoryDatabaseRoot) {
services.AddDbContext<DemoDbContext>(options =>
options.UseInMemoryDatabase("DemoApp", memoryDatabaseRoot)
.EnableServiceProviderCaching(false));
}
}
The simplest solution is to change the Auto generated DbContext and remove the .HasNoKey() config, but it would be removed each time the schema structure will be generated with EF Core PowerTools.
Search for other solutions which would not require changes in Auto generated files
Found: how to test keyless entity - github discussion planned for EF Core 5 and stackoverflow source
Attemp #2 - Try to create another DbContext and override the entity setup by adding explicitly a key when Database.IsInMemory
public class TestingDemoDbContext : DemoDbContext {
public TestingDemoDbContext(){}
public TestingDemoDbContext(DbContextOptions<DemoDbContext> options): base(options){}
protected override void OnModelCreating(ModelBuilder modelBuilder) {
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ViewDemoAccountInfo>(entity => {
if (Database.IsInMemory()) {
entity.HasKey(e => new { e.AccountType, e.AccountStartDate });
}
});
}
}
In BaseIntegrationTest use the "extended" TestingDemoDbContext in ConfigureInMemoryDatabases method.
internal static class IntegrationExtensions {
public static void ConfigureInMemoryDatabases(this IServiceCollection services, InMemoryDatabaseRoot memoryDatabaseRoot) {
services.AddDbContext<TestingDemoDbContext>(options =>
options.UseInMemoryDatabase("DemoApp", memoryDatabaseRoot)
.EnableServiceProviderCaching(false));
}
}
The test is similar, with a small difference: var dbContext = scope.ServiceProvider.GetService<TestingDemoDbContext>();
Result - strange, but it throws The string argument 'connectionString' cannot be empty. - even I do use the InMemoryDatabase
Attemp #3 - Try to use the OnModelCreatingPartial method to add a Key for that keyless entity.
So, in the same namespace with the Regular DbContext, create the partial DbContext meant to enrich the existing config
namespace Demo.Data.Entities {
public partial class DemoDbContext : DbContext {
partial void OnModelCreatingPartial(ModelBuilder builder) {
builder.Entity<ViewDemoAccountInfo>(entity => {
// try to set a key when Database.IsInMemory()
entity.HasKey(e => new { e.AccountType, e.AccountStartDate }));
});
}
}
}
Result - The key { e.AccountType, e.AccountStartDate } cannot be added to keyless type 'ViewDemoAccountInfo'.
Any hints on how to add some mock data for Keyless entities (mapped to Sql View), with InMemoryDatabase, for testing purpose (with XUnit) would be grateful appreciated.
As well, if something is wrong or is considered bad practice in the setup I have listed here - would appreciate to receive improvement suggestions.
I know this is an old post, but I wanted to share the solution I ended up using for this in case anyone else comes across here.
In my model class, I added a [NotMapped] field named 'UnitTestKey'.
KeylessTable.cs
[Table("KeylessTable", Schema = "dbo")]
public class KeylessTable
{
[NotMapped]
public int UnitTestKey { get; set; }
[Column("FakeColName")]
public string FakeColumn { get; set; }
}
In my DbContext class, I use IHostEnvironment and used that to set HasKey() or HasNoKey() depending on if we are in the "Unit Testing" environment.
This example is using .NET 5. If using .NET Core 3.1 like in the original question, you would want to use IWebHostEnvironment.
ContextClass.cs
public class ContextClass : DbContext
{
private readonly IHostEnvironment _environment;
public ContextClass(DbContextOptions<ContextClass> options, IHostEnvironment environment) : base(options)
{
_environment = environment;
}
public DbSet<KeylessTable> KeylessTable => Set<KeylessTable>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<KeylessTable>(entity => {
if (_environment.EnvironmentName == "UnitTesting")
entity.HasKey(x => x.UnitTestKey);
else
entity.HasNoKey();
});
}
}
Then in my unit test, I mock the environment and set the name to be "UnitTesting".
UnitTest.cs
[Fact]
public async void GetKeylessTable_KeylessTableList()
{
// Arrange
var environment = new Mock<IHostEnvironment>();
environment.Setup(e => e.EnvironmentName).Returns("UnitTesting");
var options = new DbContextOptionsBuilder<ContextClass>().UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()).Options;
var contextStub = new ContextClass(options, environment.Object);
contextStub.Database.EnsureDeleted();
contextStub.Database.EnsureCreated();
contextStub.Set<KeylessTable>().AddRange(_keylessTablelMockData);
contextStub.SaveChanges();
var repository = new Repo(contextStub);
// Act
var response = await repository.GetKeylessData();
// Assert
response.Should().BeEquivalentTo(_keylessTablelMockData);
}

How to properly use IPluralizer in IDesignTimeServices

In my app I use code-first approach and I'm adding entities to DbContext only via IEntityTypeConfiguration<>.
My goal is to achieve pluralized table names i.e. Models for Model.
After reading documentation, article, This question
my understading would be that my pluralizer should be registered and as IPluralizer used during creating migration, however it is not.
Of course I could have implicitly use DbSet<Model> Models {get;set;} or use builder.ToTable("Models");, but in my generic scenario I'd like to avoid that, especially as I would like some models not to override abstract generic scenario.
Question is, am I doing something wrong, or I misunderstand the way it should behave
AppDesignService.cs
public class AppDesignService : IDesignTimeServices
{
public void ConfigureDesignTimeServices(IServiceCollection services)
{
Debugger.Launch();
services.AddSingleton<IPluralizer, InflectorPluralizer>();
}
}
MyDbContext.cs
public class AppDbContext : IdentityDbContext<AppUser,AppRole,Guid>
{
public EmsDbContext(DbContextOptions options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ApplyConfigurationsFromAssembly(Assembly.GetAssembly(typeof(AppDbContext)));
}
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
var connectionString = Configuration.GetConnectionString("DefaultConnection");
services.AddDbContext<DbContext, AppDbContext>(opt =>
{
opt.UseSqlServer(connectionString, sqlOpt =>
{
sqlOpt.EnableRetryOnFailure(3);
});
});
// code skipped for brevity
}
Config
public interface IEntity
{
int Id {get;set;}
}
public class Model : IEntity
{
public int Id {get;set;}
public string Name {get;set;}
}
public abstract class DbEntityConfig<T> : IEntityTypeConfiguration<T>
where T : class, IEntity
{
public void Configure(EntityTypeBuilder<T> builder)
{
builder.HasKey(m => m.Id);
}
}
public class ModelEntityConfig : DbEntityConfig<Model>
{
public void Configure(EntityTypeBuilder<Model> builder)
{
base.Configure(builder);
// Of course I want to avoid this call, cos TheOtherModel might not override base method
// builder.ToTable("Models");
builder.Property(m => m.Name).HasMaxLength(25);
}
}
Result
public partial class Test : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Model",
columns: table => new
// code skipped for brevity
}
}
Expected result:
public partial class Test : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Models",
columns: table => new
// code skipped for brevity
}
}
The linked article is incorrect. As you can see from Pluralization hook for DbContext Scaffolding EF Core documentation:
EF Core 2.0 introduces a new IPluralizer service that is used to singularize entity type names and pluralize DbSet names.
Shortly, it is used only by scaffolding commands, hence cannot be used for changing the table name model conventions.
In general migration tools use the model the way it is configured by conventions, data annotations and fluent API. So applying custom convention should be with model API inside OnModelCreating. Something like this:
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
entityType.Relational().TableName = GetTableName(entityType);
where GetTableName method implements your naming convention:
string GetTableName(IEntityType entityType)
{
// use entiityType.Name and other info
return ...;
}
Update (EF Core 3.0+): Use entityType.SetTableName(...) in place of entityType.Relational().TableName = ...

InvalidOperationException: Cannot create a DbSet for 'Role' because this type is not included in the model for the context

The following solution works in .net core 1.1, but after upgrading from 1.1 to 2.0, I received the following error:
InvalidOperationException: Cannot create a DbSet for 'Role' because this type is not included in the model for the context.
When the user attempts to log in and the following statement is executed:
var result = await _signInManager.PasswordSignInAsync(model.Email,
model.Password, model.RememberMe, lockoutOnFailure: false);
What is wrong?
User.cs
public partial class User : IdentityUser<Guid>
{
public string Name { get; set; }
}
IdentityEntities.cs
public partial class UserLogin : IdentityUserLogin<Guid>
{
}
public partial class UserRole : IdentityUserRole<Guid>
{
}
public partial class UserClaim : IdentityUserClaim<Guid>
{
}
public partial class Role : IdentityRole<Guid>
{
public Role() : base()
{
}
public Role(string roleName)
{
Name = roleName;
}
}
public partial class RoleClaim : IdentityRoleClaim<Guid>
{
}
public partial class UserToken : IdentityUserToken<Guid>
{
}
ConfigureServices
services.AddIdentity<User, Role>
Check that your AppDbContext is NOT inherited from DbContext but instead it should be inherited from IdentityDbContext<ApplicationUser>
Added this and it worked:
builder.Entity<IdentityUserRole<Guid>>().HasKey(p => new { p.UserId, p.RoleId });
The most common reasons for
Cannot create a DbSet for 'THE-MODEL' because this type is not
included in the model for the context
are as follows
The model name does not match with the table name in database
EntityFramework cannot figure out required meta by convention and you have
not overriden it.
in your case Role inherits IdentityRoleClaim and that was not configured and default convention required "Id" as key but i suppose it did not have that property so it had to be configured. It would also have worked if you created property in Role like Id => new{UserId,RoleId} which would by convention present Id as the key property to entity framework.
You can fix the issue by replacing this
public class ApplicationDbContext : IdentityDbContext<AppUser>
with this
public class ApplicationDbContext : IdentityDbContext<AppUser, AppRole, string>
notice this string type in the last argument.
If your DbContext doesn't inherit IdentityUserContext - don't use AddEntityFrameworkStores in the ConfigureServices method. Substitute it with AddUserStore and AddRoleStore as follows:
public void ConfigureServices(IServiceCollection services)
{
...
services
.AddIdentity<MyUserType, MyRoleType>(...)
.AddUserStore<UserStore<MyUserType, MyRoleType, MyDbContextType, MyKeyType, MyUserClaimType, MyUserRoleType, MyUserLoginType, MyUserTokenType, MyRoleClaimType>>()
.AddRoleStore<RoleStore<MyRoleType, MyDbContextType, MyKeyType, MyUserRoleType, MyRoleClaimType>>();
...
}
If you check the implementation of the AddEntityFrameworkStores method you'll see that it adds stores via searching for generic types in the DbContext, assuming that it inherits IdentityUserContext. Regardless, you'll be stuck with the base Identity* types for claims ... which will generate this exception.
I fixed this by adding:
public DbSet<ApplicationRole> ApplicationRoles { get; set; }
to my DbContext.
I had the similar problem, this was from the bad configuration for IUserStore in my case. I'm using Autofac for dependency injection and this configuration solved my problem:
var dbContextParameter = new ResolvedParameter((pi, ctx) => pi.ParameterType == typeof(IdentityDbContext),
(pi, ctx) => ctx.Resolve<DatabaseContext>());
builder.RegisterType<UserStore<User, Role, DatabaseContext, Guid,UserClaim,UsersInRole,UserLogin,UserToken,RoleClaim>>()
.As<IUserStore<User>>().WithParameter(dbContextParameter).InstancePerLifetimeScope();
builder.RegisterType<CustomRoleStore>()
//RegisterType<UserStore<User, Role, DatabaseContext, Guid,UserClaim,UsersInRole,UserLogin,UserToken,RoleClaim>>()
.As<IRoleStore<Role>>().WithParameter(dbContextParameter).InstancePerLifetimeScope();
My databaseContext drives from IdentityDbContext
public class DatabaseContext : IdentityDbContext<User, Role, Guid, UserClaim
, UsersInRole, UserLogin, RoleClaim, UserToken
>, IDatabaseContext
and all I had to do was to Create a Userstore and give it to my DI to inject into signinmanager class
Add your models in the DBContext using model builder.
Write your DB Context class as below.
public partial class CDBContext : DbContext
{
public CDBContext (string ConnectionString) : base(new DbContextOptionsBuilder().UseSqlServer(ConnectionString).Options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Query<MediaData>();
}
In my case I passed wrong class to EF DbContext. I had 2 classes, first one "Accounting" and the second one "Account". I sent "Accounting" , which EF-Dbcontext didn't have the entity and returned
Cannot create a DbSet for 'Accounting' because this type is not included in the model for the context.
Please check your class, which you want to send to your DbContext.
You need to check that your AppDbContext is inherited from IdentityDbContext or not. Your AppDbContext must inherit from IdentityDbContext.
Then OnModelCreating you need to map relations like in below -
builder.Entity<ApplicationUserRole>(userRole =>
{
userRole.HasKey(ur => new { ur.UserId, ur.RoleId });
userRole.HasOne(ur => ur.Role)
.WithMany(r => r.UserRoles)
.HasForeignKey(ur => ur.RoleId)
.IsRequired();
userRole.HasOne(ur => ur.User)
.WithMany(r => r.UserRoles)
.HasForeignKey(ur => ur.UserId)
.IsRequired();
});
Overall Application Db Context will looks like -
using Core.Entities.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
namespace Infrastructure.Identity
{
public class AppIdentityDbContext : IdentityDbContext<AppUser, ApplicationRole,
string, IdentityUserClaim<string>,
ApplicationUserRole, IdentityUserLogin<string>, IdentityRoleClaim<string>,
IdentityUserToken<string>>
{
public AppIdentityDbContext(DbContextOptions<AppIdentityDbContext> options) :
base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<ApplicationUserRole>(userRole =>
{
userRole.HasKey(ur => new { ur.UserId, ur.RoleId });
userRole.HasOne(ur => ur.Role)
.WithMany(r => r.UserRoles)
.HasForeignKey(ur => ur.RoleId)
.IsRequired();
userRole.HasOne(ur => ur.User)
.WithMany(r => r.UserRoles)
.HasForeignKey(ur => ur.UserId)
.IsRequired();
});
}
}
}
Hope your problem will resolve.
I had the same issue and I found that I didn't include it in the RoleStore and in the UserStore
so, You need to do the following
In the ApplicationDbContext
public class ApplicationDbContext: IdentityDbContext<User, Role, GUID, UserClaim, UserRole, UserLogin, RoleClaim, UserToken>
{}
You need to add Role, UserRole in the stores as follows:
create a class that inherits from RoleStore as follows
public class AppRoleStore : RoleStore<Role, ApplicaitonDbContext, GUID, UserRole, RoleClaim>{}
2)Create class for the UserStore as follow
public class AppUserStore : UserStore<User, Role, ApplicationDbContext, GUID, UserClaim, UserRole, UserLogin, UserToken, RoleClaim>
{}
I had this error as well, guide to get it working using EF Core 5.0 and EF Core 6.0 here:
https://stackoverflow.com/a/71321924/3850405
Also had this problem. I was calling a stored procedure and passing back a scalar value by name that did not correspond to table structure at all:
result = db.Set<*Object*>()
.FromSqlRaw($"EXECUTE [dbo].[*StoredProcedure*] {*var*}")
.AsEnumerable()
.First().*ScalarValueFromProcedure*;
Object name differed from the table DbSet name. It worked initially, but somehow broke along the way. Creating a DbSet<Object> fixed it.
I had this problem. To fixed it, I did some changes in my Entity Config Class.
public class AspNetUserClaimsConfig : IEntityTypeConfiguration<IdentityUserClaim<string>>
{
public void Configure(EntityTypeBuilder<IdentityUserClaim<string>> builder)
{
builder.ToTable("AspNetUserClaims", "Identity");
// Primary key
builder.HasKey(uc => uc.Id);
}
}
In my context I did this:
public class IdentityContext : DbContext
{
public IdentityContext(DbContextOptions<IdentityContext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new ApplicationUsersConfig());
base.OnModelCreating(modelBuilder);
}
}

entity framework 6.1 Seed & OnModelCreating not working together

I'm using EF in a Web API project with code first mode.
So far, so good, but I have Time field in a table, where I want Precision 0.
So I added the required codes in the OnModelCreating method of the DBContext class.
after that the Seed method in Configuration class was not executed.
when I comment the code in OnModelCreating, the Seed method is executing.
I would be happy if somebody could give me a reason for this, and how shall I restructure my classes to run both of the methods.
Normally I use the standard way. I have the simplest DbContext:
public partial class HarmoniaContext : DbContext {
public HarmoniaContext()
: base("Harmonia") {
}
...virtual properties or the DbSet<>...
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Entity<Nyitvatartas>()
.Property(e => e.KezdIdo)
.HasPrecision(0);
modelBuilder.Entity<Nyitvatartas>()
.Property(e => e.BefIdo)
.HasPrecision(0);
}
}
and the configuration file
internal sealed class Configuration :DbMigrationsConfiguration<HarmoniaContext> {
public Configuration() {
AutomaticMigrationsEnabled = true;
}
protected override void Seed(HarmoniaContext context) {
context.Beallitas.AddOrUpdate(b => b.Nev, ...}
context.Szerepkor.AddOrUpdate(sz=> sz.RendszerNev, ...}
}
}

Categories

Resources