c# asp.net identity and custom roles - c#

I think I am just doing something silly here.
I have used code first entity framework with asp.net identity and I set up a custom user like this:
public class User : IdentityUser, IKey<string>
{
[MaxLength(100)] public string JobTitle { get; set; }
[MaxLength(100)] public string Image { get; set; }
[MaxLength(100)] public string FirstName { get; set; }
[MaxLength(100)] public string LastName { get; set; }
}
then I updated my DbContext to match:
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<User>(m => m.ToTable("Users"));
builder.Entity<IdentityRole>(m => m.ToTable("Roles"));
builder.Entity<IdentityRoleClaim<string>>(m => m.ToTable("RoleClaims"));
builder.Entity<IdentityUserClaim<string>>(m => m.ToTable("UserClaims"));
builder.Entity<IdentityUserLogin<string>>(m => m.ToTable("UserLogins"));
builder.Entity<IdentityUserRole<string>>(m => m.ToTable("UserRoles"));
builder.Entity<IdentityUserToken<string>>(m => m.ToTable("UserTokens"));
}
All works, and the tables were created successfully.
Now I want to do the same for roles, except this time I don't need extra columns (it's the interface that is important here) so I create a new Role class:
public class Role: IdentityRole, IKey<string>
{
}
I then changed my OnModelCreating method to this:
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<User>(m => m.ToTable("Users"));
builder.Entity<Role>(m => m.ToTable("Roles"));
builder.Entity<IdentityRoleClaim<string>>(m => m.ToTable("RoleClaims"));
builder.Entity<IdentityUserClaim<string>>(m => m.ToTable("UserClaims"));
builder.Entity<IdentityUserLogin<string>>(m => m.ToTable("UserLogins"));
builder.Entity<IdentityUserRole<string>>(m => m.ToTable("UserRoles"));
builder.Entity<IdentityUserToken<string>>(m => m.ToTable("UserTokens"));
}
The only line that changed was builder.Entity<Role>(m => m.ToTable("Roles"));.
When I run add-migration RoleChange I expected nothing to have changed since my last migration, but instead I get this error:
The entity type 'Role' cannot be mapped to a table because it is derived from 'IdentityRole'. Only base entity types can be mapped to a table.
Does anyone know why?
I don't understand why User works, but Role won't....
Here is the full context:
public class DatabaseContext : IdentityDbContext<User>
{
public DbSet<Claim> Claims { get; set; }
/// <summary>
/// For testing only
/// </summary>
public DatabaseContext()
{
}
// ReSharper disable once SuggestBaseTypeForParameter
public DatabaseContext(DbContextOptions<DatabaseContext> options)
: base(options)
{
}
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<User>(m => m.ToTable("Users"));
builder.Entity<Role>(m => m.ToTable("Roles"));
builder.Entity<IdentityRoleClaim<string>>(m => m.ToTable("RoleClaims"));
builder.Entity<IdentityUserClaim<string>>(m => m.ToTable("UserClaims"));
builder.Entity<IdentityUserLogin<string>>(m => m.ToTable("UserLogins"));
builder.Entity<IdentityUserRole<string>>(m => m.ToTable("UserRoles"));
builder.Entity<IdentityUserToken<string>>(m => m.ToTable("UserTokens"));
}
}

What you have done is correct, you just need to update this line
public class DatabaseContext : IdentityDbContext<User>
as below :
public class DatabaseContext : IdentityDbContext<User, Role, string>

Related

Does Entity Framework support generic relations?

Does Entity Framework support generic relations?
E.g.
public class Comment<T> : Entity
where T : Entity
{
public string Text { get; set; }
public long EntityId { get; set; }
public T Entity { get; set; }
}
public class DocumentComment : Comment<Document> {
}
public class DeliveryComment : Comment<Delivery> {
}
UPDATE: I'll expand on my process as I tried this.
I tried creating configurations to make it work as I figured I needed to specify the types of variables but got an error when trying to add the migration.
public class CommentConfiguration<TU,T> : IEntityTypeConfiguration<TU>
where TU : Comment<T>
where T : Entity
{
public Configure(EntityTypeBuilder<TU> builder)
{
builder.ToTable(nameof(Comment));
builder.Property(x => x.Id);
builder.HasKey(x => x.Id);
builder.HasOne(x => x.Entity)
.WithMany()
.HasForeignKey(x => x.EntityId)
.OnDelete(DeleteBehavior.ClientCascade)
}
}
public class DocumentCommentConfiguration : CommentConfiguration<DocumentComment,Document>
{
public Configure(EntityTypeBuilder<DocumentComment> builder)
{
}
}
public class DeliveryCommentConfiguration : CommentConfiguration<DeliveryComment,Delivery>
{
public Configure(EntityTypeBuilder<DeliveryComment> builder)
{
}
}
Actually using conventions based setup worked just fine for me, so (at least based on provided info) you don't need to provide IEntityTypeConfiguration's.
If you still want to then (apart from the compilation issues, cause currently provided code is not compliable) you need to fix the table name in CommentConfiguration<TU,T> (by default you can't map different entity types to the same table). For example:
public class CommentConfiguration<TU,T> : IEntityTypeConfiguration<TU>
where TU : Comment<T>
where T : Entity
{
public Configure(EntityTypeBuilder<TU> builder)
{
builder.ToTable(typeof(TU).Name);
// ... rest of the config
}
}
Full code sample used for testing.
Also can be useful - inheritance in EF Core.

EF and existing database and table, with a bunch of new tables

EF and existing database and table, with a bunch of new tables.
Context:
An old database has a Table User that the new application will use.
I used the entity DataModel Wizard to Map it this .
What I have now :
A bunch of new entities and their configuration:
public class Bundle
{
public Bundle() { Produits = new HashSet<Produit>(); }
public int BundleID { get; set; }
public string Designation { get; set; }
public ICollection<Produit> Produits { get; set; }
}
public class BundleConfiguration : EntityTypeConfiguration<Bundle>
{
public BundleConfiguration()
{
ToTable("PL_Bundle");
HasKey(e => e.BundleID);
Property(e => e.Designation).HasMaxLength(200);
}
}
A DbContext with an extension using reflection to map all available configuration.
public partial class FooBarDbContext : DbContext
{
public FooBarDbContext() : base("name=Model1")
=> Database.SetInitializer<FooBarDbContext>(null);
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.ApplyAllConfigurations();
base.OnModelCreating(modelBuilder);
}
public DbSet<User> User { get; set; }
public DbSet<Bundle> Bundles { get; set; }
// [...]
}
public static class ModelBuilderExtensions
{
public static void ApplyAllConfigurations(this DbModelBuilder modelBuilder)
{
IEnumerable<Type> typesToRegister
= AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(t => t.GetTypes())
.Where(t => t.IsClass)
.Where(type => !string.IsNullOrEmpty(type.Namespace) &&
type.GetTypeInfo().BaseType != null &&
type.GetTypeInfo().BaseType.GetTypeInfo().IsGenericType &&
type.GetTypeInfo().BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>))
.ToArray();
foreach (var type in typesToRegister)
{
dynamic configurationInstance = Activator.CreateInstance(type);
modelBuilder.Configurations.Add(configurationInstance);
}
}
}
In the calling application I am trying to test the Database and table to check everything works..
static void Main(string[] args)
{
var db = new FooBarCore.DbContext.FooBarDbContext();
var all = db.User.ToList(); // Work
var bundles1 = db.Bundles.ToList();
The previous line give me error either :
Nom d'objet 'dbo.PL_Bundle' non valide. => Invalid object name 'dbo.PL_Bundle'
What I tried:
Finding if ModelBuilderExtensions was an issue. But using classical modelBuilder.Configurations.Add(new BundleConfiguration());.
Or using fluent API directly in the OnModelCreating throw the same error.
Check the database => Table are not here.
Search for EF not create table, but CreateDatabaseIfNotExist, DropCreateDatabaseWhenModelChanges, DropCreateDatabaseAlways are scary, they all have this Drop Database in the name.
so, as stated in comment:
Nom d'objet 'dbo.PL_Bundle' non valide. => Invalid object name
'dbo.PL_Bundle'
this means, that EF thinks that there should be a table PL_Bundle, but there isn't. It isn't there, because you added new class into your model.
For EF to make proper change to the database, you should mechanism called Migrations
You have to enable migrations, generate migration and apply it to the database.
Good practice is to store the migrations also as SQL scripts (by using UpdateDatabase -script) and make system admin apply them manually to prevent any secret datalosses if you remove a column or a table for example.
You ship the scripts with new versions of software and it should crash until the migration is applied, stating in logs which one is needed.
Update your DbContext class to this:
public partial class FooBarDbContext : DbContext
{
public FooBarDbContext() : base("name=Model1")
=> Database.SetInitializer<FooBarDbContext>(null);
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//Configurations for Bundle class
modelBuilder.Entity<Bundle>()
.ToTable("PL_Bundle")
.HasKey(e => e.BundleID);
.Property(e => e.Designation).HasMaxLength(200);
modelBuilder.ApplyAllConfigurations();
base.OnModelCreating(modelBuilder);
}
public DbSet<User> User { get; set; }
public DbSet<Bundle> Bundles { get; set; }
// [...]
}

Store the Controllers name and Actions Name into my Custom Tables that I Add them into Identity in CodeFirst Asp.Net MVC Project

Seniors,
I'm using ASP.NET Identity on my ASP.Net web application and I Add my custom Tables(with their relations) to Identity on my CodeFirst ASP.Net MVC Project.when I Run Project for the first time,the databace is created automatically with the custom tables and relations between them in SqlServer.
Custom Tables :
MvcControllers
ActionsTbls
GroupsTbls
AspNetUser_Action
AspNetUser_Group
Action_Group
This is The Diagram of my Database Image :
Click Here
For Creating Custom tables,I Add Some Codes to IdentityModels.cs.
IdentityModels.cs :
namespace Admin_Base_SN.Models
{
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit https://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
// Add custom user claims here
return userIdentity;
}
public virtual AspNetUser_Action AspNetUser_Action { get; set; }
public virtual AspNetUser_Group AspNetUser_Group { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<MvcController> MvcController { get; set; }
public DbSet<ActionsTbls> ActionsTbls { get; set; }
public DbSet<AspNetUser_Action> AspNetUser_Actions { get; set; }
public DbSet<GroupsTbl> GroupsTbls { get; set; }
public DbSet<Action_Group> Action_Groups { get; set; }
public DbSet<AspNetUser_Group> AspNetUser_Groups { get; set; }
public ApplicationDbContext()
: base("DefaultConnection", throwIfV1Schema: false)
{
}
public static ApplicationDbContext Create()
{
return new ApplicationDbContext();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
// one-to-zero or one relationship between ApplicationUser and Customer
// UserId column in Customers table will be foreign key
modelBuilder.Entity<ApplicationUser>()
.HasOptional(m => m.AspNetUser_Action)
.WithRequired(m => m.ApplicationUser)
.Map(p => p.MapKey("AspNetUser_Id"));
modelBuilder.Entity<ApplicationUser>()
.HasOptional(m => m.AspNetUser_Group)
.WithRequired(m => m.ApplicationUser)
.Map(p => p.MapKey("AspNetUser_Id"));
}
}
}
What I want
At first,I want to save All the Controllers Names and Actions Names of Project into separated Lists,then insert them into MvcController Table & ActionsTbl Table.This process should be done automatically when I Run Project for the first time.I mean When the Database is Created, the Lists inert to their tables automatically.
I think it's better to Add a New Custom Function to the Identity for inserting Lists to the Tables.
I appreciate your efforts in reaching a solution for my problem.
If desired types are Controller and ActionResult (without web api), then
you may get controllers and actions by using reflection
var controllers = Assembly.GetAssembly(typeof(*any type in assembly*))
.GetTypes()
.Where(x => x.IsSubclassOf(typeof(Controller)))
.ToList();
var result = controllers
.Select(type => new
{
ControllerType = type,
Actions = type.GetMethods()
.Where(m => m.ReturnType == typeof(ActionResult) || m.ReturnType.IsSubclassOf(typeof(ActionResult))).ToList()
})
.ToList();
Since you're using EF you can put all necessary logic into Seed Method
and enable Automatic Migration

Entity Framework 6, cannot use Ignore method on the property

Currently, I'm using ASP Identity with MVC 5.I want to remove phone number field from the AspNetUsers table, but when I use add-migration command it causes the following error.
You cannot use Ignore method on the property 'PhoneNumber' on type
'Models.DbModel.User' because this type inherits from the type
'Microsoft.AspNet.Identity.EntityFramework.IdentityUser`
I have already read tons of questions on here, but all of them said you have to ignore property in your base class, however, I don't have any access to the base in this case.
How can I solve this problem?
Update: when I used fluent API inside the OnModelCreating method it worked, I don't want to use it this way so I separated the config class for each entity.
Below is my code:
Derived Entity Class
public class User: IdentityUser
{
public ICollection<Comment> Comments { get; set; }
}
Config class
public sealed class UserConfig : EntityTypeConfiguration<User>
{
public UserConfig()
{
ToTable("dbo", "Users");
Ignore(x => x.PhoneNumber);
Ignore(x => x.PhoneNumberConfirmed);
}
}
Context Class
public class WebsiteContext : IdentityDbContext
{
public WebsiteContext()
: base("XYZ")
{
}
public DbSet<Comment> Comment { get; set; }
//public DbSet<User> User { get; set; }
public static WebsiteContext Create()
{
return new WebsiteContext();
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new CommentConfig());
modelBuilder.Configurations.Add(new UserConfig());
}
}
Try the [NotMapped] attribute from
System.ComponentModel.DataAnnotations.Schema
This might get you around that limitation , it has been used to ignore Enums in the mapping, this might not be exactly what you want

invalid column name in .net while using inheritance

I have a problem mapping a relationship
while reverse engineering in visual studio .net
I use inheritance:
public class Quiz : Component
{
public QuizQuestion rootQuestion { get; set; }
public override String getType() { return "quiz"; }
}
the property rootQuestion is not specified in the motherclass, it only exists in the subclass
Quiz doesn't exist as a table in my sqlserver database(only the QuizQuestions and Component table exists in the database, (my teacher told me to do it like this for
the java part of this project).
But I want the subclass Quiz to have a property rootQuestion that refers to quizRootQuestion in my database. So here's what I did:
public class QuizMapper : EntityTypeConfiguration<Quiz>
{
public QuizMapper()
{
this.HasKey(t => t.ComponentID);
this.HasOptional(c => c.rootQuestion)
.WithMany().Map(m => m.MapKey("quizRootQuestionID"));
}
}
public class QuizQuestionMap : EntityTypeConfiguration<QuizQuestion>
{
public QuizQuestionMap()
{
// Properties
// Table & Column Mappings
this.HasKey(t => t.QuestionID);
this.ToTable("QuizQuestions");
this.Property(t => t.QuestionID).HasColumnName("questionID");
}
}
I get this error when i'm trying to run in my browser:
Invalid column name 'rootQuestion_QuestionID'.
Tables in my database:
Component:
componentId
quizRootQuestionID
TypeId(=discriminator)
QuizQuestions
questionID
question
Can someone please tell me what I am doing wrong?
You would get exactly this exception if you forgot to add the QuizMapper to the model builder configuration in OnModelCreating of your derived context:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new QuizMapper());
modelBuilder.Configurations.Add(new QuizQuestionMap());
}

Categories