ASP MVC5 Identity User Abstraction - c#

I want to build N-tire web application with default Identity 2 provider. So, my Data layer contains pure c# classes with model definition, without any externad dependency. But it is impossible to link some classes to my Application User without adding AspNet.Identity reference.
I have tried to make an interface of User class:
public interface ISystemUser
{
string Id { get; set; }
string Title { get; set; }
}
public class Place
{
public int Id { get; set; }
public string Address { get; set; }
public ISystemUser User { get; set; }
}
And in Infrastructure layer substitute it with implementation:
public class ApplicationUser : IdentityUser, ISystemUser
{
public string Title { get; set; }
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext() : base("DefaultConnection", throwIfV1Schema: false)
{
}
public DbSet<Place> Places { get; set; }
}
But entity framework does not creates relation between entities.
Is there any 'right' way do implement this or it is necesarry to add reference?

There's a workaround, which is pretty ugly in my opinion but works.
You will need 2 classes, one for the User and another for the ApplicationUser. ApplicationUser must have all properties of User. Like this:
//Domain Layer
public class User
{
public string Id { get; set; }
public string Title { get; set; }
}
//Infrastructure Layer
public class ApplicationUser
{
public string Title { get; set; }
}
Now, the trick is mapping the User class to the same table of the ApplicationUser class. Like this:
public class UserConfig : EntityTypeConfiguration<User>
{
public UserConfig()
{
HasKey(u => u.Id);
ToTable("AspNetUsers");
}
}
Hope it helps!

Related

How to create a Favorites system in ASP.NET Core API with these limitations?

I want to create a Favorites system in my API but I have some limitations due to architecture.
I'm using Clean Architecture ( or a form of it at least ) in the sense that my project is built as such:
Project structure
App.Domain
-> App.Application ( has reference to App.Domain )
-> App.API ( has reference to App.Application )
-> App.Persistence ( has reference to App.Application )
-> App.API ( has reference to App.Persistence )
-> App.Identity ( has reference to App.Application )
-> App.API ( has reference to App.Identity )
( The API being the same one, I only have one API )
Now I'm trying to create a Favorite system this way:
App.Domain -> Entities -> Item.cs
public class Item: AuditableEntities
{
public Item()
{
}
public Guid ItemId { get; set; }
public string Name { get; set; }
public List<ApplicationUser> Users { get; set; }
}
App.Identity -> Models -> ApplicationUser.cs
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public List<Item> FavoritedItems { get; set; }
}
The problem is I can't refer to ApplicationUser from the Item entity because it doesn't know about the Identity project, however I can refer to the Item entity from the ApplicationUser model because it knows about the Domain project.
Should I try to refer to the ApplicationUser model from the Place entity or is there a better way to do it ?
Thank you so much for your help.
EDIT:
Like #smokesnes advised, I did this:
// namespace App.Domain.Entities
public class Item: AuditableEntities
{
public Item()
{
}
public Guid ItemId { get; set; }
public string Name { get; set; }
public List<IApplicationUser> Users { get; set; }
}
// namespace App.Domain.Contracts
public interface IApplicationUser
{
string FirstName { get; set; }
string LastName { get; set; }
string Gender { get; set; }
List<Item> FavoritedItems { get; set; }
}
And I have an Identity project that has the real ApplicationUser : IdentityUser<Guid>, IApplicationUser like he said.
However now I have a problem, My AppDbContext ( App.Persistence ) uses Configuration files to know what the relationships are between tables, the ItemConfiguration file uses this line to express its one to many relationship with the IApplicationUser interface:
AppDbContext:
// App.Persistence
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options)
: base(options)
{
}
public DbSet<Item> Items { get; set; }
public DbSet<Favorite> Favorites { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new ItemConfiguration());
modelBuilder.ApplyConfiguration(new FavoriteConfiguration());
}
// ...
}
ItemConfiguration.cs
// App.Persistence
builder.HasOne<Favorite>(d => d.Favorite)
.WithMany(d => d.Items);
FavoriteConfiguration.cs
// App.Persistence
builder.HasOne<IApplicationUser>(d => d.User)
.WithMany(d => d.Favorites);
And when I try to run a migration here's what I get:
System.ArgumentException: The specified type 'App.Domain.Contracts.IApplicationUser' must be a non-interface reference type to be used as an entity type.
And the problem is I can't change it to ApplicationUser because then I have to change it in the Item entity and that's where I needed the interface in the first place.
Also, I add a scope to the ApplicationUser and its interface in the App.Identity.IdentityServiceRegistration.cs that gets added in the App.Api.Startup.cs.ConfigureServices method:
IdentityServiceRegistration.cs
// App.Identity
public static void AddIdentityServices(this IServiceCollection services, IConfiguration configuration)
{
services.AddIdentity<ApplicationUser, IdentityRole<Guid>>()
.AddEntityFrameworkStores<AppIdentityDbContext>().AddDefaultTokenProviders();
services.AddScoped<IApplicationUser, ApplicationUser>();
}
And for #JeremyLakeman:
AppIdentityDbContext
// App.Identity
public class AppIdentityDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid>, Guid>
{
public AppIdentityDbContext(DbContextOptions<AppIdentityDbContext> options) : base(options)
{
}
}
Does anyone know what should be done ?
There might be several ways to do this. One simple way is to simply add an interface that the Domain knows about, but the implementation is in another project. As such;
// namespace App.Domain.Entities
public class Item: AuditableEntities
{
public Item()
{
}
public Guid ItemId { get; set; }
public string Name { get; set; }
public List<IApplicationUser> Users { get; set; }
}
// namespace App.Domain.Entities or App.Domain
public interface IApplicationUser
{
string FirstName { get; set; }
string LastName { get; set; }
string Gender { get; set; }
List<Item> FavoritedItems { get; set; }
}
And then let your ApplicationUser implement this interface:
// namespace App.Identity.Models
public class ApplicationUser : IdentityUser, IApplicationUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public List<Item> FavoritedItems { get; set; }
}
Then your Item-entity can deal with the interface without having a tight coupling to the implementation. However, you cannot create the ApplicationUser in the Domain-project. It will need to be created outside. Or again, you can create a factory where the interface is known to the Domain, but the implementation is not.
It looks like you are already using a DbContext that extends IdentityDbContext<...> in order to define your ApplicationUser. So lets borrow a little from this design pattern used by Microsoft.AspNetCore.Identity.EntityFrameworkCore, and split your model definition between packages.
You'll probably also need to merge your AppDbContext into your AppIdentityDbContext so that you only define one context. Otherwise you'll have trouble querying with joins across your models. However, since your context must extend IdentityDbContext, this can't be an abstract type.
Your App.Domain package doesn't know anything about ApplicationUser, so it can't define any navigations.
namespace App.Domain
{
public interface IAppContext<TItem> where TItem : Item
{
DbSet<TItem> Items { get; set; }
}
public class Item: AuditableEntities
{
public Item()
{
}
public Guid ItemId { get; set; }
public string Name { get; set; }
}
}
Your App.Identity package does know about users, so it can extend that definition. And define the DbContext that your application will actually use.
namespace App.Identity
{
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Gender { get; set; }
public List<FavouriteItem> FavoritedItems { get; set; }
}
public class FavouriteItem : Item
{
public List<ApplicationUser> Users { get; set; }
}
public class AppIdentityDbContext : IdentityDbContext<ApplicationUser, IdentityRole<Guid>, Guid>
{
...
public DbSet<FavouriteItem> Items { get; set; }
}
}
You seem to be mixing Domains here. What you want is probably to split your Domain Models based on their Bounded Contexts, so that each Model class operates only on the data it actually needs.
This would basically mean to have at least one service for User Profiles, handling your ApplicationUser models.
Then you'll have a different service for Favorites.

Entity Framework - inheriting from model

I'm new to the Entity Framework and I've followed tutorials online to create my SQL Server database and made several models and a context class to include properties to access them.
This is my account model:
public class Account
{
public int ID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
This is my context class:
public class DashContext : DbContext
{
public DashContext()
: base(Constants.ConnectionString)
{
this.Configuration.LazyLoadingEnabled = true;
this.Configuration.ProxyCreationEnabled = false;
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
Database.SetInitializer<DashContext>(null);
}
public DbSet<Account> Accounts { get; set; }
}
This works - when I access the DbSet property I can access all the account entires in my database.
However, I want to create an implmentation of the Account class that contains more properties than just columns because it has to interact with my program.
So, I tried to do the following:
public class GameAccount : Account
{
public int SomeSpecialProperty { get; set; }
}
However, when I'm using my context class to get the Account object, I'm not sure how to convert it to GameAccount. I know I can create a constructor that copies the properties from Account to GameAccount, like this:
public class GameAccount
{
public int ID { get; private set; }
public string Username { get; private set; }
public string Password { get; private set; }
public GameAccount(Account model)
{
this.ID = model.ID;
this.Username = model.Username;
this.Password = model.Password;
}
}
...but that seems a bit inefficent to me and I'm sure there's a simpler way.
What do you think?
You have a few options:
Option 1
Use a partial class as indicated by Fruchtzwerg.
Option 2
You can use AutoMapper to map the items from one type to the other. Here is an example:
// Notice the ReverseMap call. That will allow mapping in both directions.
Mapper.Initialize(cfg =>
cfg.CreateMap<Account, GameAccount>().ReverseMap());
var account = Mapper.Map<Account>(new GameAccount());
var gameAccount = Mapper.Map<GameAccount>(account);
Copy Constructors could be very costly to develop and maintenance. Typically generated classes of the Entity Framework are partial.
BradleyDotNET explains:
When code is generated; you don't want your additional methods/properties/whatever blown away, so the designers mark such classes partial to allow users to put additional code in a different file.
So a possible approach is extending the class
public partial class Account
{
public int ID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
}
with additional properties like
public partial class Account
{
public int SomeSpecialProperty { get; set; }
}

Code first Sharing Primary Key between tables

I am using Entity Framework code first to try and create a database whereby I have several tables that share unique primary keys.
I have the following structure to my domain classes, and would hope that FirstClass, SecondClass, ThirdClass would all be created as tables that link to a BaseTable where the shared keys are stored. I have included the SharedClass class in this example since I also have this strutcure in my project.
[Table("BaseTable")]
public abstract class BaseClass {
public int Id { get; set; }
}
public abstract class SharedClass : BaseClass {
public string SharePropertyOne { get; set; }
public string SharePropertyOne { get; set; }
}
[Table("FirstClass")]
public abstract class FirstClass : SharedClass {
public string SharePropertyOne { get; set; }
public string SharePropertyOne { get; set; }
}
[Table("SecondClass")]
public abstract class SecondClass : SharedClass {
public string SharePropertyOne { get; set; }
public string SharePropertyOne { get; set; }
}
[Table("ThirdClass")]
public abstract class ThirdClass : SharedClass {
public string SharePropertyOne { get; set; }
public string SharePropertyOne { get; set; }
}
What is currently happening is that FirstClass, SecondClass and ThirdClass are all being created but with their own primary keys and no BaseTable is being created.
I am not sure if I am missing something after looking through several questions on here that are asking something similar.
The EF inheritance is not activated by default and even by applying Table data annotation on a base class.
It can be activated by either adding a DbSet to the derived DbContext:
public DbSet<BaseClass> BaseClass { get; set; }
which also allows you to use polymorphic queries, or with fluent configuration - the bare minimum needed is:
modelBuilder.Entity<BaseClass>();

How to Specify Entity Framework Core Table Mapping?

I've made a simple Entity Framework ASP Core Application that works but I do not know why:
I've made a context like this:
public class AstootContext : DbContext
{
public AstootContext(DbContextOptions<AstootContext> options)
: base(options)
{ }
public DbSet<Account> Accounts { get; set; }
public DbSet<User> Users { get; set; }
}
And I have two tables with models like this:
public class Account
{
public int Id { get; set; }
public string Username { get; set; }
public string PasswordHash { get; set; }
public DateTime Created { get; set; }
List<User> Users { get; set; }
}
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public DateTime Birthday { get; set; }
public Account Account { get; set; }
}
The interesting thing is that when I run my application it actually can pick up the data. It just seems weird because I have not specified any table mapping.
I'm assuming this just automaps because the specified tables are the same name.
My questions are:
How do I specify Table explicit table mapping in case I do not want my model names to be exactly the same as the DB?
How do I specify Custom Column Mapping.
Is there anything special I have to specify for Primary/Foreign Keys
edit
To clarify
Say I had a table in the DB MyAccounts and I wanted to map that to an entity Accounts.
Say I had a column password and I wanted that to map to a POCO property PasswordHash
To specify the name of the database table, you can use an attribute or the fluent API:
Using Attributes:
[Table("MyAccountsTable")]
public class Account
{
public string PasswordHash { get; set; }
}
Using Fluent API:
public class YourContext : DbContext
{
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Language>(entity => {
entity.ToTable("MyAccountsTable");
});
}
}
To name your columns manually, it's very similar and you can use an attribute or the fluent API:
Using Attributes:
public class Account
{
[Column("MyPasswordHashColumn")]
public string PasswordHash { get; set; }
}
Using Fluent API:
public class YourContext : DbContext
{
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<Language>(x => x
.ToTable("MyAccountsTable")
.Property(entity => entity.PasswordHash)
.HasColumnName("MyPasswordHashColumn")
);
}
}

EF7 Incorrect configuration of the DBContext?

I am trying to figure out if my DBContext is set right. The Web Application I am trying to do is using ASP.NET 5, MVC6 and EF7.
It is connected to a DB that contains 3 tables (Comment, Review, Film). This is my model
WebDbModel.cs
using Microsoft.AspNet.Identity.EntityFramework;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace WebExample.Models
{
public partial class Comment : IdentityUser
{
public string CommentId { get; set; }
public string ReviewId { get; set; }
public string Author { get; set; }
public System.DateTime Created { get; set; }
}
public partial class Review : IdentityUser
{
public string ReviewId { get; set; }
public string Name { get; set; }
public string Author { get; set; }
public string Creator { get; set; }
public string Status { get; set; }
public Nullable<System.DateTime> CreatedOn { get; set; }
}
public partial class Film : IdentityUser
{
public string ReviewId { get; set; }
public string FilmID { get; set; }
public string CommentId { get; set; }
public Nullable<int> CommentCount { get; set; }
public string FilmName { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public virtual Review Review { get; set; }
}
}
Then, I have a class named
RegistrationDbContext.cs
using Microsoft.AspNet.Identity.EntityFramework;
using Microsoft.Data.Entity;
namespace WebExample.Models
{
public class ApplicationUser : IdentityUser
{
}
public class RegistrationDbContext : IdentityDbContext<ApplicationUser>
{
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);
}
// Registration of the DB tables we are mapping.
public DbSet<Comment> Comments { get; set; }
public DbSet<Review> Reviews { get; set; }
public DbSet<Film> Films { get; set; }
}
}
I am wondering from here what is the use of public class ApplicationUser : IdentityUser
I am not sure if I should leave it there... If I remove it, then in my Startup.cs, the following code will complain...
// Add Identity services to the services container.
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<RegistrationDbContext>()
.AddDefaultTokenProviders();
So I don't know to what replace it given the fact I have Comment, Review and Film models...
So my question is... I guess I can delete the ApplicationUser class (auto generated when I created my solution) since this is not part of my model at all but... what should I put instead?
Not too sure if my question is right though, so apologies in advance! This seemed to be changed a lot compared to MVC5, EF6 and I couldn't fine too much documentation around IdentityDbContext
Also... am I missing something else in the RegistrationDbContext.cs class? The only extra thing I added was the registration of the tables... the rest came in the class by default.
Thanks!
There are a couple of issues here.
Firstly, IdentityUser is the representation of a logged-in user to your site, and is by default represented by the AspNetUsers table in the database, and stores all the usual stuff like email address, password, etc.
The provided ApplicationUser subclass is there in case you want to extend that in any way. Say for example you want to store the date of birth of your users:
public class ApplicationUser : IdentityUser
{
public DateTime DateOfBirth { get; set; }
}
That will tack on a DateOfBirth column to the AspNetUsers table.
In your case, if you don't want to extend the default user table in any way, you can just delete the ApplicationUser class and replace all references to it in your code to IdentityUser. For example:
// Add Identity services to the services container.
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<RegistrationDbContext>()
.AddDefaultTokenProviders();
Secondly, your models should not be inheriting from IdentityUser. That will just add all the fields from the AspNetUsers table to every model, which doesn't make sense, so remove : IdentityUser from your models.

Categories