DbContext.set() cannot create a DbSet for entity because this type is not included in the model for the context - asp.net-core

I am using EF Core. I am using DbContext.Set() method but it is giving me the error - "Cannot create a DbSet for 'MediaDate' because this type is not included in the model for the context.'"
Below is my code:
var context = new GoldentaurusContext();
DbSet<MediaDate> set = context.Set<MediaDate>();
mediaDateList = set.FromSql("[dbo].[sp_GetMediaDate]")
.Select(x => new SelectListItem { Text = x.DateText, Value = x.DateValue })
.ToList();
The MediaDate class:
public class MediaDate
{
public string DateText { get; set; }
public string DateValue { get; set; }
}
Why it is requiring me to add the MediaDate class to the DbContext class?
Please help what I am doing wrong?

Your DB Context class should be like below.
public partial class DatabaseContext : DbContext
{
public DatabaseContext (string ConnectionString) : base(new DbContextOptionsBuilder().UseSqlServer(ConnectionString).Options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Query<MediaData>();
}
Add your models in the DatabaseContext using model builder.
This is how I have resolved this isssue

For EF in DotNet Core 3.1+ make sure you add your non-table entity to the OnModelCreating override on your DbContext and call .HasNoKey() on the fluent API. This will allow you to call a stored procedure using DataContext.Set<MediaData>().FromSqlRaw("dbo.MyStoredProc") and return your entity list.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<MediaData>().HasNoKey();
}

A simple DatabaseContext would look like this:
using YourProject.Model;
using System.Data.Entity;
namespace YourProject.Data
{
public class DatabaseContext : DbContext
{
public DatabaseContext() :
base("name=YourDatabase")
{
}
public DbSet<MediaData> MediaDates{ get; set; }
}
}
You always need to include your models in the DatabaseContext to create the DbSets. Make sure you've declared the right namespaces and imported the right ones.

First, you should introduce your model (MediaDate) to DbContext.
add a DbSet<MediaDate> property to your context:
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public DbSet<MediaDate> MediaDates { get; set; }
}

Related

Extending EF Core Identity UserStore with integer user ID

Using Blazor server, dotnet 5 and Entity Framework Core, I have successfully customised Identity using integer IDs
e.g
public class ApplicationUser : IdentityUser<int>
{
[Required]
[MaxLength(50)]
public string FirstName { get; set; }
[Required]
[MaxLength(50)]
public string LastName { get; set; }
}
I now want to extend UserStore for custom login to store password history to prevent duplication. The documented way of doing this seems to be:
public class ApplicationUserStore : UserStore<ApplicationUser>
{
public ApplicationUserStore(ApplicationDbContext context, IdentityErrorDescriber describer = null) : base(context, describer)
{
}
public override async Task<IdentityResult> CreateAsync(ApplicationUser appUser)
{
await base.CreateAsync(appUser);
await AddToUsedPasswordAsync(appUser, appUser.PasswordHash);
}
public Task AddToUsedPasswordAsync(ApplicationUser appuser, string userpassword)
{
appuser.UserUsedPassword.Add(new UsedPassword() { UserID = appuser.Id, HashPassword = userpassword });
return UpdateAsync(appuser);
}
}
This works for the default GUID Id but when using an integer it throws the error:
The type ApplicationUser cannot be used as type parameter 'TUser' in the generic type or method 'UserStore'. There is no implicit reference conversion from ApplicationUser' to 'Microsoft.AspNetCore.Identity.IdentityUser'.
What is the correct way to do this?
EDIT:
Per #Yinqiu, modified for custom ApplicationRole to
public class ApplicationUserStore : UserStore<ApplicationUser, ApplicationRole, ApplicationDbContext, int>
{
public ApplicationUserStore(ApplicationDbContext context, IdentityErrorDescriber describer = null) : base(context, describer)
{
}
Builds successfully and creates users but gives run time error when trying to access ApplicationUserManager.IsInRoleAsync:
{"Cannot create a DbSet for 'IdentityUserRole' because this type is not included in the model for the context."}
However I have a custom User Role:
public class ApplicationUserRole : IdentityUserRole<int>
{
public ApplicationUserRole() : base() { }
public bool IsSystemEssential { get; set; }
}
With a definition in ApplicationDbContext :
public DbSet ApplicationUserRoles { get; set; }
SOLVED (provisionally)
After looking at the overloaded inheritance options you have to extend as follows:
public class ApplicationUserStore : UserStore<ApplicationUser, ApplicationRole, ApplicationDbContext, int, ApplicationUserClaim, ApplicationUserRole, ApplicationUserLogin, ApplicationUserToken, ApplicationRoleClaim>
{
public ApplicationUserStore(ApplicationDbContext context, IdentityErrorDescriber describer = null) : base(context, describer)
{
}
}
This seems to work. Still need to check all role and claim updates.
You can change your ApplicationUserStore as following.
public class ApplicationUserStore : UserStore<ApplicationUser,IdentityRole<int>, ApplicationDbContext,int>
{
public ApplicationUserStore(ApplicationDbContext context, IdentityErrorDescriber describer = null) : base(context, describer)
{
}
//...
}

Current user (ASP.NET Identity) information within Generic repository pattern

I have implemented generic repository pattern over an EF Core context, for example;
public class GenericRepository<TEntity, TContext> : IGenericRepository<TEntity>
where TEntity : EntityBase
where TContext : DbContext
{
internal TContext context;
internal DbSet<TEntity> dbSet;
public GenericRepository(TContext context)
{
this.context = context;
dbSet = context.Set<TEntity>();
}
public virtual TEntity GetById(long id)
{
return dbSet.Find(id);
}
public virtual void Insert(TEntity entity)
{
entity.CreatedDate = DateTime.Now;
dbSet.Add(entity);
}
//... additional methods removed for brevity
}
All my models are using an EntityBase class that allows me to record when the record was created and by whom, for example;
public abstract class EntityBase {
public long Id { get; set; }
public DateTime CreatedDate { get; set; }
public string CreatedBy { get; set; }
//... additional fields removed for brevity
}
What are the different ways to access the current user within the repository, so I can add the user to the entity on creation;
public virtual void Insert(TEntity entity)
{
entity.CreatedBy = CurrentUser.Name; // TODO: get current user here?
entity.CreatedDate = DateTime.Now;
dbSet.Add(entity);
}
It feels like I should be able to do this via middleware and or injection.
I think, you should pass user information from controller to the repository method or set CreatedBy value inside the controller instead of Repository method. Moreover , in my opinion you should avoid depend your repository to the identity and keep it simple and independent.
In other words, referencing HttpContext.User.Identity inside your repository is not a good idea,and HttpContext belog to the Presentation Layer not Data Layer.

ASP.NET Core: DbSet to executing raw stored procedure

I am using ASP.NET Core 2.0 and razor pages. The SQL table does not have a primary key and I will not be able to make changes to the SQL table. I am trying to use a stored procedure to retrieve the data from the table and show the resultant data in UI.
Since there is no primary key available, I am getting error as - Microsoft.EntityFrameworkCore.Infrastructure.ModelValidator.ValidateNonNullPrimaryKeys. I would like to move the code from DBSet to Raw sql as defined in https://www.learnentityframeworkcore.com/raw-sql
Below is my existing code :
//Data - myDbContext
public class MyDbContext : DbContext
{
public DbSet<LOB> lobs { get; set; }
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
{
}
}
// Model
public class LOB
{
public string Desc { get; set; }
}
//Index.cshtml.cs
public class IndexModel : PageModel
{
private readonly MyDbContext _dbContext;
public IndexModel(MyDbContext dbContext)
{
_dbContext = dbContext;
}
public List<LOB> lOBs { get; set; } = new List<LOB>();
[BindProperty]
public string[] SelectedLOBs { get; set; }
public SelectList LOBOptions { get; set; }
public async Task OnGetAsync()
{
lOBs = await _dbContext.Set<LOB>().FromSql(
"EXECUTE sp")
.AsNoTracking()
.ToListAsync();
LOBOptions = new SelectList(lOBs, "Desc1");
}
}
// Index.cshtml
<select class="form-control" required multiple id="selLOB" asp-for="SelectedLOBs" asp-items="Model.LOBOptions"></select>
How to fill the dropdown using context.database property ?
Thanks
For Asp.Net Core and EF Core are different. Asp.Net Core 2.0 is corresponding to Microsoft.AspNetCore.All 2.0 and EF Core 2.1 is correspoinding to Microsoft.EntityFrameworkCore 2.1, you could refer Microsoft.EntityFrameworkCore 2.1 in Microsoft.AspNetCore.All 2.0.
Follow steps below to resolve your issue.
Update package Microsoft.EntityFrameworkCore to V2.2.3 and Microsoft.EntityFrameworkCore.Tools to V2.2.3
Change DbSet to DbQuery
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbQuery<LOB> lobs { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
Change your razor code.
public async Task OnGetAsync()
{
lOBs = await _dbContext.Query<LOB>().FromSql(
"EXECUTE sp")
.AsNoTracking()
.ToListAsync();
LOBOptions = new SelectList(lOBs, "Desc", "Desc", "Desc1");
}
Change your view
<select class="form-control" required multiple id="selLOB"
asp-for="SelectedLOBs" asp-items="Model.LOBOptions">
</select>

exception:"type was not mapped" in entityframework codefirst with layers

i'm trying to apply LAYERS Concept on demo project developed using mvc and entity framework both
Data Annotations : for validations in Data Access Layer and
Fluent API : for mapping and tables relations
Problem : DbContext didn't Create DB and there is a Runtime Exception :
The type 'Domain.DataLayer.Member' was not mapped. Check that the type has not been explicitly excluded by using the Ignore method or NotMappedAttribute data annotation. Verify that the type was defined as a class, is not primitive, nested or generic, and does not inherit from EntityObject.
Code : my solutions consists of :
1- class library (Domain.Classes project): where i wrote all of my classes
public class Member
{
public int Id { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public string FullName { get; set; }
}
2- DAL (Domain.DataLayer project): also another class library and i referenced domain.classes
namespace Domain.DataLayer.Repositories
{
[MetadataType(typeof(MemberMetadata))]
public partial class Member : Classes.Member , IValidatableObject
{
public Member()
{
Tasks = new HashSet<Task>();
History = new HashSet<Commint>();
}
public string ConfirmPassword { get; set; }
public HashSet<Task> Tasks { get; set; }
public HashSet<Commint> History { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var result = new List<ValidationResult>();
if (!string.Equals(Password,ConfirmPassword))
{
result.Add(new ValidationResult("mismatch pwsd", new[] {"ConfirmPassword" }));
}
return result;
}
}
}
and i used repository pattern :
public class MemberRepository : IRepository<Member>
{
public Task<IQueryable<Member>> GetAllEntities()
{
return Task<IQueryable<Member>>.Factory.StartNew(() => new Context().Members.AsQueryable());
}
}
3-BLL : for sake of simplicity : there is no Business Logic Layer
4- PL (Domain.Application MVC Project) : Member Controller :
public async Task<ActionResult> Index()
{
var members = await _repository.GetAllEntities();
return View(members);
}
Note : i depended on DbContext to create DB with name like : Domain.DataLayer.Context but it didn't craete DB so i created the DB and passed the connectionString through Context constructor like this :
namespace Domain.DataLayer
{
public class Context : DbContext
{
public Context(): base("InterviewDemo") // i tried also base("name=InterviewDemo")
{
}
public DbSet<Member> Members { get; set; }
public DbSet<Task> Tasks { get; set; }
public DbSet<Commint> TaskHistory { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new MemberConfig());
modelBuilder.Configurations.Add(new TaskConfig());
modelBuilder.Configurations.Add(new CommintConfig());
base.OnModelCreating(modelBuilder);
}
}
}

Map tables using fluent api in asp.net MVC5 EF6?

I am trying to add profile/Membership information into my MVC5 application and adding configuration mappings.
I get the following error message:
my.Models.IdentityUserLogin: : EntityType 'IdentityUserLogin' has no
key defined. Define the key for this EntityType.
my.Models.IdentityUserRole: : EntityType 'IdentityUserRole' has no key
defined. Define the key for this EntityType.
IdentityUserLogins: EntityType: EntitySet 'IdentityUserLogins' is
based on type 'IdentityUserLogin' that has no keys defined.
IdentityUserRoles: EntityType: EntitySet 'IdentityUserRoles' is based
on type 'IdentityUserRole' that has no keys defined.
public class ApplicationUser : IdentityUser
{
public string City { get; set; }
public string Discriminator { get; set; }
public string Address { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new ApplicationUserConfiguration());
}
}
Calling base.OnModelCreating(modelBuilder) did not solve the issue for me.
The behavior of Microsoft.AspNet.Identity.EntityFramework seems to be different in VS2013-Preview, VS2013-RC, and VS2013-RTM. I'm using the RTM version.
After inheriting from IdentityUser I had to recreate all other primary keys in the model to make it work:
public class ApplicationUser : IdentityUser
{
public string DisplayName { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext() : base("DefaultConnection") { }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<IdentityUserLogin>().HasKey<string>(l => l.UserId);
modelBuilder.Entity<IdentityRole>().HasKey<string>(r => r.Id);
modelBuilder.Entity<IdentityUserRole>().HasKey(r => new { r.RoleId, r.UserId });
}
(See Configuring/Mapping Properties and Types with the Fluent API)
I guess work on AspNet.Identity.EntityFramework is ongoing and this will be fixed (?)
Call base.OnModelCreating(modelBuilder) after configuration added.
Follow these steps around OnModelCreating method and test after each one to be aware of taking effect:
Make sure you have one Context to prevent of their rule conflicts.
Call base.OnModelCreating(modelBuilder); inside the mentioned
method (first of all)
Add the followings plus previews step in the method:
modelBuilder.Entity<IdentityUserLogin>().HasKey<string>(l => l.UserId);
modelBuilder.Entity<IdentityRole>().HasKey<string>(r => r.Id);
modelBuilder.Entity<IdentityUserRole>().HasKey(r => new { r.RoleId, r.UserId });