12

I am using this sample project (https://github.com/imranbaloch/ASPNETIdentityWithOnion) as my application architecture, in this sample the core is completly decoplied from the infrastrure including the identity framework.

In this sample the author has used the adapter pattern to decouple core identity classes (IdentityUser, IdentityRole ... ) and provide classes like them in the Core layer.

Now the issue in this sample project is that the Domain model (Product, Images) are not linked with the dummy classes (AppUser, ApplicationRole, AppliationUserRoles, ...) that mimics the Identity ones.

Then I have modified the code to added the reference to AppUser

public sealed class Image : BaseEntity
{
    public Image()
    {
        Products = new HashSet<Product>();
    }

    public string Path { get; set; }

    public AppUser AppUser { get; set; } // The  Added Reference ...

    public ICollection<Product> Products { get; set; }
}

If I put the "AppUser" navigation property inside the "Image" class, the created database will have FOUR new tables other than the default FIVE tables of the identity framework.

Onion Database Problem with Identity Tables

I need to merge these tables into the default ones. how ?

EDIT:

This is the Identity Models that resides in the Data Layer (which I can not reference from the core).

public class ApplicationIdentityUser :
    IdentityUser<int, ApplicationIdentityUserLogin, ApplicationIdentityUserRole, ApplicationIdentityUserClaim>, IDomainUser {

    public ApplicationIdentityUser()
        : base() {
        Images = new HashSet<Image>();
    }

    public string Name { get; set; }
    public virtual ICollection<Image> Images { get; set; }
}


public class ApplicationIdentityRole : IdentityRole<int, ApplicationIdentityUserRole>
{
    public ApplicationIdentityRole(){}

    public ApplicationIdentityRole(string name){Name = name;}
}

public class ApplicationIdentityUserRole : IdentityUserRole<int> {}

public class ApplicationIdentityUserClaim : IdentityUserClaim<int>{}

public class ApplicationIdentityUserLogin : IdentityUserLogin<int>{}

Also this is my model builder in the OnModelCreating method:

  modelBuilder.Entity<Image>()
            .Property(e => e.Id)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        modelBuilder.Entity<Image>()
            .HasMany(e => e.Products)
            .WithRequired(e => e.Image)
            .WillCascadeOnDelete(false);
        modelBuilder.Entity<ApplicationIdentityUser>()
             .Property(e => e.Id)
             .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        modelBuilder.Entity<ApplicationIdentityRole>()
            .Property(e => e.Id)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        modelBuilder.Entity<ApplicationIdentityUserClaim>()
             .Property(e => e.Id)
             .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Zaid Al-Omari
  • 711
  • 1
  • 8
  • 17
  • 1
    AspNet* tables are surplus. Need to get rid of them. Check what is your ApplicationDbContext is doing with these tables? – trailmax Oct 27 '14 at 11:09
  • my ApplicationDbContext is actully driving from the IdentityDbContext, and I need a way to store user information, do you mean that i can use the tables that begins with Application* ? – Zaid Al-Omari Oct 27 '14 at 13:57
  • 1
    I'm saying that there should be only one set of *Users, *UserClaims, etc. tables. Looks like the way you do inheritence in your ApplicationDbContext is broken and you are not overriding `Users` and `Roles` and other, so EF creates 2 sets of tables for you. Check for that. – trailmax Oct 27 '14 at 14:11
  • okay thats good, I will update my question to include implementation details – Zaid Al-Omari Oct 27 '14 at 14:28
  • @trailmax i have updated the question, please check ... – Zaid Al-Omari Oct 27 '14 at 14:33
  • What do `IDbset`s look like in ApplicationDbContext? – trailmax Oct 27 '14 at 15:31
  • there are no IDbSet in the ApplicationDbContext, entities are only defined using the Fluent API on the OnModelCreating (which also call the base.OnModelCreating). you can check the full project on github in the provided link (first line of the question). – Zaid Al-Omari Oct 27 '14 at 15:59

3 Answers3

26

Okay I have solved this by doing the following:

  1. Include a dependency in your Core to Microsoft.AspNet.Identity.Core
  2. Implement IUser interface on the AppUser (this interface come from Microsoft.AspNet.Identity.Core).
  3. Implement IRole interface on the ApplicationRole.
  4. Completely get rid of the IdentityDbContext and inherit only from DbContext.
  5. Implement your own version of IUserStore* providing your AppUser
  6. Implement your own version of IRoleStore providing your ApplicationRole.

I know that making a dependency on the Microsoft.AspNet.Identity.Core sounds Odd, but we only need the interface IUser which is basically considered as Core Domain Model for your application too.

The Ultimate Idea here is to GET RID OF THE Microsoft.AspNet.Identity.EntityFramework completely.

Interested devs can +1 this, so I can upload a full working sample on GitHub.

Zaid Al-Omari
  • 711
  • 1
  • 8
  • 17
  • 2
    I was just about to embark on this journey as I came to the same conclusion that Microsoft.AspNet.Identity.EntityFramework is nice sample code but interferes with your domain if you intend users to be first class domain models. – Chris Gomez Dec 05 '14 at 22:27
  • Would really like to see how you've get rid of the IdentityDbContext and how you referenced your ApplicationIdentityUser in the Image entity as it resides in the Core and not the Data layer! Any example would be appreciated. – webStuff May 29 '15 at 04:02
  • Any chance you could put an example on GitHub? – webStuff Jun 23 '15 at 00:56
  • were you going to put up a sample? – Brian Rice Aug 16 '15 at 18:48
  • I think this is the sample https://github.com/imranbaloch/ASPNETIdentityWithOnion – Ruchan May 11 '16 at 05:57
  • 1
    I don't think so, search the repository and you'll find Microsoft.AspNet.Identity.EntityFramework – JoshYates1980 Feb 23 '17 at 21:40
2

I'm using this framework, no need to have a link in every entity To get the userID reference, i added a property UserIDBy in BaseEntity so every entity will inherit it.

public abstract class BaseEntity
{
    public int Id { get; set; }
    public string UserIDBy { get; set; }
}

Next, in the web project, there is already an extension method called GetUserId(this IIdentity identity) in IdentityExtensions.cs, so to stock the UserIDBy in every Create and Edit action result:

Create Action Result :

 // POST: /Region/Create
    [HttpPost]
    public async Task<ActionResult> Create([Bind(Include = "RegionName")] Region region)
    {
        if (ModelState.IsValid)
        {
            // TODO: Add insert logic here
            var id = User.Identity.GetUserId();
            region.UserIDBy = id.ToString(); 

            await _regionService.AddAsync(region);
            return Json(new { success = true });
        }

        return PartialView("_Create", region);
    }

Edit Action Result :

 //// POST: /Region/Edit/5
    [HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Edit([Bind(Include = "id,RegionName")] Region region)
    {
        if (ModelState.IsValid)
        {
            var id = User.Identity.GetUserId();
            region.UserIDBy = id.ToString(); 
            _regionService.Update(region);
            return Json(new { success = true });
        }
        return PartialView("_Edit", region);
    }

Dont forget to import it :

using Myapp.Web.Extensions;
Marouane Afroukh
  • 2,783
  • 2
  • 14
  • 9
  • Very clever. Thanks for sharing. The ASPNETIdentityWithOnion is a great shell, but the headache of creating the relationship to custom tables and identity framework is not worth it. – JoshYates1980 Feb 23 '17 at 21:52
  • Do you have an open source project that implements ASPNETIdentityWithOnion? – JoshYates1980 Feb 24 '17 at 01:11
1

Just stumbled upon this, having the same problem.

The problem with the marked answer is that it still references Microsoft.AspNet, which makes it rough for future .NET Core-plans.

The main problem is really with the general attempt to build in an authentication feature in the core, which defeats the purpose.

Consider letting the built in functionality for web authentication and authorization remain in the web layer, and reference a Core user object (UserProfile?) that represents the Core needs. This would also simplify switching to another authentication method (AD).

Depending on your preference, you can then reference the Core.UserProfile from your AspNetUser to avoid multiple SQL-calls, or simply make sure to have a good cache policy on the Core.UserProfile operations.

This allows you to control your authentication methods separate from your Core model.

Classe
  • 256
  • 1
  • 6