Issues setting up ASP.Net Role Provider + EF 7 - c#

Long story, originally a project was started using Azure AD auth as the authentication for the application, approaching the end of the project we noticed the huge flaws Azure auth had (Normal Users where not able to authenticate)
So the decision was made to move from Azure auth to ASP Identity Auth. To do this a new web project was created and all of the data from the first project was placed into it by coping the files from one project to another.
This caused a few headaches but eventually got everything going. With the new project it came with a Migration and ApplicationDBContext already to handle all of the ASP tables. This was the first issue as we already had a very detailed DbContext but using command line we managed to run all the migrations correctly.
Now we are presented with a new issue, every time we launch the application in VS2015 the error:
The Error:
InvalidOperationException: Unable to resolve service for type 'Musted.Models.ApplicationDbContext' while attempting to activate 'Microsoft.AspNet.Identity.EntityFramework.UserStore`3[Musted.Models.ApplicationUser,Microsoft.AspNet.Identity.EntityFramework.IdentityRole,Musted.Models.ApplicationDbContext]'.
I believe the issue is somewhere in the Startup.cs with how the identity is created.
Existing Context: ProjectContext.cs
New ASP Auth Context: ApplicationDBContext.cs
Startup.cs <-> Where I believe the issue to be
public void ConfigureServices(IServiceCollection services)
{
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ProjectContext>();
services.AddScoped<ProjectContextSeedData>();
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddMvc();
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, MusteredContextSeedData seeder)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
try
{
using (var serviceScope = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>()
.CreateScope())
{
serviceScope.ServiceProvider.GetService<ApplicationDbContext>()
.Database.Migrate();
}
}
catch { }
}
app.UseIISPlatformHandler(options => options.AuthenticationDescriptions.Clear());
app.UseStaticFiles();
app.UseIdentity();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
ApplicationDBContext.cs
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connString = Startup.Configuration["Data:ProjectContextConnection"];
optionsBuilder.UseSqlServer(connString);
base.OnConfiguring(optionsBuilder);
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
}
}
ProjectContext.cs
public class ProjectContext: DbContext
{
public ProjectContext()
{
Database.EnsureCreated();
}
public DbSet<User> Users {get; set;}
public DbSet<UserLevel> UserLevels { get; set; }
public DbSet<ApprovalStatus> ApprovalStatus { get; set; }
public DbSet<Area> Areas { get; set; }
public DbSet<Company> Company { get; set; }
public DbSet<Event> Events { get; set; }
public DbSet<LeaveType> LeaveTypes { get; set; }
public DbSet<Leave> Leave { get; set; }
public DbSet<Shift> Shifts { get; set; }
public DbSet<UserLoginAudit> UserLoginAudit { get; set; }
public DbSet<UserType> UserType { get; set; }
public DbSet<ShiftDay> ShiftDay { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connString = Startup.Configuration["Data:ProjectContextConnection"];
optionsBuilder.UseSqlServer(connString);
base.OnConfiguring(optionsBuilder);
}
}
UPDATE
So after some more digging, I have found the line in the ASP generated code that is failing.
private readonly UserManager<ApplicationUser> _userManager;
//This is the line that is erroring
var result = await _userManager.CreateAsync(user, model.Password);
That calls is all throughout the ASP stuff so I am guessing nothing will work

In the end - I deleted the project and imported the classes from the start. I wasted hours trying to get it working the other way and the new project was just much quicker.

Related

Database Schema not changing at Runtime in Asp.net Core 2.2 & Entity Framework Core

i had an application where data is saved in different sql schema for different Users.
For e.g.
User 1 Data is saved in SCHEMA1
User 2 Data is saved in SCHEMA2
Previously application was developed in MVC 3 and it is working fine and as expected.
Now we are migrating application in .Net Core 2.2 in which this fucntionality is not working
.net core does not have IDbModelCacheKeyProvider due to this only one schema is working
Below is the DBContext File
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
//public string Schema { get; set; }
private readonly IConfiguration configuration;
public string SchemaName { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public ApplicationDbContext(string schemaname)
: base()
{
SchemaName = schemaname;
}
public DbSet<EmployeeDetail> EmployeeDetail { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var builder = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json");
var configuration = builder.Build();
optionsBuilder.UseSqlServer(configuration["ConnectionStrings:SchemaDBConnection"]);
var serviceProvider = new ServiceCollection().AddEntityFrameworkSqlServer()
.AddTransient<IModelCustomizer, SchemaContextCustomize>()
.BuildServiceProvider();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.RemovePluralizingTableNameConvention();
modelBuilder.HasDefaultSchema(SchemaName);
base.OnModelCreating(modelBuilder);
}
public string CacheKey
{
get { return SchemaName; }
}
}
public class SchemaContextCustomize : ModelCustomizer
{
public SchemaContextCustomize(ModelCustomizerDependencies dependencies)
: base(dependencies)
{
}
public override void Customize(ModelBuilder modelBuilder, DbContext dbContext)
{
base.Customize(modelBuilder, dbContext);
string schemaName = (dbContext as ApplicationDbContext).SchemaName;
(dbContext as ApplicationDbContext).SchemaName = schemaName;
}
}
My question is how to change schemaName at runtime
So what is the right way to organize that mechanism:
Figure out the schema name by the user credentials;
Get user-specific data from database from specific schema.
I was able to change schema at runtime by changing onConfiguring Method
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public string SchemaName { get; set; }
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public ApplicationDbContext(string schemaname)
: base()
{
SchemaName = schemaname;
}
public DbSet<EmployeeDetail> EmployeeDetail { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var builder = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json");
var configuration = builder.Build();
var serviceProvider = new ServiceCollection().AddEntityFrameworkSqlServer()
.AddSingleton<IModelCustomizer, SchemaContextCustomize>()
.BuildServiceProvider();
optionsBuilder.UseSqlServer(configuration["ConnectionStrings:SchemaDBConnection"]).UseInternalServiceProvider(serviceProvider);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// modelBuilder.MapProduct(SchemaName);
modelBuilder.RemovePluralizingTableNameConvention();
if (!string.IsNullOrEmpty(SchemaName))
{
modelBuilder.HasDefaultSchema(SchemaName);
}
base.OnModelCreating(modelBuilder);
}
public string CacheKey
{
get { return SchemaName; }
}
public class SchemaContextCustomize : ModelCustomizer
{
public SchemaContextCustomize(ModelCustomizerDependencies dependencies)
: base(dependencies)
{
}
public override void Customize(ModelBuilder modelBuilder, DbContext dbContext)
{
base.Customize(modelBuilder, dbContext);
string schemaName = (dbContext as ApplicationDbContext).SchemaName;
(dbContext as ApplicationDbContext).SchemaName = schemaName;
}
}
}
The best way is to use Multi-Tenancy Architecture to be able to use database schema per user ( Tenant )
This architecture is recommended for Saas appllications
Concepts
Let’s agree on some basic concepts:
We use a tenant identification (or resolution) strategy to find out what tenant are we talking to
A tenant DbContext access strategy will figure out the way to retrieve (and store)
This article will show you how to implement Multi-tenancy app using .net core : https://stackify.com/writing-multitenant-asp-net-core-applications/

ASP.net Core OData String Key Is Null

I have a strange one here. I've been porting some controllers over from asp.net odata to asp.net core odata and ran into a bit of a snag with the primary keys.
In my .net framework 4.6.2 app I have a GUID as a primary key and in the .net core app I have a string as a primary key. I've been able to get most everything working EXCEPT a Get(key) method. This is my method signature:
[HttpGet]
[EnableQuery]
public async Task<IActionResult> Get([FromODataUri] string key)
{
// key is null!
}
Please Below Follow step
Install Microsoft.AspNetCore.OData from NuGet Package
StartUp.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddOData(); //This Is added for OData
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc(routeBuilder =>
{
routeBuilder.EnableDependencyInjection(); //This Is added for OData
routeBuilder.Expand().Select().Count().OrderBy().Filter(); //This Is added for OData
});
}
In Api Conteroller
[HttpGet]
[EnableQuery] //This Is added for OData
public ActionResult<List<TestModel>> Get()
{
var model = new List<TestModel>();
for (int i = 1; i <= 10; i++)
{
var res = new TestModel()
{
ID = i,
Name="Test"+i,
Mobile="Test"+i,
City="Test_"+i
};
model.Add(res);
}
return model;
}
public class TestModel
{
public int ID { get; set; }
public string Name { get; set; }
public string Mobile { get; set; }
public string City { get; set; }
}
After Run Api And Check Like This

There is no argument given that corresponds to the required formal parameter 'options'

I'm working on my first application in .Net Core.
I'm getting this build error for some reason:
Error CS7036 There is no argument given that corresponds to the required formal parameter 'options' of 'LakeViewContext.LakeViewContext(DbContextOptions<LakeViewContext>)' LakeView
I wasn't able to find a solution through Google Search or MS documentation.
My Context class:
using LakeView.Models;
using Microsoft.EntityFrameworkCore;
namespace LakeView
{
public class LakeViewContext : DbContext
{
public LakeViewContext(DbContextOptions<LakeViewContext> options) : base(options)
{
}
public DbSet<HTMLElement> HTMLElements { get; set; }
public DbSet<CustomizedElement> CustomizedElements { get; set; }
public DbSet<TemplateFileType> TemplateFileTypes { get; set; }
public DbSet<StyleFile> StyleFiles { get; set; }
public DbSet<Template> Templates { get; set; }
public DbSet<Course> Courses { get; set; }
public DbSet<Page> Pages { get; set; }
public DbSet<HTML> HTMLs { get; set; }
public DbSet<Comment> Comments { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<CustomizedElementTemplate>()
.HasKey(s => new { s.CustomizedElementId, s.TemplateId });
base.OnModelCreating(modelBuilder);
}
}
}
Controller class:
using LakeView.Models;
using LakeView.Models.ViewModels;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Linq;
namespace LakeView.Controllers
{
public class CoursesController : Controller
{
private LakeViewContext db = new LakeViewContext();
public IActionResult Index()
{
ICollection<Course> courses = db.Courses.ToList();
return View(courses);
}
[HttpGet]
public IActionResult CreateCourse()
{
return View("CreateCourse");
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult CreateCourse(CreateCourseViewModel courseVM)
{
if (ModelState.IsValid)
{
Course newCourse = new Course()
{
CourseCode = courseVM.CourseCode,
CourseTitle = courseVM.CourseTitle,
MasterOU = int.Parse(courseVM.MasterOU)
};
db.Courses.Add(newCourse);
db.SaveChanges();
return RedirectToAction("Index");
}
return View("CreateCourse", courseVM);
}
}
}
(bold text is underlined in Visual Studio with the same error
"private LakeViewContext db = new LakeViewContext();"
Startup class:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using LakeView.Models;
namespace LakeView
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
var connection = #"Data Source = (localdb)\MSSQLLocalDB; Database = LakeViewData; Trusted_Connection = True;";
services.AddDbContext<LakeViewContext>(options => options.UseSqlServer(connection));
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
//loggerFactory.AddConsole();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
}
}
LakeViewContext expects a DbContextOptions<LakeViewContext> to be passed into its constructor. However, you are calling the constructor without providing anything:
private LakeViewContext db = new LakeViewContext();
To fix the issue, you can just plug into the Dependency Injection system that you've set up. To do this, change your controller as follows:
public class CoursesController : Controller
{
private readonly LakeViewContext db;
public CoursesController(LakeVieContext db)
{
this.db = db;
}
...
The ASP.NET Core Dependency Injection system will provide you with a LakeViewContext in the constructor - Just use that.
you are trying to new up the dbcontext in your controller without passing in the options.
You should instead add a constructor to your controller and add the dbContext to your constructor so it will get injected, ie
public CoursesController(LakeViewContext dbContext)
{
db = dbContext;
}
private LakeViewContext db;
... the rest of your code
dependency injection will pass it in to you
When observing at how the classes are generated via the database first approach, I was able to fix this error with an empty constructor.
public class MyDBContext : DbContext
{
public MyDBContext()
{
}
//Rest of your code
}

sqlite on .net core publish to azure fails

I'm trying to publish a code-first webapp that uses sqlite to Azure. Now, the application is working fine locally. but when I publish, it seems that the database file is not being created properly, and I get the following error:
SqliteException: SQLite Error 1: 'no such table: Blogs'.
Microsoft.Data.Sqlite.Interop.MarshalEx.ThrowExceptionForRC(int rc, Sqlite3Handle db)
I am not sure as to why the Blogs table isn't created (and I assume the rest aren't created either).
my ApplicationDbContext.cs looks like this:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public ApplicationDbContext()
{
Database.EnsureCreated();
Database.Migrate();
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
}
public DbSet<Publication> Publications { get; set; }
public DbSet<Blog> Blogs { get; set; }
public DbSet<Student> Students { get; set; }
public DbSet<ApplicationUser> ApplicationUser { get; set; }
}
Configuration:
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(Configuration.GetConnectionString("DefaultConnection"))
);
}
and DefaultConnection in appsettings.json:
"ConnectionStrings": {
"DefaultConnection": "Filename=.\\mydb.sqlite"
},
Furthermore, when I download mydb.sqlite and open it, its completely empty.
#Tseng your instinct about EnsureCreated was right. I ended up solving this by clearing the constructor and adding this to the end of the Configure method in Startup.cs
var serviceScopeFactory = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>()
using (var serviceScope = serviceScopeFactory.CreateScope())
{
var dbContext = serviceScope.ServiceProvider.GetService<ApplicationDbContext>();
dbContext.Database.EnsureCreated();
}
I found the answer here

Stack Overflow exception at Database.EnsureCreated EF 7 ASP.NET 5 MVC 6

I am encountering an error at Database creation at Application Start whereas the exact same code works perfectly fine in all other projects.
Startup function in Startup.cs
public Startup(IHostingEnvironment env)
{
// Set up configuration sources.
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables();
if (env.IsDevelopment())
{
// This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
builder.AddApplicationInsightsSettings(developerMode: true);
}
Configuration = builder.Build();
Globals.Configuration = Configuration;
Globals.HostingEnvironment = env;
Globals.EnsureDatabaseCreated();
}
Globals.EnsureDatabaseCreated()
public static void EnsureDatabaseCreated()
{
var optionsBuilder = new DbContextOptionsBuilder();
if (HostingEnvironment.IsDevelopment()) optionsBuilder.UseSqlServer(Configuration["Data:dev:DataContext"]);
else if (HostingEnvironment.IsStaging()) optionsBuilder.UseSqlServer(Configuration["Data:staging:DataContext"]);
else if (HostingEnvironment.IsProduction()) optionsBuilder.UseSqlServer(Configuration["Data:live:DataContext"]);
var context = new ApplicationContext(optionsBuilder.Options);
context.Database.EnsureCreated();
optionsBuilder = new DbContextOptionsBuilder();
if (HostingEnvironment.IsDevelopment()) optionsBuilder.UseSqlServer(Configuration["Data:dev:TransientContext"]);
else if (HostingEnvironment.IsStaging()) optionsBuilder.UseSqlServer(Configuration["Data:staging:TransientContext"]);
else if (HostingEnvironment.IsProduction()) optionsBuilder.UseSqlServer(Configuration["Data:live:TransientContext"]);
new TransientContext(optionsBuilder.Options).Database.EnsureCreated();
}
ApplicationContext.cs
public class ApplicationContext : DbContext
{
public DbSet<Models.Security.User> Logins { get; set; }
public DbSet<Models.Security.Session> Sessions { get; set; }
public DbSet<Models.Security.Verification> VerificationTokens { get; set; }
public DbSet<Models.CRM.User> Users { get; set; }
public DbSet<Models.CRM.Organization> Merchants { get; set; }
public DbSet<Models.CRM.LinkedAddress> Shops { get; set; }
public DbSet<Models.CRM.ContactDetail> ContactDetails { get; set; }
public DbSet<Models.CRM.Location> Locations { get; set; }
public ApplicationContext(DbContextOptions options) : base(options)
{
}
}
Error Screenshot
After waiting for two days for an answer, unfortunately i ended up creating a new project and copying code there and it worked. Seems like a configuration issue.
Note: Since i didn't received any answers i am marking this as the correct answer. If a user comes in future and share their viewpoints, i will be happy to mark their answer if it adds some value to future readers.

Categories