Customise Asp.Net Core ApiAuthorizationDbContext table names - c#

I'm working with Asp.Net Core identity, and I want to change the tables names that are generated by the default ApiAuthorizationDbContext. For the sake of argument, let's say that I want to change AspNetRoleClaims to be MyRoleClaims.
My DbContext looks like this:
public class MyDbContext : ApiAuthorizationDbContext<ApplicationUser>
{
public MyDbContext(
DbContextOptions<MyDbContext> options,
IOptions<OperationalStoreOptions> operationalStoreOptions)
: base(options, operationalStoreOptions)
{
}
public DbSet<MyData> MyData { get; set; }
}
I'm aware that if I inherit directly from IdentityDbContext then I can specify certain entities, but I can't see a way to do that using ApiAuthorizationDbContext. It appears that the only difference between the two are the following tables:
public DbSet<PersistedGrant> PersistedGrants { get; set; }
public DbSet<DeviceFlowCodes> DeviceFlowCodes { get; set; }
So, I suppose I could create my DbContext based on IdentityDbContext, and add these manually, but that feels like I'm re-inventing the wheel. Is there a way to specify these table names, or do I really need to build this manually from IdentityDbContext?

Override OnModelCreating in your ApiAuthorizationDbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ApplicationUser>().ToTable("User");
modelBuilder.Entity<IdentityRole>().ToTable("Role");
modelBuilder.Entity<IdentityUserRole>().ToTable("UserRole");
modelBuilder.Entity<IdentityUserClaim>().ToTable("UserClaim");
modelBuilder.Entity<IdentityUserLogin>().ToTable("UserLogin");
}

Related

cannot perform override in class inheritance in ADO .NET Entity Model edmx

I have to build a .net web application accessing tables of an existing db.
The db uses different tables for different companies: customers in company "ACorp" are stored in table "ACorpCustomers", those in company "B" are stored in table "BCorpCustomers".
Using ADO .NET Entity Model, I created a different Db Context for each Company:
public partial class ACorpContext : DbContext
{
public ACorpContext()
: base("name=ACorpContext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<ACorpCustomer> ACorpCustomers { get; set; }
}
}
The edmx generates also the class
public partial class ACorpCustomer
{
public string Name { get; set; }
public string Phone { get; set; }
}
I created a parent class Customer to be used in the application, with the same properties:
public class ACorpCustomer
{
public virtual string Name { get; set; }
public virtual string Phone { get; set; }
}
I havent't found a way to let the specific entity ACorpCustomers inherit from the parent Customer; the edmx returns the inheritance error, but there is no way to override the properties.
Update
In order to avoid edmx file usage, this is what I finally tried out:
I disabled the __MigrationHistory sql table creation using the AutomaticMigrationsEnabled parameter:
internal sealed class Configuration : DbMigrationsConfiguration<MyDomain.Models.ACorpContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = false;
}
}
I disabled the db initialization in the App.config file setting
disableDatabaseInitialization="true"
Then I added a an ADO .NET Entity Model but chose the "code first from database".
In order to be sure not to change the db from the model, I disabled the DB Initializer:
public ACorpContext()
: base("name=ACorpContext")
{
Database.SetInitializer<ACorpContext>(null);
}
Now I expect to be my responsability to be keep in sync the domain model with the db.
Anyway, I feel sure that in case of misalignment no attempt will be done to modify the db.
Without the edmx, I have no more limitations defining inheritance from an abstract class Customer.
I cannot understand why Visual Studio considers this as "Code First" approach, anyway.
Your definition
public partial class ACorpCustomer
has nothing to do with inheritance. partial is a .NET moderator that signifies that your class definition is a part of the bigger definition. For example if you have your class split between 2 code files. .Net "puts" them together and you endup with one type
Here what you seem need to do is
public abstract class Customer
{
public string Name { get; set; }
public string Phone { get; set; }
}
public class ACorpCustomer : Customer
{
// may be, some unique properties here
}
public class BCorpCustomer : Customer
{
// may be, some unique properties here
}
The properties Name and Phone don't even need to be virtual. Looking back into your title, there is nothing that you need to override. Nothing that I see..
This is trivial in Code-First, which you can (and should) use with an existing database. Just map the single Customer entity to the correct table for each DbContext:
public partial class ACorpContext : MyBaseDbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>().ToTable("ACorpContext");
}
public virtual DbSet<Customer> Customers{ get; set; }
}

Models from stored procedures

I am writing an application where I'm using code first design. The models returned from stored procedures do not map directly to an entity from the database.
The issue I am having is while I'm inheriting from an interface on each entity, I'm unable to use these custom models:
System.InvalidOperationException: Cannot create a DbSet for 'CategoryDetailEntity' because this type is not included in the model for the context.
at Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.get_EntityType() at Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.CheckState()
at Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.get_EntityQueryable() at Microsoft.EntityFrameworkCore.Internal.InternalDbSet1.System.Collections.Generic.IAsyncEnumerable.GetAsyncEnumerator(CancellationToken cancellationToken)
at System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable1.GetAsyncEnumerator() at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable1 source, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToArrayAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
Here is the code in my context file:
// Shouldn't be in the database, this is pulled from a stored procedure
public DbSet<Entities.CustomEntities.CategoryDetailEntity> CategoryDetailEntities { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Exclude models that are not bound to a table
modelBuilder.Entity<Entities.CustomEntities.CategoryDetailEntity>().ToTable(nameof(CategoryDetailEntities), t => t.ExcludeFromMigrations());
}
The entity code:
[NotMapped]
public partial class CategoryDetailEntity : CategoryEntity
{
public int NumProducts { get; set; }
}
CategoryEntity:
[Table("Category")]
public partial class CategoryEntity : BaseEntity
{
public CategoryEntity Parent { get; set; } = null;
public string Icon { get; set; }
}
The base entity has the ID, Created By / Date, Modified By / Date.
Solution:
#JohnM was able to lead me to the solution. I had two issues.
I tried to create a base service class in which my Database models were using. This was the same in my WebAPI, where in the Startup.cs file, I was only using the Base Service to do dependency injection:
services.AddScoped(typeof(IBaseService<>), typeof(BaseService<>));
This meant that the stored procedure was never being called first, only the base class of simple crud functionality.
Once I added all of the services layers explicitly, I was able to call the GetAll function from the correct service as intended.
public CategoryController(ICategoryService service, IMapper mapper) : base(service, mapper) {}
instead of
public CategoryController(IBaseService<CategoryDetailModel> service, IMapper mapper) : base(service, mapper) {}
The accepted Solution solved the other issue, in which using Code First, I wanted to make sure that the model from the Stored Procedure was not included in the migration.
In the Context.cs file:
// Shouldn't be in the database, this is pulled from a stored procedure
public DbSet<CategoryDetailModel> CategoryDetails { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Exclude models that are not bound to a table
modelBuilder.Entity<CategoryDetailModel>().HasNoKey().ToView(null);
}
Using this, I no longer had to mark the Model as [NotMapped]
public partial class CategoryDetailModel : BaseEntity
{
public string Icon { get; set; }
public int NumProducts { get; set; }
public ImageEntity Image { get; set; }
}
For calling stored procedures I define a regular class to hold the results - so one property for each column in the result set. I then define it as a keyless entity type, so the following goes in the OnModelCreating() method of your database context class:
modelBuilder.Entity<MyModelClass>().HasNoKey().ToView(null);

No method to map entity to table in entity framework core

I am using entity framework core in a normal .net project, version 4.7. I know I can do this. The problem is that I can't seem to map an entity to a table because the "ToTable" method doesn't exist. I can't edit the poco or entity classes because they are predefined and generated. So I can't use the attribute. I looked on the internet and everyone seems to use this method to map an entity to a table.
Here is my code:
public class FactsDbContext : DbContext
{
public DbSet<TblIncident> TblIncidents { get; set; }
public DbSet<TblAction> TblActions { get; set; }
public DbSet<TblAddressTypeAlias> TblAddressTypeAliases { get; set; }
public DbSet<TblCountry> TblCountries { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
//these methods don't exist in my case
modelBuilder.Entity<TblIncident>(entity => entity.ToTable("Incident"));
modelBuilder.Entity<TblIncident>().ToTable("Incident");
}
}
I also tried to use IEntityTypeConfiguration with a EntityTypeBuilder but it still don't have access to the map to table method:
public class IncidentConfig : IEntityTypeConfiguration<TblIncident>
{
public void Configure(EntityTypeBuilder<TblIncident> builder)
{
builder.ToTable("Incident");
}
}
I looked into the Entity Framework Core repository on GitHub and searched for the method "Totable" inside the repository. It turns out it is defined as an extension method but it is in separate nuget package and library called Microsoft.EntityFrameworkCore.SqlServer
After I downloaded the package I got the Totable method that I need. Still it doesn't make sense to add that method in a separate package for sql server when you already have the "Table" attribute that you can add on entities directly in the entity framework core package.
You can use the below approach. You have to use Table data annotation.
DBContext:
public virtual DbSet<Article> Article { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Article>(b =>
{
b.Property(e => e.Property1).HasDefaultValue(true);
... //Other properties
}
Model class:
[Table("Article")]
public class Article
{
You can also use to ToTable in DBContext, but you have to make sure that you have included using Microsoft.EntityFrameworkCore;.
Line modelBuilder.Entity<TblIncident>().ToTable("Incident"); looks correct according to the documentation.
https://learn.microsoft.com/en-us/ef/core/modeling/relational/tables#fluent-api
It's very old thread but I got the same issue and I solved it by placing base.OnModelCreating(builder) as a first line of OnModelCreating method.
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Rest of the code
}

Entity Framework entity can't be found in dbcontext auto-generated code

I use entity framework 6.1.3 model first approach.
I needed to design a simple model as following.
Then i generated sql to create the database but i found that my entities sets were not defined in my DbContext. I tried to add them manually but Entity Framework keeps regenerating the following code.
It only generates the abstract entity which is exactly the opposite of what it is designed for.
public partial class UploadsDataModelContainer : DbContext
{
public UploadsDataModelContainer()
: base("name=UploadsDataModelContainer")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public virtual DbSet<AzureBlobs> AzureBlobs { get; set; }
}
Is this a bug in EF code generator or am i missing a step in model creation ?
Thanks to RicardoPeres i added a class file with the following content and it resolved my problem by avoiding the automatic updates of the DbContext.cs file :
public partial class UploadsDataModelContainer : DbContext
{
public virtual DbSet<Chunk> CHunks { get; set; }
public virtual DbSet<Upload> Uploads { get; set; }
}

Entity Framework 6, cannot use Ignore method on the property

Currently, I'm using ASP Identity with MVC 5.I want to remove phone number field from the AspNetUsers table, but when I use add-migration command it causes the following error.
You cannot use Ignore method on the property 'PhoneNumber' on type
'Models.DbModel.User' because this type inherits from the type
'Microsoft.AspNet.Identity.EntityFramework.IdentityUser`
I have already read tons of questions on here, but all of them said you have to ignore property in your base class, however, I don't have any access to the base in this case.
How can I solve this problem?
Update: when I used fluent API inside the OnModelCreating method it worked, I don't want to use it this way so I separated the config class for each entity.
Below is my code:
Derived Entity Class
public class User: IdentityUser
{
public ICollection<Comment> Comments { get; set; }
}
Config class
public sealed class UserConfig : EntityTypeConfiguration<User>
{
public UserConfig()
{
ToTable("dbo", "Users");
Ignore(x => x.PhoneNumber);
Ignore(x => x.PhoneNumberConfirmed);
}
}
Context Class
public class WebsiteContext : IdentityDbContext
{
public WebsiteContext()
: base("XYZ")
{
}
public DbSet<Comment> Comment { get; set; }
//public DbSet<User> User { get; set; }
public static WebsiteContext Create()
{
return new WebsiteContext();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new CommentConfig());
modelBuilder.Configurations.Add(new UserConfig());
}
}
Try the [NotMapped] attribute from
System.ComponentModel.DataAnnotations.Schema
This might get you around that limitation , it has been used to ignore Enums in the mapping, this might not be exactly what you want

Categories