ASP.NET Core: DbSet to executing raw stored procedure - asp.net-core

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>

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)
{
}
//...
}

Entity Framework Core multiple DbContext with cross reference

I'm trying to create a modular project in ASP.NET Core with Entity Framework Core. So, my plan is to have a base project which contains things like auth config, identity model. There would be multiple feature module project which can be activated depends on what the costumer bought.
The problem is how to solve the migration in the database. Modules would be all separated but they might needs base project. For example if the module want to reference a user for a record in the database.
I created a CoreDbContext (in the base project).
public class CoreDbContext : DbContext
{
public DbSet<WeatherForecast> WeatherForecasts { get; set; }
public CoreDbContext()
{ }
public CoreDbContext(DbContextOptions<CoreDbContext> options) : base(options)
{ }
}
public class WeatherForecast
{
public int Id { get; set; }
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public string Summary { get; set; }
}
MeasurementsDbContext (this is the feature module project)
public class MeasurementsDbContext : DbContext
{
public DbSet<Measurement> Measurements { get; set; }
public MeasurementsDbContext()
{ }
public MeasurementsDbContext(DbContextOptions<MeasurementsDbContext> options) : base(options)
{ }
}
public class Measurement
{
public int Id { get; set; }
public int Value { get; set; }
public WeatherForecast Weather { get; set; } //Heres the reference (FK)
}
Configured the project like this (Startup.cs)
services.AddDbContext<CoreDbContext>(
options =>
{
options.UseNpgsql(npsqlConnectionString, b =>
{
b.MigrationsAssembly("Core");
});
}
);
services.AddDbContext<MeasurementsDbContext>(
options =>
{
options.UseNpgsql(npsqlConnectionString, b =>
{
b.MigrationsAssembly("Measurements");
});
}
);
The problem:
EF wants to create a table for the WeatherForecast class in the Measurement module, so it will be duplicated because the Core (base project) also creates it.
My questions are:
Can I solve this to generate the proper migration file? Or maybe I have to "hack" the migration file or this a bad practice?
DbContext should have there separated databases? Is the way I want to achieve this against the EF Core conventions / rules?
What do you suggest to make this kind of modular project? (I would prefer not to use micro services)

How to use diffrent DbContext per user in mvc core 2.2

i have some company and many user for that.
now i need to have one database per company.
have to solve this problem in MVC Core. and control DI
You need to create several DataBaseContext classes like this
using Microsoft.EntityFrameworkCore;
namespace Data.Models
{
public class FirstDataContext: DbContext
{
public DbSet<User> Users{ get; set; }
public FirstDataContext(DbContextOptions<FirstDataContext> options)
: base(options)
{
}
}
public class SecondDataContext : DbContext
{
public DbSet<User> Users{ get; set; }
public SecondDataContext(DbContextOptions<SecondDataContext> options)
: base(options)
{
}
}
}
then on appsettings.json add several connection strings that contains address of database
{
"ConnectionStrings": {
"FirstCompany": "Server=(localdb)\\mssqllocaldb;Database=company1;Trusted_Connection=True;"
},
"SecondCompany": "Server=(localdb)\\mssqllocaldb;Database=company2;Trusted_Connection=True;"
},
}
then go to StartUp.cs
public void ConfigureServices(IServiceCollection services)
{
string FirstCompany= Configuration.GetConnectionString("FirstCompany");
string SecondCompany= Configuration.GetConnectionString("SecondCompany");
services.AddDbContext<FirstDataContext>(options =>
options.UseSqlServer(connection));
services.AddDbContext<SecondDataContext>(options =>
options.UseSqlServer(connection));
services.AddMvc();
}
DI:
public class HomeController: Controller
{
FirstDataContext fdcntxt;
SecondDataContext sdcntxt;
public HomeController (FirstDataContext fdcntxt, SecondDataContext sdcntxt)
{
this.fdcntxt = fdcntxt;
this.sdcntxt = sdcntxt;
}
}
I think this should be works).

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

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; }
}

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 });