User details within the DbContext model builder - c#

I'm new to razor pages / efcore / aspnet identity and have been trying to figure this out but its beating me.
Basically, I use AspNet Identity for user authentication & authorisation. I've extended AspNetUsers with an additional OrganisationId, which is an FK to Organisation entity; and added the ID as a claim in the identity claim store. This works fine.
Now I need to set an efcore global filter based on the authenticated user's organisationId so they can only view data that is assigned to their organisation.
However, I can't access the authenticated user details within the ModelBuilder.
public class SDMOxContext : IdentityDbContext<
ApplicationUser, ApplicationRole, string,
ApplicationUserClaim, ApplicationUserRole, ApplicationUserLogin,
ApplicationRoleClaim, ApplicationUserToken>
{
public SDMOxContext(DbContextOptions<SDMOxContext> options)
: base(options)
{ }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Set global filter so users can only see projects within their organisation.
builder.Entity<Project>().HasQueryFilter(project => project.OrganisationId == 1);
}
Instead of 1 in the global filter, I need to enter the user organisationid, which is stored as a user claim. Usually I get it with this:
User.FindFirstValue("OrganisationId")
However, User doesn't exist in the current context.

So I would need to apply the query filter at a later stage, ie. after user authentication? Any pointers where to start with a mid-tier/logic-tier approach?
Granted this is an opinion on architecture, but I break it down like this:
Data-Tier - This tier's responsibility to to access resources (normally) outside the executing application. This includes; Databases, File IO, Web Api's etc.
Business/Logic-Tier - This tier's responsibility (which could be broken down further) should Authenticate, Authorize, Validate and build objects that represent the businesses needs. To build these objects, it may consume one or more data access objects (for example, it may use an IO DA to retrieve the Image from a local file system or Azure storage and a Database DA to retrieve metadata about that image).
Presentation/Exposure-Tier - This tier's responsibility is to wrap and transform the object into the consumers need (winforms, wpf, html, json, xml, binary serialization etc).
By leaving logic out of the data-tier (even in multi-tenant systems) you gain the ability to access data across all systems (and trust me there is a lot of money to be made here).
This is probably way more than I can explain in such a short place and very my opinion. I'm going to be leaving out quite a bit but here goes.
Data-Tier
namespace ProjectsData
{
public interface IProjectDA
{
IProjectDO GetProject(Guid projectId, Guid organizationId);
}
private class ProjectDA : DbContext, IProjectDA
{
public ProjectDA (...)
public IEnumerable<ProjectDO> Projects { get; set; }
protected override void OnModelCreating(ModelBuilder builder) {... }
public IProjectDO GetProject(Guid projectId, Guid organizationId)
{
var result = Projects
.FirstOrDefault(p => p.Id == projectId && OrganizationId = organizationId);
return result;
}
}
public interface IProjectDO{ ... }
private class ProjectDO: IProjectDO
{
public Guid Id { get; set; }
public Guid OrganizationId { get; set; }
public Guid CategoryId { get; set; }
}
}
Logic
namespace ProjectBusiness
{
public interface IProjectBO { .. }
public interface IOrganization
{
Guid OrganizationId { get; }
}
private class ProjectBA : IProjectBO
{
private readonly IProjectDA _projectDA;
private readonly IIdentity _identity;
private readonly IOrganization _organization;
public ProjectLogic(IProjectDA projectDA,
IIdentity identity,
IOrganizationContext organizationContext)
{
_projectDA = projectDA;
_identity = identity;
}
public IProjectBO GetProject(Guid id)
{
var do = _projectDA
.GetProject(id, _organization);
var result = map.To<ProjectBO>(do);
return result;
}
}
public interface IProjectBO { .. }
private class ProjectBO
{
public Guid Id { get; set; }
public Guid OrganizationId { get; set; }
public Guid CategoryId { get; set; }
}
}
So under these circumstances the data layer is aware of type of request, but isn't multi-tenant aware. It isn't limiting all request based on anything. This architecture is advantageous in a number of ways.
First, in the above example, your product takes off and your supervisor wants to know what Categories are the most popular.
namespace StatisticsBusiness
{
public interface IStatisticsBO
{
IEnumerable<ICategoryStatisticBO> CategoryStatistics { get; set; }
}
public interface ICategoryStaticBO
{
Guid CategoryId { get; }
int ProjectCount { get; }
}
private class StatisticsBA : IStatisticsBO
{
private readonly IProjectDA _projectDA;
private readonly IIdentity _identity;
public ProjectLogic(IProjectDA projectDA,
IIdentity identity)
{
_projectDA = projectDA;
_identity = identity;
}
public IEnumerable<IProjectBO GetOrderedCategoryPopularity()
{
var dos = _projectDA
.GetProjectCategoryCounts()
var result = map.To<IEnumerable<IStatisticsBO>>(dos);
return result;
}
}
public interface IStatisticsBO{ .. }
private class StatisticsBO
{
public Guid CategoryId { get; }
public int ProjectCount { get; }
}
}
Note: Some people prefer to pass an expression as a predicate. Both have their advantages and disadvantages. If you decide to go the predicate route, then you'll have to decide if all your Data Access types use predicates or not. Just realize that using predicates against IO or Web Api might be more effort that it's worth.
Secondly, some requirement causes you not to be able to use Entity Framework. You replace it with Dapper or some other new better technology/framework. All you have to create new I<whataver>DA classes because the consuming logic is unaware of anything other than those interfaces (programming against an interface, the L in SOLID programming principles and the I in SOLID programming principles).
I don't use this pattern all the time because for some smaller websites, it's too much work for the payoff.

I will suggest to decompose the solution in tow parts
Add an organization id in your dbcontext, much like a tenant id in multi-tenant env. See this link for example.
Next challenge will be to pass the organization id as a parameter to DbContext constructor. For this you can create a factory for DbContext. Since you store the OrganizationId in claims. The factory can access the same claim HttpContext and pass the organization id as a parameter while instanting the dbContext.
It's not perfect but can give you a starting point.

Related

Using Queryable Extension to get back Foreign Key Data Value

I have my Unit of Measure which users fill in and save, they can then save a list of Unit Sizes which has its own table and is a foreign key to the Unit Of Measure. When I am fetching all the data back, the Unit Size value is coming back blank.
I have read a half dozen ways to do this and I am not comprehending them. The one that makes the most sense to me is using a Queryable extension so I am trying to go that route but my code still hasn't quite gotten there.
Here is where I am at - these are my entities:
namespace Mudman.Data.Entities
{
[Table("UnitOfMeasure")]
public class UnitOfMeasure : IEntityBase, IAuditBase
{
[Key]
[Column("UnitOfMeasureId")]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Id { get; set; }
[Required]
[ForeignKey("TenantId")]
public string TenantId { get; set; }
[JsonIgnore]
public virtual Tenant Tenant { get; set; }
public string Name { get; set; }
public virtual IEnumerable<UnitOfMeasureSize> UnitSize { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
[StringLength(255)]
public string CreateUserId { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public DateTime UpdateDate { get; set; }
[StringLength(255)]
public string UpdateUserId { get; set; }
}
}
Unit Of Measure size entity:
namespace Mudman.Data.Entities
{
[Table("UnitOfMeasureSize")]
public class UnitOfMeasureSize : IEntityBase, IAuditBase
{
[Key]
[Column("UnitOfMeasureSize")]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Id { get; set; }
[Required]
[ForeignKey("TenantId")]
public string TenantId { get; set; }
[JsonIgnore]
public virtual Tenant Tenant { get; set; }
[Required]
[ForeignKey("UnitOfMeasureId")]
public string UnitOfMeasureId { get; set; }
public virtual UnitOfMeasure UnitOfMeasure { get; set; }
[Required]
public int UnitSize { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public DateTime CreateDate { get; set; } = DateTime.UtcNow;
[StringLength(255)]
public string CreateUserId { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public DateTime UpdateDate { get; set; }
[StringLength(255)]
public string UpdateUserId { get; set; }
}
}
Unit Of Measure Repository including Unit Size:
namespace Mudman.Repository
{
public class UnitOfMeasureRepository : EntityBaseRepository<UnitOfMeasure>,
IUnitOfMeasureRepository
{
MudmanDbContext context;
public UnitOfMeasureRepository(MudmanDbContext context) : base(context)
{
{ this.context = context; };
}
public IEnumerable<UnitOfMeasure> GetAllUnitsOfMeasure(string TenantId)
{
var result = context.UnitOfMeasure
.Where( uom => uom.TenantId == TenantId)
.Include(uom => uom.UnitSize);
return result;
}
}
}
My GetAllAsync method in my service:
public Task<IEnumerable<UnitOfMeasureViewModel>> GetAllAsync()
{
var result = _unitOfMeasureRepo.GetAllUnitsOfMeasure(TenantId);
result.OrderBy(r => r.Name);
return _mapper.Map<List<UnitOfMeasure>, List<UnitOfMeasureViewModel>>(result.ToList());
}
AutoMapper Code:
CreateMap<UnitOfMeasure, UnitOfMeasureViewModel>().ReverseMap()
.ForMember(dest => dest.UnitSize, uos => uos.Ignore())
.ForMember(uom => uom.UnitSize, src => src.MapFrom(uom => uom.UnitSize));
There are a few issues with your attempts so far.
Firstly, your GetAllAsync looks like it wants to be an async method but you have it making entirely synchronous calls, and hasn't been marked as async. I would avoid diving into asynchronous methods until you have the fundamentals of retrieving your data down.
What we cannot see from your example is the mapping between your unit of measure entity and the view model. The entity has a one-to-many relationship between unit of measure and UnitSizes, so what gets updated depends on how the view model is laid out and configured for mapping. This is most likely the root of your problem where your view model mapping from the entity is likely relying on a convention that isn't pairing up with the data you expect.
Performance wise, this approach will run into problems as your data model grows in terms of entities and rows. The fundamental problem with using a repository like this is that a method like this:
IEnumerable<UnitOfMeasure> GetAllUnitsOfMeasure(string TenantId)
will load all data into memory and you explicitly need to include related entities, whether the consumer will want them or not, which adds to the amount of work the queries need to do and the memory required. If TenantId is for something like a multi-tenant database such as in a SaaS application with multiple tenants using a single data source, this is a good reason to adopt a Repository pattern, but I would not pass tenantIds around as parameters. Instead, have the repository accept a dependency that can validate and resolve the current TenantId from the session. This way the repository can always ensure that the current tenant rules are validated and applied for every query without worrying about where the caller might have gotten a TenantId from. (I.e accepting a TenantId from a POST request would be bad as that value could easily be tampered with)
To address performance and probably touch on what you had read about IQueryable extensions, rather than returning IEnumerable<TEntity> from a repository, you can return IQueryable<TEntity>. The advantages here are that you can still have the repository add base filtering rules like the tenantID, and allow the consumer to handle things like sorting and projection.
For example, the repository looks more like:
public class UnitOfMeasureRepository : IUnitOfMeasureRepository
{
private readonly MudmanDbContext _context;
private readonly ICurrentUserLocator _currentUserLocator;
public UnitOfMeasureRepository(MudmanDbContext context, ICurrentUserLocator currentUserLocator )
{
_context = context ?? throw new ArgumentNullException("context");
_currentUserLocator = currentUserLocator ?? throw new ArgumentNullException("currentUserLocator");
}
public IQueryable<UnitOfMeasure> GetUnitsOfMeasure()
{
var tenantId = _currentUserLocator.CurrentUserTenantId; // Checks session for current user and retrieves a tenant ID or throws an exception. (no session, etc.)
var query = _context.UnitOfMeasure
.Where( uom => uom.TenantId == tenantId)
return query;
}
}
The changes to note here is that we do away with the base generic repository class. This was confusing as you were passing the context to a base class then setting a local context instance as well. Generic repositories with EF are a bad code smell as they lead to either very complex code, very poor performing code, or both. There is a CurrentUserLocator with the container can inject which is a simple class that can verify that a user is currently authenticated and can return their Tenant ID. From there we will return an IQueryable<UnitOfMeasure> which has a base filter for the TenantID which will allow our consumers to make up their own minds how they want to consume it. Note that we do not need to use Include for related entities, again the consumers can decide what they need.
Calling the new repository method and projecting your view models looks fairly similar to what you had. It looks like you are using Automapper, rather than using .Map() we can use .ProjectTo() with the IQueryable and Automapper can essentially build a Select() expression to pull back only the data that the view model will need. To use ProjectTo extension method we do need to provide it with the MappingConfiguration that was used to create your mapper and that will tell it how to build the ViewModel. (So rather than having a dependency of type 'Mapper' you will need one for the MapperConfiguration you set up for that mapper.)
public IEnumerable<UnitOfMeasureViewModel> GetAll()
{
var models = _unitOfMeasureRepo.GetUnitsOfMeasure()
.OrderBy(r => r.Name)
.ProjectTo<UnitOfMeasureViewModel>(_mapperConfiguration)
.ToList();
}
What this does is call our repository method to get the IQueryable, which we can then append the ordering we desire, and call ProjectTo to allow Automapper to populate the view models before executing the query with ToList(). When using Select or ProjectTo we don't need to worry about using Include to eager load related data that might be mapped, these methods take care of loading data related entities if/when needed automatically.
Even in cases where we want to use a method like this to update entities with related entities, using IQueryable works there to:
public void IncrementUnitSize(string unitOfMeasureId)
{
var unitOfMeasure = _unitOfMeasureRepo.GetUnitsOfMeasure()
.Include(r => r.UnitSizes)
.Where(r => r.Id == unitOfMeasureId)
.Single();
foreach(var unitSize in unitOfMeasure.UnitSizes)
unitSize.UnitSize += 1;
_context.SaveChanges();
}
Just as an example of fetching related entities as needed, versus having a method that returns IEnumerable and needs to eager load everything just in case some caller might need it.
These methods can very easily be translated into an asyncronous method without touching the repository:
public async Task<IEnumerable<UnitOfMeasureViewModel>> GetAll()
{
var models = await _unitOfMeasureRepo.GetAllUnitsOfMeasure(TenantId)
.OrderBy(r => r.Name)
.ProjectTo<UnitOfMeasureViewModel>(_mapperConfiguration)
.ToListAsync();
}
... and that is all! Just remember that async doesn't make the call faster, if anything it makes it a touch slower. What it does is make the server more responsive by allowing it to move the request handling to a background thread and free the request thread to pick up a new server request. That is great for methods that are going to take a bit of time, or are going to get called very frequently to avoid tying down all of the server request threads leading to timeouts for users waiting for a response from the server. For methods that are very fast and aren't expected to get hammered by a lot of users, async doesn't add a lot of value and you need to ensure every async call is awaited or you can end up with whacky behaviour and exceptions.

How do I query identity data efficiently in ASP.Net Core?

Background
I have a website written in ASP.NET Core v2.1.1.
I have a custom identity user class:
public class FooIdentityUser : IdentityUser<string>, IIdentityModel
{
[MaxLength(50)]
public string FirstName { get; set; }
[MaxLength(50)]
public string LastName { get; set; }
public string FullName => $"{FirstName} {LastName}";
public bool FooBool { get; set; }
}
and a custom identity role class:
public class FooIdentityRole : IdentityRole<string>
{
}
Which I then reference in the dbcontext:
public class FooIdentityDbContext : IdentityDbContext<FooIdentityUser,FooIdentityRole,string>
{
public FooIdentityDbContext(DbContextOptions<FooIdentityDbContext> options)
: base(options)
{
}
}
Requirement
My overall requirement is that I want to give system admin users the ability to view and eventually manage user data from within the admin area of the website.
Specifically:
I want to provide a list of users that are in a foo role
And / or I want to list all users that have FooBool set to true
And / or I want to query on email address, first name & last name
And / or carry out a sort
Question
Does anyone have any links to web pages where this has been done before or can you respond on how I can implement this feature? I have attempted a couple of approaches below.
Approaches / Research
From what I can see there are two approaches to doing this:
Approach 1
Because I want to list users specifically for a user role based in a view, I can see that user manager provides a method for this:
_userManager.GetUsersInRoleAsync(fooRoleName)
The issue I have with this is it returns an IList so whilst it will return all users with this role, if I want to query on FooBool and / or FirstName, LastName or Email Address, it will need to cycle through the list to filter these out which would be inefficient if there are 10s of thousands or 100s of thousands of users?
Ideally, this would return an IQueryable so it wouldn't hit the database until my where and order by had been applied but I can't find a way of doing this?
Approach 2
The other way may be to query the context directly through my generic repository.
public class GenericIdentityRepository<TModel> : IIdentityRepository<TModel> where TModel : class, IIdentityModel
{
private readonly ILogger _logger;
public FooIdentityDbContext Context { get; set; }
private readonly DbSet<TModel> _dbSet;
public GenericIdentityRepository(FooIdentityDbContext dbContext, ILogger<GenericIdentityRepository<TModel>> logger)
{
Context = dbContext;
_logger = logger;
_dbSet = Context.Set<TModel>();
}
public IQueryable<TModel> GetAll()
{
_logger.LogDebug("GetAll " + typeof(TModel));
IQueryable<TModel> query = _dbSet;
return query;
}
public IQueryable<TModel> GetAllNoTracking()
{
_logger.LogDebug("GetAllNotTracking " + typeof(TModel));
IQueryable<TModel> query = GetAll().AsNoTracking();
return query;
}
}
I was looking to see if I could do something by creating custom classes for userrole and then using linq to give me an IQueryable?
public class FooIdentityUserRole : IdentityUserRole<string>
{
public virtual FooIdentityUser User { get; set; }
public virtual FooIdentityRole Role { get; set; }
}
And then somehow query the data to return an IQueryable but I'm struggling to produce the correct linq I need to do this.
My suggestion is to use the FooIdentityDbContext directly in your controllers and just query the data in the way you want. I don't know a way you could achieve what you want using the UserManager class. Maybe there is but honestly, I wouldn't mix things. UserManager is more useful when you are dealing with a single user and want to do things with it such as AddToRoleAsync or ChangePasswordAsync.
You have much more flexibility using the DbContextclass directly. You don't need some fancy generic repository. Keep it simple and concise unless you definitely need the abstraction (which almost always you don't)
Down to the actual answer: You've already configured the entities correctly, so now just inject the FooIdentityDbContext and start querying. Something like this:
public class HomeController : Controller
{
private readonly FooIdentityDbContext_dbContext;
public HomeController(FooIdentityDbContext dbContext)
{
_dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
}
public async Task<IActionResult> UserList(string roleName, bool fooBool, string firstName)
{
// You are interested in Users on Roles, so it's easier to start on the UserRoles table
var usersInRole = _dbContext.UserRoles.Select(userRole => userRole);
// filter only users on role first
if (!string.IsNullOrWhiteSpace(roleName))
{
usersInRole = usersInRole.Where(ur => ur.Role.Name == roleName);
}
// then filter by fooBool
usersInRole = usersInRole.Where(ur => ur.User.FooBool == fooBool);
// then filter by user firstname or whatever field you need
if (!string.IsNullOrWhiteSpace(firstName))
{
usersInRole = usersInRole.Where(ur => ur.User.FirstName.StartsWith(firstName));
}
// finally materialize the query, sorting by FirstName ascending
// It's a common good practice to not return your entities and select only what's necessary for the view.
var filteredUsers = await usersInRole.Select(ur => new UserListViewModel
{
Id = ur.UserId,
Email = ur.User.Email,
FooBool = ur.User.FooBool,
FirstName = ur.User.FirstName
}).OrderBy(u => u.FirstName).ToListAsync();
return View("UserListNew", filteredUsers);
}
}
Bonus: I've been reading the EF Core in Action book by Jon Smith and it's great. I highly recommend reading it if you want to keep using EF Core in your projects. It's full of nice tips and real world examples.
use .Users.
await _userManager.Users.Where(w => w.LastChangeUserId == null).ToListAsync();

asp.net mvc better way of getting relational data

I am building an asp.net mvc site that allows users (with the role of manager) to add/manage other users
To do this I've added a relational table called ManagerUsers to the database that contains a manager id and a user id. When a manager invites a user, the data is stored in the ManagerUsers table.
When a manger views the users I am doing the following:
using (var context = new ApplicationDbContext())
{
Guid myId = Guid.Parse(User.Identity.GetUserId());
var userIds = context.ManagersUsers.Where(u => u.ManagerId == myId).Select(u => u.UserId.ToString()).ToList();
var userProfiles = context.Users.Where(t => userIds.Contains(t.Id)).ToList();
return View(userProfiles);
}
This works ok but seems kind of slow and long-winded. Does anyone know a better way of doing it?
EDIT: based on some replies I think I need to clarify what I'm asking. What I want to know is whether there is a better way to get a list of users that are under my management than getting a list of users Ids from the ManagerUsers table and then finding them from all of the users in the Users table? Maybe there is a better way of storing this data to make it faster for retrieval?
This tutorial shows examples of defining relationships with Entity Framework and the virtual property:
https://www.asp.net/mvc/overview/getting-started/getting-started-with-ef-using-mvc/creating-an-entity-framework-data-model-for-an-asp-net-mvc-application
It would look something like this:
public virtual <ApplicationUser> User { get; set; }
This will actually create a table relating the two models. From here you should be able to get the Users using ManagerUser.Users or something to this effect. I would also follow mason's example and implement a Repository pattern.
You shouldn't tightly couple your data access code to your MVC layer. That makes it difficult to change data layers, and it makes it difficult to test MVC without hitting a real database. You're far better off creating a separate layer that allows them to be loosely coupled.
interface IMembershipService
{
List<UserProfile> GetUsersForManager(Guid managerId);
}
class SqlServerMembershipService : IMembershipService
{
private readonly string ConnectionString;
public SqlServerMembershipService(string connectionString)
{
//Any initialization of the repository goes here
ConnectionString = connectionString;
}
public List<UserProfile> GetUsersForManager(Guid managerId)
{
using (var context = new ApplicationDbContext(connectionString))
{
var userIds = context.ManagersUsers.Where(u => u.ManagerId == myId).Select(u => u.UserId.ToString()).ToList();
var userProfiles = context.Users.Where(t => userIds.Contains(t.Id)).ToList();
return View(userProfiles);
}
}
}
Your MVC controller looks like this:
class UsersController
{
private readonly IMembershipService MembershipService;
public UsersController(IMembershipService membershipService)
{
MembershipService = membershipService;
}
public ActionResult Index()
{
Guid myId = Guid.Parse(User.Identity.GetUserId());
var profiles = MembershipService.GetUsersForManager(myId);
return View(profiles);
}
}
See how UsersController now has no idea about SqlServerMembershipService? All it knows is that it's going to receive some class via its constructor that will handle retrieving data for it. How it gets that class it up to you. You could tightly couple it by saying IMembershipService MembershipService = new SqlServerMembershipService but it's better to use Dependency Injection to do that for you.
Just in case anyone cares, here is what I did in the end:
public class ApplicationManager : ApplicationUser
{
public virtual List<ApplicationUser> Users { get; set; }
public ApplicationManager()
{
Users = new List<ApplicationUser>();
}
}
public class ApplicationUser : IdentityUser
{
public virtual ApplicationManager Manager { get; set; }
...
}
This adds two fields to the AspNetUsers table - Manager_Id and Discriminator (which states whether the user is an ApplcationManager or ApplicationUser).

IdentityDbContext User DbSet Name

I've created a custom user inheriting from IdentityUser called Contacts, and my applications dbcontext inherits from IdentityDbContext like so:
public class Contact : IdentityUser<int, ContactLogin, ContactRole, ContactClaim>
{
public Contact()
{
}
}
public class dbcontext : IdentityDbContext<Contact, Role, int, ContactLogin, ContactRole, ContactClaim>
{
public dbcontext()
: base("dbcontext")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// IdentityDbContext base - must be called prior to changing identity configuration
base.OnModelCreating(modelBuilder);
// custom identity table names and primary key column Id names
modelBuilder.Entity<Contact>().ToTable("Contacts").Property(p => p.Id).HasColumnName("ContactId").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
modelBuilder.Entity<ContactRole>().ToTable("ContactRoles");
modelBuilder.Entity<ContactLogin>().ToTable("ContactLogins");
modelBuilder.Entity<ContactClaim>().ToTable("ContactClaims").Property(p => p.Id).HasColumnName("ContactClaimId");
modelBuilder.Entity<Role>().ToTable("Roles").Property(p => p.Id).HasColumnName("RoleId").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
By default IdentityDbContext contains a Users DbSet. Is it possible to change the name of this DbSet to match the type that it's implementing, e.g Contacts?
It's not a big deal, but it would just be nice to refer to the DbSet using dbcontext.Contacts instead of dbcontext.Users.
Thanks.
The base IdentityDbContext uses: public virtual IDbSet<TUser> Users { get; set; } to expose the Users DbSet.
You'll need a similar property for your own implementation, e.g: public IDbSet<Contacts> Contacts { get; set; }
Update
Question was regarding renaming the existing DbSet of Contacts from Users to Contacts.
No, you can't do this out of the box. You could attempt to wrap it and expose it again, but this isn't really the right thing to do. See this question for an in depth discussion.
Just a note that if you decide to overwrite anything or add your own, the default EF implementation of UserStore will use the DbSet named Users. Just something to keep an eye on if you get unexpected behavior.
Generally what I tend to do is have a big separation of concerns right.
So I have:
public IDbSet<User> Users { get; set; }
This represents anyone who wants to log into my system. So now I want to model actual concepts into my database, concepts that relate to real world things. So I have a system administrator for example, I will create an entity for this.
public class SystemAdministrator
{
public string Name { get; set; }
public int LocationId { get; set; } // a complex representation of where this administrator works from
public int UserId { get; set; } // this is now a reference to their log in
}
Now my context will look like this:
public IDbSet<User> Users { get; set; }
public DbSet<SystemAdministrator> SystemAdministrators { get; set; } // I use DbSet because it exposes more methods to use like AddRange.
This means now my database has proper representations of real world concepts which is easy for everyone to develop against. I do the same for Clients or Employees.
This also means that I can move away from primitive obsession

How to link MVC application with membership provider and data model in different class libraries together?

I have 3 class libraries and ASP.MVC 4 web application.
Library - Base data model (users, roles, etc...)
public class User
{
public int ID { get; set; }
public string Comments { get; set; }....
}
Library - Extended data model (Partners, Products) - this can vary with the different web app.
Library - Membership provider.
I create my DBContext in the MVC app, as only there I know which DBSet I need for current app.
public class DSContext:DbContext
{
public DbSet<User> Users { get; set; }
}
I can access and use models from both data libraries in MVC app (and it works), but how to tell membership provider to use the same DBContext that I use in MVC app something like this?
var pUser = SomeDBContext.Users.Find(Username);
return new MembershipUser(...Set properties from pUser...);
SomeDBContext can be any DbContext that has property Users with type User from first class library.
After some trial and error got the solution to this
Feel free to comment - as I do not know how correct it is.
Class library
public class User
{
[Required]
[Key]
public string UserName{get; set;}
public string PassWord{ get; set;}
}
public class UserContext : DbContext
{
public DbSet<User> Users { get; set; }
}
Provider - the key was to make it abstract
public abstract class STMembershipProvider : ExtendedMembershipProvider
and to add abstract property
public abstract CL.UserContext DB { get; }
public override bool ValidateUser(string username, string password)
{
User dbuser = DB.Users.Find(username);
if (dbuser != null)
{
if (dbuser.PassWord == password) return true;
}
return false;
}
In MVC application create local DbContext what inherits from class library DbContext and local provider that inherits from MembershipProvider in the library.
public class EFContext:CL.UserContext
{
}
and
public class LocalMp:MP.STMembershipProvider
{
public override CL.UserContext DB
{
get
{
return new EF.Models.EFContext();
}
}
}
and that did the magic :)
To use general library for user rights and access you have to design an abstract layer which will be used by your membership provider. Since DBContext is directly connected to specific business domain and you want to use a general library for access and authorization you need to map specific objects of your project to that abstract layer. I can imagine that such abstract layer should allow to operate on entities (or tabels) in a following way:
Get a specific instance of business object
Map it (or cast it) to abstract layer security object A
Run your membership provider to do security work on object A
Use output to decide any action (allow access, restrict access)...
Hope this will help.

Categories