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);
Related
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; }
}
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");
}
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; }
}
I need to get data from a table I have created using the entity framework. Right now, the web api project is responsible to generate the table out of test data I've created.
The web api project has a folder called DAL with a BookingsContext class in. Looks like this:
public class BookingsContext : DbContext
{
public BookingsContext() : base("BookingsContext")
{
}
// DbSet to bookings
public DbSet<Booking> Bookings { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
If I create an instant of this class in the controller, I can call the DbSet method to get the bookings from the database.
The problem is that I have created a class library called GetBookings. Its is responsible to get all the bookings from the database.
The class which should get the data from the database table is my GetAllBookingsQuery.
// Generic interface
public interface IQuery<out T>
{
T Execute();
}
class GetAllBookingsQuery : IQuery<List<Booking>>
{
// Get a list of bookings from the database.
public List<Booking> Execute()
{
return null;
}
}
Here I can't make a instans of the DbContext and call the simple method public DbSet Bookings { get; set; }
How should I get the data from the table?
You should create separate project with your DAL then add reference to it in both WebApi project and your class library.
I am using Entity Framework 6 code-first with an existing database, but having problems mapping my entities to the database tables.
Normally, I would use database-first approach and have my entity and context code generated, but using the designer has become a huge pain.
I have set Database.SetInitializer(null) as I do not want EF to change my schema.
Database schema:
Code-first:
public class Project
{
public int ProjectId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
public class ReleaseControlContext : DbContext
{
public ReleaseControlContext()
: base(ConfigurationManager.ConnectionStrings["ReleaseControl"].ConnectionString)
{
Database.SetInitializer<ReleaseControlContext>(null);
}
public DbSet<Project> Projects { get; set; }
}
Calling code:
using(var context = new ReleaseControlContext())
{
var projects = context.Projects.ToList();
}
The following exception is thrown:
SqlException: Invalid object name 'dbo.Projects'.
This is because my database table is Project and not Projects. I don't want to rename my context's DbSet<Project> to "Project" because that would be semantically incorrect.
Question:
Do I have to use the fluent API/data annotations to map between the Project database table and the DbSet<Project> Projects collection?
You can use the
[Table("Project")]
public class Project {
....
}
annotation against the Project entity, or in the OnModelCreating(DbModelBuilder modelBuilder) you can call modelBuilder.Entity<Project>().ToTable("Project");.
Both would do the same thing.
You should define a class (ie:ProjectMap) that inherits from the generic class EntityTypeConfiguration(T) where T is here your Project class.
In this ProjectMap class, you can define explicitly a table mapping :
this.ToTable("Project", "dbo");
The class ProjectMap should be called in the following method of your DbContext class
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ProjectMap());
}