12

I'm lost using the authentication method that comes with MVC 5 Template.

I had the need to include the CreateBy user in an entity called client, so after some research I came to this:

Model:

[Table("Clients")]
public partial class Client
{
    [Key]
    [DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }

    public virtual int UserCreated_Id { get; set; }

    [ForeignKey("UserCreated_Id")]
    public virtual ApplicationUser UserCreated { get; set; }
}

Controller Method:

client.UserCreated_Id = User.Identity.GetUserId<int>();

But I had to change almost everything in the Identity Model:

From

public class ApplicationUser : IdentityUser

To

public class ApplicationUser : IdentityUser<int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>

And almost 30 changes because of this.

But now I have 2 DbContext:

Identity Context:

public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, int, ApplicationUserLogin, ApplicationUserRole, ApplicationUserClaim>
{
    public ApplicationDbContext() : base("IPDB") {}

    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }
}

My Application Context:

public class MyDbContext : DbContext
{
    public MyDbContext() : base("IPDB")
    {

        // Tells Entity Framework that we will handle the creation of the database manually for all the projects in the solution
        Database.SetInitializer<MyDbContext>(null);
    }

    public DbSet<Client> Clients { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        // ANOTHER CHANGE I HAD TO MADE TO BE ABLE TO SCAFFOLDING
        modelBuilder.Entity<ApplicationUserLogin>().HasKey<int>(l => l.UserId);
        modelBuilder.Entity<ApplicationRole>().HasKey<int>(r => r.Id);
        modelBuilder.Entity<ApplicationUserRole>().HasKey(r => new { r.RoleId, r.UserId });
    }
}

My problem now are:

  • Do I need 2 DbContext?
  • Am I associating the user to the client entity correctly?
  • I need to create a list of all users, and aditional information, will I read the information from 2 DbContext?

Please, I need some clear guide because I am very confused right now and I really love to build great code, and I think it's not the case.

Patrick
  • 2,995
  • 14
  • 64
  • 125

2 Answers2

4

Do I need 2 DbContext?

Having a DbContext for the Identity and other for the rest of the app is considered a good practice for some people. However it is not mandatory, and I don't think it is necessary.

You should not have big dbcontexts, smaller contexts are faster. It means that sometimes you have to create more than one dbcontext. I believe 50 entities per context should be enough.

Am I associating the user to the client entity correctly?

You are not (in my opinion). The ApplicationUser uses a Guid (by default) to represent its primary key value, not an Int32. So, instead of this:

public virtual int UserCreated_Id { get; set; }

[ForeignKey("UserCreated_Id")]
public virtual ApplicationUser UserCreated { get; set; } 

you should use this (remember to remove the virtual in UserCreated_Id):

public Guid UserCreated_Id { get; set; }

[ForeignKey("UserCreated_Id")]
public virtual ApplicationUser UserCreated { get; set; } 

in your context, this should be enough:

public class ApplicationUser : IdentityUser
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>

I need to create a list of all users, and aditional information, will I read the information from 2 DbContext?

Yeah, you probably will, but I don't think it is a big performance deal, because it won't cause any additional database query, considering that you are using only 1 dbcontext at the time.

Please, I need some clear guide because I am very confused right now and I really love to build great code, and I think it's not the case.

Do you like clean code? So, here comes my tip (based on my opinion): FORGET ABOUT ASP.NET IDENTITY! Identity is a bad solution, it is not decoupled, confusing, and unnecessary.

You can create your own login system, using a custom User entity. Use this https://crackstation.net/hashing-security.htm#aspsourcecode for hashing the passwords and this MVC Authentication - Easiest Way to create an OWIN authentication

Community
  • 1
  • 1
Fabio
  • 11,892
  • 1
  • 25
  • 41
  • Muito Obrigado Fabio :) How can I manipulate the logged user information so I can associate it with the client or I can extract information? Going for a new login system that I don't know maybe complex if I don't start with a clean simple sample. – Patrick Dec 06 '15 at 22:26
  • 1
    Totally disagree with "Create your own login system" recommended to a person clearly not ready for this. Also, I've never heard of "Microsoft recommends maximum of 50 entities per context" before. Can you provide a reference for that? – trailmax Dec 07 '15 at 01:10
  • Another incorrect statment "The ApplicationUser uses a Guid". It can use `Guid`. In the provided code you can see `ApplicationUser` is taking `int` as a `TKey` generic parameter. – trailmax Dec 07 '15 at 01:16
  • 1
    Reading data from 2 different DbContext in EF will create 2 separate requests to the DB. So statement "it won't cause any additional database query" is not correct. – trailmax Dec 07 '15 at 01:17
  • @trailmax did you read the question? `ApplicationUser` uses a `Guid` by default. The OP doesn't want to make a lot of changes. In his example, he's using `Int32`, that's why he had to make a lot a changes... Futhermore, I can have multiple dbcontexts, it won't cause any performance issue, considering that I using only 1 dbcontext at the time. – Fabio Dec 07 '15 at 04:37
  • There are also performance benefits associated with the use of smaller contexts. The initialization of contexts, when loaded for the first time, maps its metadata in memory. Such metadata describing the existing properties in each entity, their respective columns in the tables, their relationships, and other characteristics of the model. When using smaller contexts, the time of mapping these metadata is much smaller and this directly affects the time it takes to initialize the contexts. – Fabio Dec 07 '15 at 04:37
  • Smaller contexts also have a faster construction of queries, since the mapping views (representations used for execute queries and execute updates) are smaller, so the validation of the conceptual model and the database model is faster. – Fabio Dec 07 '15 at 04:42
  • @Patrick as I've mentioned, this thread http://stackoverflow.com/questions/32095889/mvc-authentication-easiest-way shows how to configure the authentication. This one similar http://stackoverflow.com/questions/33398961/db-first-authentication-confusion-with-asp-net-web-api-2-ef6/33546674#33546674, but for WebAPI instead of MVC – Fabio Dec 07 '15 at 04:55
  • @FabioLuz Yes I saw it but I confess that it's not very simple to understand for me. – Patrick Dec 07 '15 at 12:04
  • @Patrick I'll try to find an easier solution for your problem. I'll post here when I solve this – Fabio Dec 11 '15 at 16:10
  • @FabioLuz Ok thank you, or maybe simplify the use I have started to do with Identity. The next step I need for example is to extract the username of the user in a relationship with another entity like Calls (who created the call). – Patrick Dec 11 '15 at 16:42
  • @Patrick Take a look at this link http://blogs.msdn.com/b/webdev/archive/2013/10/16/customizing-profile-information-in-asp-net-identity-in-vs-2013-templates.aspx You will see that he is using a custom class called `MyUser`. Then, the only thing you'll have to do is create a `MyUser` object inside `Call` class. EF you handle the relationship automatically (remember to add and apply migrations) – Fabio Dec 11 '15 at 16:52
  • I don't use Code First or Migrations – Patrick Dec 11 '15 at 16:56
  • you are using Code First. `MyDbContext : DbContext` Dbcontext is the main class of Code First. Are you saying that your other classes (like `Call`) are not using Code-First? – Fabio Dec 11 '15 at 17:07
  • @FabioLuz Hi, Thank you for your help. I have checked Anders's answer has correct because it seems more simple and easy to use everything that comes with the MVC template and trying to adapt to my reality. Mas muito obrigado pela sua ajuda ;) – Patrick Dec 14 '15 at 15:23
  • Sem problemas! obrigado eu! – Fabio Dec 14 '15 at 15:30
4

Type of Id on ApplicationUser

ASP.NET Identity 2.0 is very flexible, while also offering some descent default implementations that will do in mose cases. The MVC 5 template uses the default implementations in most places, but in some cases some extra stuff is added to make customization easier.

Internally ASP.NET Identity always uses IdentityUser<...>. All those template arguments provide a way to chose your own key type, which means you'll have to chose your own IdentityUserRole type as well (as it contains the key).

There's also the convenience default implementation that uses a string for key. The value of the string is the string representation of a GUID.

public class IdentityUser : 
  IdentityUser<string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>, IUser, IUser<string>
{
  /// <summary>
  /// Constructor which creates a new Guid for the Id
  /// </summary>
  public IdentityUser()
  {
    this.Id = Guid.NewGuid().ToString();
  }

Having a string as primary key in a database is not that common, but for security sensitive stuff it makes sense. There is no risk that data copied between databases will have colliding IDs, giving users wrong priveleges.

In your case you need to chose: Use the default implementation with a string PK or change the id to an int. In the latter case you have to change "almost everything" in the identitymodel as you've discovered. Although I think you could do without implementing your own ApplicationRole. It should be possible to just declare ApplicationUser as

public class ApplicationUser: IdentityUser<int, IdentityUserLogin<int>, IdentityUserRole<int>....>

DbContexts

In most cases a single DbContext is fine for an application. If multiple DbContexts are used, they should be for distinct different sets of data that do not relate to each other. As you've set up your Client class to relate to ApplicationUser they should both be in the same DbContext. The template tries to convey the thought that the main DbContext of the application context should inherit IdentityDbContext<> by naming it ApplicationDbContext. Use that one, and extend it with your own stuff. I usually rename it and move it to somewhere else, but still I have one DbContext only and let it inherit IdentityDbContext<>.

Rolling your own Identity Solution

Unless you really, really know what you're doing - don't roll your own identity solution. Use an existing one that has been security hardened by external reviews. If you're not happy with ASP.NET Identity you can have a look at Brock Allen's Identity reboot.

For credibility (yes I'm a bounty hunter today): The official ASP.NET documentation links to my blog.

Anders Abel
  • 67,989
  • 17
  • 150
  • 217
  • Hi thanks, it's really not easy to choose which way to follow, It's still not clear for me a simple Microsoft Identity implementation to use in my project so I can use the logged user to relate with my other entities... – Patrick Dec 14 '15 at 14:18
  • Do you have any remaining specific questions that I can add to the answer? – Anders Abel Dec 14 '15 at 14:21
  • I think that if I ask you to show me a path based on my code and what is the right way in your opinion, I might be pushing the limits so, just help me has you think I should go from my code if it's ok for you. It's a field very dark from here :) – Patrick Dec 14 '15 at 14:23
  • 1
    The most simple way: Change `Client.UserCreated_Id` to a string. That will allow you to use the default ASP.NET Identity implementations. Use one single DbContext. – Anders Abel Dec 14 '15 at 14:26
  • How I will have access to the username so I can show it in the Client View or do the connection between both? – Patrick Dec 14 '15 at 14:40
  • 1
    If you're using EF: `ApplicationContext.Clients.Select(c => new {c.Id, c.UserCreated.UserName })`. If you're not using EF for the rest of your application the right way is really to implement `IUserStore<>` yourself on top of whatever DB access you have. – Anders Abel Dec 14 '15 at 14:49
  • Ok, this will be possible has soon has I include my custom entities inside of the ApplicationContext correct? – Patrick Dec 14 '15 at 15:19
  • 1
    Yes, if you go the EF path you should add all of your entities to ApplicationContext. – Anders Abel Dec 14 '15 at 15:20
  • Thank you very much for your help, I have check your help has correct. If you don't mind I might ask some more details when I tried to implement the changes, ok? Thank you. – Patrick Dec 14 '15 at 15:22