MVC5 and Entity Migrations with Asp.Net Identity - asp.net

I have an issue that I have been struggling with. I would appreciate any help
So, I have an application that uses Code first. For authentication, I use Asp.Net Identity 2.0.1. So I have a User class that derives from Identity User like this:
public class User: IdentityUser
{
public virtual UserInfo UserInfo { get; set; }
public virtual Organization Organization { get; set; }
}
Then i define my Context as:
public class IdentityContext: IdentityDbContext<User>
{
public IdentityContext(): base("Name=IdentityContext")
{
}
static IdentityContext()
{
Database.SetInitializer<IdentityContext>(new CreateDatabaseIfNotExists<IdentityContext>());
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new UserMap());
modelBuilder.Configurations.Add(new UserInfoMap());
}
}
Then i enable migrations using this context. And since Organization class is linked to User it also is added to the migration. But I dont want that, since its different from the Identity classes. My Organization class is as follows:
public partial class Organization: EntityBase
{
public Organization()
{
this.Users = new List<User>();
}
public int OrganizationId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public virtual ICollection<User> Users { get; set; }
}
This derives from EntityBase and not from IdentityUser class and is handled differently.
What I would be able to do is to handle all my non-identity classes through a different context, lets say AppContext that could be defined as:
public class AppContext: DbContextBase
{
public AppContext(): base("Name=IdentityContext")
{
}
static AppContext()
{
Database.SetInitializer<AppContext>(new CreateDatabaseIfNotExists<AppContext>());
}
public new IDbSet<T> Set<T>() where T : class
{
return base.Set<T>();
}
protected override void OnModelCreating(DbModelBuilder builder)
{
builder.Configurations.Add(new OrganizationMap());
}
}
And then enable a second migration for this context.
But when I enable a second migration it fails with the exception that "Migrations have already been enabled in project 'Datastore'. To overwrite the existing migrations configuration, use the -Force parameter.". Although it still creates the Configuration file for the second migration.
So when i try to add a migration for this configuration it complains the following:
One or more validation errors were detected during model generation:
Datastore.IdentityUserLogin: : EntityType 'IdentityUserLogin' has no
key defined. Define the key for this EntityType.
Datastore.IdentityUserRole: : EntityType 'IdentityUserRole' has no key
defined. Define the key for this EntityType.
Datastore.UserInfo: : EntityType 'UserInfo' 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.
UserInfoes: EntityType: EntitySet 'UserInfoes' is based on type 'UserInfo' that has no >keys defined.
I am not sure how to get around this? Any help is really appreciated

The idea was to inherit the AppContext with IdentityDbContext. IdentityDbContext itself inherits from DbContext so it has all the functionality available for DbContext plus extra configuration requirements for the Asp.Net Identity framework

Related

How to specify default property values for owned entity types in Entity Framework Core 2.0?

I have a simple POCO type, say something like
public class OwnedEntity {
public string stringProperty { get; set; }
public decimal decimalProperty { get; set; }
public bool boolProperty { get; set; }
public int intProperty { get; set; }
}
and an actual entity with an OwnedEntity reference
public class SomeEntity {
public string Id { get; set; }
public OwnedEntity OwnedEntity { get; set; }
}
I set up the relationship like described in the documentation using EF Core's Fluent API:
protected override void OnModelCreating (ModelBuilder builder) {
base.OnModelCreating (builder);
builder.Entity<SomeEntity> ().OwnsOne (e => e.OwnedEntity);
}
I can't find anything on how to define default-values for all the properties of OwnedEntity. I tried to initialize the properties like this:
public class OwnedEntity {
public string stringProperty { get; set; } = "initial"
public decimal decimalProperty { get; set; } = -1M;
public bool boolProperty { get; set; } = false;
public int intProperty { get; set; } = -1;
}
but with no effect. Same goes with the [DefaultValueAttribute] (but that was to expect since it's explicitly mentioned).
There's a bit of information on how to handle initial values for regular entities:
modelBuilder.Entity<SomeOtherEntity>()
.Property(e => e.SomeIntProperty)
.HasDefaultValue(3);
But since I'm facing an Owned Entity Type, I can't access the type via Entity<T>.
Is there a way of doing what I'm looking for?
Some things worth mentioning:
I have a solid amount of specific entities where most of them are using the OwnsOne relation
Declaring all OwnedEntity-properties in a base class is not an option since not all the entities have those properties
I`m using EF Core 2.0.3 and ASP.NET Core MVC 2.0.4
Edit:
Originally, I wanted to have newly created SomeEntity instances to come with preset properties for all of the 'embedded' SomeEntity.OwnedEntity properties.
But looking at how my associated controller works, it all makes sense... I have the following methods for the 'Create' operation:
[HttpGet]
public IActionResult Create () {
return View (nameof (Create));
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create (SomeEntity model) {
context.Add (model);
await context.SaveChangesAsync ();
// redirect etc.
}
Which means that no object is created for the [HttGet] overload of Create and all the HTML inputs linked to properties (via asp-for) are initially empty. Okay. So I guess the proper way of doing this is to manually create a new instance of SomeEntity and pass it to the Create view like this:
[HttpGet]
public IActionResult Create () {
return View (nameof (Create), new SomeEntity());
}
Is this the right approach then or are there some more things to keep in mind?
Assuming you understand what EF Core Default Values are for, and just looking for equivalent of Entity<T>().Property(...) equivalent.
The owned entities are always configured for each owner type by using the ReferenceOwnershipBuilder<TEntity,TRelatedEntity> class methods. To access this class you either use the result of OwnsOne method, or use the OwnsOne overload taking second argument of type Action<ReferenceOwnershipBuilder<TEntity,TRelatedEntity>>.
For instance, using the second approach:
builder.Entity<SomeEntity>().OwnsOne(e => e.OwnedEntity, ob =>
{
ob.Property(e => e.stringProperty)
.HasDefaultValue("initial");
ob.Property(e => e.decimalProperty)
.HasDefaultValue(-1M);
// etc.
});

Implementing ASP.NET Core Identity in a n-tier application using EF7 and SQLite

I'm creating an n-tier application in .NET core in which I try to keep the Identity framework out of my presentation layer as much as possible. Since I'm going to use a linux server to run my application, I'll be using Sqlite databases to persist my domain and all identity related classes.
All documentation I've encountered (official ms documentation and various blogs, tutorials ...) doesn't use SQLite.
In my datalayer I have a DbContext class which inherits from the ASP.NET's core IdentityDbContext.
public class SquiriusDbContext : IdentityDbContext<User>
{
public DbSet<Blog> Blogs { get; set; }
public DbSet<Post> Posts { get; set; }
public DbSet<Article> Articles { get; set; }
public DbSet<HtmlSection> HtmlSections { get; set; }
public DbSet<MediaSection> MediaSections { get; set; }
public DbSet<Comment> Comments { get; set; }
public DbSet<Category> Categories { get; set; }
public DbSet<Tag> Tags { get; set; }
public SquiriusDbContext() { }
public SquiriusDbContext(DbContextOptions options) : base(options)
{
Database.Migrate();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = "squiriusdb.db" };
var connectionString = connectionStringBuilder.ToString();
var connection = new SqliteConnection(connectionString);
optionsBuilder.UseSqlite(connection);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
}
When I try to perform a migration, I'm getting the following error:
PM> add-migration identity
An error occurred while calling method 'ConfigureServices' on startup class 'Startup'. Consider using IDbContextFactory to override the initialization of the DbContext at design-time. Error: GenericArguments[0], 'Squirius.Domain.Authentication.User', on 'Microsoft.AspNetCore.Identity.EntityFrameworkCore.UserStore`4[TUser,TRole,TContext,TKey]' violates the constraint of type 'TUser'.
System.InvalidOperationException: The entity type 'User' requires a primary key to be defined.
The error says that the entity type 'User' requires a primary key to be defined, which is strange since this User class inherits directly from Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUser.
I think this might have something to do with the limitations of the EF7 Sqlite provider, and using an ms sql server database would solve this error.
Does anyone know a way around this so I can use an Sqlite db in order to be able to build for a linux platform?
Thanks!

Enable migrations with reference to ApplicationUser identity framework code first

I'll create a database with identity framework (code first way) but I've errors when I run code below in the package manager.
PM> Enable-Migrations -ContextTypeName FreeTime.AspMvc.Context.FreeTimeContext -Force
Here are my errors:
IdentityUserLogin: : EntityType IdentityUserLogin has no key defined. Define the key for this EntityType.
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.
More information:
(Click to see full screen)
In other classes I've made a property type of ApplicationUser named UserId and one of type of string named User for holding witch user has made witch topic. Here you've an example:
public class Topic
{
[Key]
public int TopicId { get; set; }
[ForeignKey("dbo.AspNetUsers.Id")]
public string UserId { get; set; }
public ApplicationUser User { get; set; }
}
Small update: Here is my context file:
public class FreeTimeContext: DbContext
{
public DbSet<Topic> Topics { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
}
}
I've also seen that there is a second context file named ApplicationDbContext.
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("FreeTimeContext" /* --> this I've edited into my own name */,
throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
}
Big update: In a later state I've tried other things like list below:
First I've tried to enabled the migrations for ApplicationDbContext. Here are the steps I've done.
First I've run this code:
PM> enable-migrations -ContextTypeName FreeTime.AspMvc.Models.ApplicationDbContext -ForceThis is done without errors.
Then I've run this:
PM> add-migration "initAspTables"
Also no problems with this code.
Finaly I've update my database:
PM> Update-Database
Also done with no problems.
Check if the tables are created into the server manager → Check but doesn't contain my own tables. ☹
Conclusion: not really what I will have.
Second thing I've done is inherit FreeTimeContext from ApplicationContext instead of DbContext like code below.public class FreeTimeContext: ApplicationDbContext
{
public DbSet Topics { get; set; }
public FreeTimeContext(): base()
{ }
// other code
}And again to try enable the migrations with this code:
PM> enable-migrations -ContextTypeName FreeTime.AspMvc.Context.FreeTimeContext -Force
but I've this error:
The ForeignKeyAttribute on property UserId on type FreeTime.AspMvc.Models.Topic is not valid. The navigation property dbo.AspNetUsers.Id was not found on the dependent type FreeTime.AspMvc.Models.Topic. The Name value should be a valid navigation property name.More information:(Click to see full screen)Conclusion: didn't work.
Can you help me? I don't know what else I could do to migrate the class ApplicationUser together with my own classes to the database.

Extending Identity3 in MVC6

using the latest (current) RC1 of asp.net5 I'm looking at creating a simple relationship between a User entity and a WorkLog entity.
Is it possible to use the ApplicationUser Class from Identity as a starting point and use the ApplicationUser key which is defined as the linking key? I have had problems extending the ApplicationUser in the past and therefore generated a seperate dbcontext (pointing to the same database) and created my own plumbing in order to pass the IdentityUsers Id into my seperate dbcontext. Does anyone have any examples of extending the IdentityDbContext adding foreign key tables mapping to the IdentityUser Class?
Example below
//DBContext
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<WorkLogItem> WorkLogItems { 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);
builder.Entity<WorkLogItem>(
e =>
{
e.Property(p => p.id).IsRequired().UseSqlServerIdentityColumn();
});
}
}
//WorkLogItem
public class WorkLogItem
{
public int id { get; set;}
public String UserId { get; set; }
public int Hours { get; set; }
public String Description { get; set; }
}
//ApplicationUser
public class ApplicationUser : IdentityUser
{
public ICollection<WorkLogItem> WorkLogItems { get; set; }
}
Doing what you've asked is expected to work out of the box. You can look at this commit to see the difference between a newly created MVC 6 project with Identity and your schema above.
Registering a user, and refreshing /Home/Index causes WorkLogItems to be added as expected. Note you don't need a separate DB context for this.
public IActionResult Index()
{
var user = _db.Users.Include(p => p.WorkLogItems).FirstOrDefault();
if (user != null)
{
user.WorkLogItems.Add(new WorkLogItem { Description = "New item added" });
_db.SaveChanges();
ViewBag.WorkItems = user.WorkLogItems.ToList();
}
else ViewBag.WorkItems = new WorkLogItem[] { };
return View();
}
The key items to be aware of when you add any collection to an existing entity are;
Make sure you add the migration and update the databse
Make sure you use Include on the query because EF7 does not support Lazy Loading.

Code First Generic Repository with existing Database tables

I have a Generic Repository class using code first to perform data operations.
public class GenericRepository<T> where T : class
{
public DbContext _context = new DbContext("name=con");
private DbSet<T> _dbset;
public DbSet<T> Dbset
{
set { _dbset = value; }
get
{
_dbset = _context.Set<T>();
return _dbset;
}
}
public IQueryable<T> GetAll()
{
return Dbset;
}
}
I have an entity class Teacher, which maps to an existing table "Teacher" in my database, with exactly the same fields.
public class Teacher
{
public Teacher()
{
//
// TODO: Add constructor logic here
//
}
public int TeacherID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
I have the following code below which binds data from Teacher to a repeater control.
GenericRepository<Teacher> studentrepository = new GenericRepository<Teacher>();
rptSchoolData.DataSource = studentrepository.GetAll().ToList();
rptSchoolData.DataBind();
But I get an exception exception "The entity type Teacher is not part of the model in the current context". Do I have to do any additional work when using an existing database for code first?
You must create a context class that derives from DbContext. The class should have properties of type DbSet<T> which will give EF enough information to create and communicate with a database with default naming and association conventions. It will use properties like Student.Teacher (if any) to infer foreign key associations:
public class MyContext: DbContext
{
public DbSet<Teacher> Teachers { get; set; }
public DbSet<Student> Students { get; set; }
...
}
If the defaults are not what you want, or when you've got an existing database that you want to match with the names and associations in your model you can do two (or three) things:
Override OnModelCreating to configure the mappings manually. Like when the tables in the database have those ugly prefixes (to remind people that they see a table when they see a table):
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Teacher>()
.Map(e => e.ToTable("tblTeacher"));
...
}
(Less favorable) Use data annotations to do the same.
Turn it around and use Entity Framework Powertools to reverse-engineer a database into a class model including fluent mappings and a DbContext-derived context. Maybe easier to modify an existing model than to start from scratch.

Resources