So I've started learning how to use the Entity Framework in MVC using the project template and I have encountered a problem. In my HomeController I wrote the below method GenerateContractorCustomer which when called seems to overwrite the default ASPNetUser tables in the database with the new models I created.
public void GenerateContractorCustomer()
{
using (var context = new ContractorCustomerContext())
{
ContractorModel contractor = new ContractorModel { ContractorID =1, ContractorName ="John"};
context.Contractor.Add(contractor);
context.SaveChanges();
}
}
I created a DbContext:
public class ContractorCustomerContext : DbContext
{
public ContractorCustomerContext() : base("DefaultConnection") { }
public DbSet<ContractorModel> Contractor { get; set; }
public DbSet<CustomerModel> Customer { get; set; }
And in the IdentityModel there is the default:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
My question is what do I need to do to my code so that when I call GenerateContractorCustomer it does not delete the deafult ASPNetUser tables?
You are using an initializer that drops the existing database and creates a new database, if your model classes (entity classes) have been changed. So you don't have to worry about maintaining your database schema, when your model classes change. To change a DB initialization strategy, you could set the DB Initializer using Database class in Context class, as shown below:
public class ExampleDBContext: DbContext
{
public ExampleDBContext(): base("AConnectionString")
{
Database.SetInitializer<ExampleDBContext>(new CreateDatabaseIfNotExists<ExampleDBContext>());
}
}
if you provide a null value you turn off this feature. See the links provided if you want an alternative configuration approach.
Related
The scenario:
I have a couple websites that I'm rebuilding with Blazor, all do e-commerce. What I want to do is extract the accounting logic (i.e. Orders, OrderItems, Accounts, Transactions, etc) and data operations into an
"Accounting" DLL so I don't have to repeat the code.
I've got the above Entities defined in the DLL, then in the WebApp.Server's DbContext I have the appropriate DbSets.
In the "Accounting" DLL, I have an interface:
public interface IDbAccountringService
{
DbSet<Account> Accounts { get; set; }
//etc
}
which the DbContext in WebApp.Server implements:
public class Db : ApiAuthorizationDbContext<User>, IDbAccountringService
{
public Db(
DbContextOptions options,
IOptions<OperationalStoreOptions> operationalStoreOptions) : base(options, operationalStoreOptions)
{
}
public DbSet<Account> Accounts { get; set; }
}
Then in the "Accounting" DLL, I have the following generic class:
public class DbAccountingService<T> where T : DbContext, IDbAccountringService
{
DbContext dbContext { get; set; }
public DbAccountingService(DbContext T)
{
dbContext = T;
}
public Account[] GetAccounts()
{
//The compiler doesn't see Accounts
return dbContext.Accounts.ToArray();
//It also doesn't see Accounts on itself
return this.Accounts.ToArray();
// However, it does see all the DbContext methods
dbContext.SaveChanges();
}
}
which I instantiate and use in my controller:
[Route("accounting/accounts")]
[ApiController]
public class JournalController : BaseApiController
{
DbAccountingService<Db> _dbAccountingService;
public JournalController(Db db, MtGlobals mtGlobals) : base(mtGlobals)
{
_dbAccountingService = new DbAccountingService<Db>(db);
}
[HttpGet("get-accounts")]
public Account[] GetAccounts()
{
return _dbAccountingService.GetAccounts();
}
}
As the comments in DbAccountingService<T> indicate, the compiler recognizes that dbContext is in fact a DbContext, but it doesn't recognize that it also implements IDbAccountringService.
I'm a little fuzzy on generics, though I usually get them working, however, here, no luck.
Is what I'm trying to do possible? I want to extract all the data operations into the "Accounting" DLL so that I don't have to write duplicate code for each website.
Your dbContext field is of type DbContext:
DbContext dbContext { get; set; }
public DbAccountingService(DbContext T)
{
dbContext = T;
}
Be aware, that you constructor parameter is of type DbContext too with parameter name T. So this T has nothing to do with the generic type parameter, it's just a parameter name.
You want the dbContext property to be the generic type:
T dbContext { get; set; }
public DbAccountingService(T context)
{
dbContext = context;
}
The relevant par is, that your field has type T (because your where constraints this to implement interface IAccountingService.
I am assuming there is something I am missing but when I generate migrations in a project, they keep showing empty Up and Down methods. Even in a brand new project. Here are my steps.
Launch Visual Studio 2019. Create a new ASP.NET Web Application (.net framework) C# project (4.7.2). Choose MVC and under authentication select Individual User Accounts. Click create to create the project.
Next I Enable Migrations.
Enable-Migrations
Next I add my first migration.
Add-Migration First
A first migration is successfully added with all the identity information for individual user accounts. All is good.
I update the database to apply the migration. All is still good.
Update-Database
Now, I add a new class to the Models folder called SchoolContext.
using System;
using System.Data.Entity;
using System.ComponentModel.DataAnnotations;
namespace WebApplication6.Models
{
public class SchoolContext : DbContext
{
public DbSet<Student> Students { get; set; }
public SchoolContext() : base("DefaultConnection") { }
}
public class Student
{
[Key]
public int StudentID { get; set; }
public string LastName { get; set; }
public string FirstMidName { get; set; }
public DateTime EnrollmentDate { get; set; }
}
}
I now go back to the Package Manager Console to create another migration. I attempt to create a new migration.
Add-Migration Second
But this time, the class is empty. it does not create my new table.
namespace WebApplication6.Migrations
{
using System;
using System.Data.Entity.Migrations;
public partial class Second : DbMigration
{
public override void Up()
{
}
public override void Down()
{
}
}
}
What am I missing? Why doesn't it want to generate a migration with my new table?
As requested, this is what my ApplicationDbContext looks like in my IdentityModel.cs class generated by Visual Studio.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
If your first migration created users tables, then I assume that it used IdentityDbContext. Then, you need to inherit your SchoolContext not from DbContext, but from IdentityDbContext.
UPDATE: based on the latest update of the question, it is clear that application already has one database context, which is ApplicationDbContext. So usually it is enough to keep all DbSet<> in one database context. No need to create new context.
You are missing the dbset property.
Put it as following example so that migrations can detect the new class/table.
After adding the dbset property, add new migration to see the new class written.
If you already have a migration pending then write -force for the code to see new changes and update migration
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext() : base("DefaultConnection") {
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
public virtual DbSet<Student> Students { get; set; }
}
Note that there are 2 dbcontexts which is not of clear use, use only ApplicationDBContext and remove the other.
so I have 2 different DbContext (ef 6.1.3 code first)
FirstDbContext
SecondDbContext
each context contains a SbSet Users that maps the user table in the corresponding database
NOTE : the data is different, DbFirst User is not DbSecond User!!
I have an abstract repository:
public abstract class Repository<TContext> where TContext : DbContext
{
public Repository(TContext ctx)
{
}
}
and 2 repositories :
public FirstRepo : Repository<FirstDbContext>
{
public FirstRepo(FirstDbContext ctx):base(ctx)
{
}
}
public SecondRepo : Repository<SecondDbContext>
{
public SecondRepo(SecondDbContext ctx):base(ctx)
{
}
}
I Have 2 different MSSQL databases related to the contexes:
DbFirst
DbSecond
I'm using dependency injection to add scoped repository and contexes, 2 database, 2 different connection.
I expected that my .Net Core application would use 2 Models
but once i get data from both the context i get
NotSupportedException: The type 'First.User' and the type
'Second.User' both have the same simple name of
'User' and so cannot be used in the same model.
Why the same model?
I know that in the same model I should have different names because EF does not look for namespaces, but in that case I shouldn't have this kind of issue.
EDIT #1 :
If I use one of the repository alone everything works as expected so i'm sure that there isn't any mispelled namespace
If I use the repositories all together i got this error, for example
var top10 = FirstRepo.GetTop10().ToList();
var sam = SecondRepo.GetByName<Second.User>("sam");
EDIT 2 (#Steve Green):
//Note that I'm not trying to do this :
public class MyTextContext : DbContext
{
public DbSet<Test.Security.Question> Security_Question { get; set; }
public DbSet<Test.Forms.Question> Forms_Question { get; set; }
}
// What I need is something like this :
public class SecurityContext : DbContext
{
public DbSet<Test.Security.Question> Question { get; set; }
}
public class FormsContext : DbContext
{
public DbSet<Test.Forms.Question> Question { get; set; }
}
Important note
If I manually ignore the "other" entity in both of the context everything works
I Remark that the context are not only in different namespaces, but also different assemblies...
// this is working!! .___.
// but i don't want to add a reference to a project just to exclude a class... it's unacceptable
public class FirstDbContext : DbContext
{
public DbSet<First.User> Users {get;set;}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Ignore<Second.User>();
}
}
public class SecondDbContext : DbContext
{
public DbSet<Second.User> Users {get;set;}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Ignore<First.Usere>();
}
}
Any suggestion different from renaming the table will be appreciated
Thanks
I'm trying to do the following... When a user submits a new article, i want to display the authors (users) username in my web application. That's why i need to "connect" these two and i want to have only 1 DbContext.
Now i'm doing it this way:
public class ApplicationDbContext : IdentityDbContext
{
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
private DbSet<Article> Articles { get; set; } // This line is all i have added/changed. Everything else was auto-generated when i created a new ASP.net MVC project with individual authentication in visual studio.
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
But i'm not sure if this is the right way to do it. How should this be done properly?
UPDATE (explanation of my comment to DavidG's answer)
First i was retrieving a list of users like this:
class ApplicationUserService
{
public List<ApplicationUser> GetUsers()
{
using (var dbContext = new ApplicationDbContext())
{
return dbContext.ApplicationUsers.ToList();
}
}
}
instead of:
class ApplicationUserService
{
public List<IdentityUser> GetUsers()
{
using (var dbContext = new ApplicationDbContext())
{
return dbContext.Users.ToList();
}
}
}
The problem was that i couldn't get the UserName or any other property because of this mistake. I watched some tutorials online, no one ever mentioned that the DbSet is Users. Anyways... now i know why i don't need this property:
public DbSet<ApplicationUser> ApplicationUsers { get; set; }
This message from VS helped me (intended for other people which should stumble upon the same problem i had):
'ApplicationDbContext.Users' hides inherited member 'IdentityDbContext<IdentityUser, IdentityRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>.Users'.
This is fine if you want to use one context, and the Identity tables are in the same database as your "article" table. I normally like to override OnModelCreating so I can add mapping/configurations for tables I create.
Depending on the size of the application, I leave the Identity context alone, and I create one for my application (I may include the Users table to make it easier to retrieve users). A matter of choice.
Your application just needs to inherit from the correct version of the IdentityDbContext class. You need to use the generic one so you can customise the user class with your own IdentityUser. So you context should be like this. Note the new inheritance and removal of the ApplicationUsers property. This is removed as the Identity class already has this done for you.
public class ApplicationDbContext : IdentityDbContext<IdentityUser>
{
private DbSet<Article> Articles { get; set; }
public ApplicationDbContext()
: base("DefaultConnection")
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
Bellow my MVC5 App IdentityModeld.cs:
public class ApplicationUser : IdentityUser {
public int StaffId { get; set; }
}
//public class ApplicationDbContext :
//IdentityDbContext<User, UserClaim, UserSecret, UserLogin, Role, UserRole> {
//original: http://www.briankeating.net/?tag=/IdentityDbContext
public class ApplicationDbContext : IdentityDbContext<ApplicationUser> {
public ApplicationDbContext() : base("DefaultConnection") {
//Database.SetInitializer<ApplicationDbContext>(null);
//Configuration.AutoDetectChangesEnabled = false;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
//modelBuilder.Ignore<Staff>();
//base.OnModelCreating(modelBuilder);
}
// dublicate from another DbContext
//public DbSet<Staff> Person { get; set; }
}
Commented lines cause error:
The model backing the 'ApplicationDbContext' context has changed since
the database was created...
I don't need migrations. There are no changes in my database or models. Can I add to existing ApplicationDbContext existing table from database as is?
There is no __MigrationHistory table in my database.
You need to update the shema stored "inside EF" to the current one by adding an empty migration.
Execute the following in the Package Manager Console:
Add-Migration Initial -IgnoreChanges
This should create a new migration with the updated shema for EF, but an empty Up and Down method.