Is there any advantage in using UserManager instead of a small 5 table identity context when getting a list of users? - asp.net

I would like to get a list of users in my application which uses the latest ASP.NET Identity so I created this controller which uses an Instance of UserManager and queries that with its own built in method (ASP.NET Identity 2)
namespace WebRole1.WebAPI
{
public class UserProfileController : ApiController
{
public UserProfileController()
{
}
public UserProfileController(ApplicationUserManager userManager)
{
UserManager = userManager;
}
private ApplicationUserManager _userManager;
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? Request.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
[HttpGet]
[ActionName("GetMapData")]
public HttpResponseMessage GetMapData()
{
var users = UserManager.Users
.Select(u => new { Id = u.Id, Name = u.UserName })
.ToList();
return Request.CreateResponse(HttpStatusCode.OK, users);
}
Is this any more efficient than if I was to create an Identity context and then use Entity Framework to go issue the same SQL against the database. For me it seems like it would be easier to do the latter as I would be more in control and the code would look simpler.

Its not any more efficient, the basic premise is that if you ever wanted to replace the EF implementation with something else (a no sql backend for example), you wouldn't have to change much of your application code compared to directly using EF apis hanging off of the Identity context. That is typically the tradeoff that should be considered.

Related

How to get Identity User outside Razor Pages in Blazor Server-side?

I am working on a Blazor Server-Side application, using Microsoft Identity, Entity Framework and a multitenant approach with shared Db.
I have extended the IdentityUser class so that I could have the TenantId in the AspNetUser Table
public class ApplicationUser : IdentityUser
{
public int TenantId { get; set; }
}
}
Then I have applied a general query filter to my dbModel based on the TenantId
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Employee>().HasQueryFilter(a => a.TenantId == TenantId);
}
In my blazor page I can call this function
public async Task SetTenant()
{
var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
var user = authState.User;
ApplicationUser = await UserManager.FindByNameAsync(user.Identity.Name);
var TenatId = ApplicationUser.TenantId;
}
Finally in my service I can get a list of Employees with the right TenantId
public Task<Employee[]> GetEmployees(int TenatntID)
{
using (var ctx = new ProgramDbContext(TenantId))
{
return Task.FromResult(ctx.Employee.Select(d => new Employee
{
Id = d.Id,
TenantId = d.TenantId,
Name= d.Name,
}).ToArray());
}
}
With this approach, everytime I want to call a function to get DB's Data, I need to identity the user and get the TenantId, then call the specific function and pass the tenantID to it.
I would like to know if my approach is completely wrong to implement this type of solution, for example:
Is it possible to add a Singleton service of an ApplicationUser, so that once is is identified after login, i can inject the service in every class where i need the ApplicationUser.TenantId?
Is it possible to identify and authenticate the Application User outside a blazor class? for example a plain C# class? I was able to pass the AuthenticationStateProvider and UserManager in the constructor of my Service class, but I cant await a function inside the constructor to actually get the ApplicationUser object.
public CaronteWebService(AuthenticationStateProvider authenticationStateProvider, UserManager userManager)
{
_AuthenticationStateProvider = authenticationStateProvider;
_userManager = userManager;
}
UserManager<ApplicationUser> _userManager;
public ApplicationUser ApplicationUser { get; set; }
AuthenticationStateProvider _AuthenticationStateProvider { get; set; }

ASP.NET MVC 5 + EF6 + Ninject - Multitenancy Database

I have a business ASP.NET MVC5 application where each customer has his own database. I want to use EF6 and Ninject for DI. For login I'm using ASP.NET Identity.
For each user exists a UserClaim where the name of the database is specified:
UserId = 1 | ClaimType = "db_name" | ClaimValue = "Customer0001"
UserId = 2 | ClaimType = "db_name" | ClaimValue = "Customer0002"
and so on... This means it is one web-application with a "shared" database for user authentication and on the other side each customers has his own database - all databases are located on the same database server (MS SQL Server).
The user need to login in, after login he should receive data from his personal database (specified in the UserClaim-Table).
For Ninject I think I have to something like this
private void AddBindings() {
kernel.Bind<EFDBContext>().ToMethod(c => new EFDBContext("db_name"));
}
But how would I get the UserClaim into the bindings? (I don't want to use a Session, because sessions can get lost).
And what steps after the bindings are necessary?
For example at the AccountRepository the EFDBContext expects the "db_name" > but how would I get it there?
public class AccountRepository : IAccountRepository {
private EFDBContext context = new EFDBContext("db_name");
}
And finally I can change the connection string inside of this class??
public class EFDBContext : DbContext {
public EFDBContext(string db_name) : base("EFDBContext") {
}
}
UPDATE AFTER #Hooman Bahreini ANSWER
NinjectDependencieResolver.cs
private void AddBindings() {
kernel.Bind<ICustomerRepository>().To<CustomerRepository>().WithConstructorArgument("http_current_context", HttpContext.Current);
}
CustomerRepository.cs
public class CustomerRepository : ICustomerRepository {
private CustomerDBContext context;
public CustomerRepository(HttpContext httpContext) {
string db_name = "";
var claimValue = ((ClaimsPrincipal)HttpContext.Current.User).Claims.FirstOrDefault(c => c.Type == "db_name");
if(claimValue != null) {
db_name = claimValue.Value.ToString();
}
context = new CustomerDBContext(db_name);
}
public IEnumerable<Test> Tests {
get { return context.Test; }
}
}
DB-Context-File
public class CustomerDBContext : DbContext {
public CustomerDBContext(string db_name) : base("CustomerDBContext") {
string temp_connection = Database.Connection.ConnectionString.Replace(";Initial Catalog=;", ";Initial Catalog=" + db_name + ";");
Database.Connection.ConnectionString = temp_connection;
}
public DbSet<Test> Test { get; set; }
}
You can access user claims from HttpContext:
var claimValue = ((ClaimsPrincipal)HttpContext.Current.User)
.Claims
.FirstOrDefault(c => c.Type == "db_name");
For your ninject code, you can create an extension method for HttpContext:
public static HttpcontextExtensions
{
public static string GetDbName(this HttpContext context)
{
return ((ClaimsPrincipal)context.Current.User)
.Claims
.FirstOrDefault(c => c.Type == "db_name");
}
}
And use the following ninject binding:
kernel.Bind<ICustomerRepository>()
.To<CustomerRepository>()
.WithConstructorArgument("db_name", HttpContext.GetDbName());
See this document for more info about accessing HttpContext in ninject.
In your example, CustomerRepository has a dependency on HttpContext, this is not a good design. CustomerRepository requires a db-name, and that's what should be passed in the constructor. Related to this is Nikola’s 4th law of IoC
Every constructor of a class being resolved should not have any
implementation other than accepting a set of its own dependencies.
To give you an example, you don't have any HttpContext in your test project, which makes unit testing CustomerRepository complicated.
P.S. I don't know your design, but maybe getting db-name from HttpContext is not an ideal solution... user may logout or clear their browser history and you will loose your db-name.

Asp.net Identity DbContext / Repository Issue

I am using Asp.Net identity within my MVC app. I can see that this has it's own ApplicationDbContext - albeit it is connected to the same SQL db as my own DbContext I am using elsewhere.
So I am trying to access some of my own data via my own code within the AccountController - it does not seem to work I presume because of some confusion over which DBContext it thinks is active?
My Code :
public class AccountController : Controller
{
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
private PostageManager postmgr;
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, PostageManager _postmgr)
{
UserManager = userManager;
SignInManager = signInManager;
postmgr = _postmgr;
}
public ApplicationSignInManager SignInManager
{
get
{
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set
{
_signInManager = value;
}
}
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
// GET: /Account/Register
[AllowAnonymous]
public ActionResult Register()
{
//create select list items for countries drop down
List<SelectListItem> countries;
countries = postmgr.GetCountries().Select(item => new SelectListItem
{
Value = item.Country,
Text = item.Country
}).ToList();
countries.Insert(0, new SelectListItem { Value = string.Empty, Text = "Select delivery country or region...", Selected = true });
RegisterViewModel mode = new RegisterViewModel
{
Countries = countries
};
return View();
}
}
}
PostageManager is just a class that sits over my DAL to fetch some data (which uses repository pattern) - I'm using just a kind of pass through method to grab a list of countries, and using it in exactly the same way I have in other controllers which works fine. Underneath that class is my repository code that is linked to my default connection string (DBContext). It's balking at the following line with a null reference exception, I think postmgr is null :
countries = postmgr.GetCountries().Select(item => new SelectListItem
In reverse to get access to the identity data within my own controllers I have done the following :
public BasketController(BasketManager _mgr, PostageManager _postmgr, ProductManager _prodmgr)
{
mgr = _mgr;
postmgr = _postmgr;
prodmgr = _prodmgr;
shopper = Cart.GetShopperId();
this.applicationDbContext = new ApplicationDbContext();
this.userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(this.applicationDbContext));
}
protected ApplicationDbContext applicationDbContext { get; set; }
protected UserManager<ApplicationUser> userManager { get; set; }
Which as far as I understand it points the identity code to use the right DbContext - I looked at doing this in reverse in my AccountController but can't fathom it out.
I basically just want to be able to use my own code that grabs my own data from within the Identity controllers to help pass extra data etc through to the views.
I might be wrong but most probably postmgr field is not initialized from constructor and that is why you have this error.
Explanation:
By default Asp will try to create controller instance by constructor without parameters. If Asp can't find constructor without parameters it will try to call constructor with parameters, but to make it possible you have to configure IoC in your app. As your controler has constructor without parameters it will be selected by Asp. So all 3 fields are empty.
But in properties SignInManager and UserManager you try to take value from field or from OwinContext. As field is empty your code will take value from OwinContext. OwinContext is quite complex and smart tool that create its context automatically based on configuration provided in Startup.Auth.cs file or any other file under App_Start folder.
I think I have figured it out - added the following to my NinjectControllerFactory :
ninjectKernel.Bind<IAuthenticationManager>().ToMethod(c => HttpContext.Current.GetOwinContext().Authentication); //.InRequestScope();
ninjectKernel.Bind<IUserStore<ApplicationUser>>().To<UserStore<ApplicationUser>>();
ninjectKernel.Bind<UserManager<ApplicationUser>>().ToSelf();
ninjectKernel.Bind<IRoleStore<IdentityRole, string>>().To<RoleStore<IdentityRole, string, IdentityUserRole>>();
ninjectKernel.Bind<RoleManager<IdentityRole>>().ToSelf();
And changed my constructor to :
public AccountController(PostageManager _postmgr)
{
postmgr = _postmgr;
}

ASP.NET Identity 2.0 UserManager.FindByIdAsyc not returning Roles

I am building a website using ASP.NET MVC v5.2, Entity Framework v6.0 (Code First) and Identity 2.0.
I have the following code in my UserAdmin controller:
// GET: /UserAdmin/Details/5
public async Task<ActionResult> Details(string id)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var user = await UserManager.FindByIdAsync(id);
return View(user);
}
My problem is that while I can populate a user's roles with the UserManager, it is not picking up the roles associated with that user when using the FindByIdAsync method.
Here is data from the IdentityUserRole table which shows for the highlighted user assigned to two roles:
Here is the debug info showing the same user as above but the Roles count is zero:
Why are the roles for this user not being returned?
Edit #1
I am not using the default implementations for UserManager.
My ApplicationUser extends IdentityUser to allow me to add custom properties. My ApplicationDbContext extends IdentityDbContext.
Here's where I set up my primary keys for Identity using Fluent API:
modelBuilder.Entity<IdentityUserLogin>().HasKey<string>(l => l.UserId);
modelBuilder.Entity<IdentityRole>().HasKey<string>(r => r.Id);
modelBuilder.Entity<IdentityUserRole>().HasKey(r => new { r.RoleId, r.UserId });
As you are extending the IdentityDbContext and IdentityUser, you don't need to define your relationships for Logins, Roles and UserRoles as they are already defined in the base classes. You need to remove the lines like modelBuilder.Entity<IdentityUserRole>().HasKey(r => new { r.RoleId, r.UserId }); as they are taken care of.
As for not using the default UserManager There is a possibility here I see based on the code you have provided.
The default implementation for UserManager takes an IUserStore<TUser> as a parameter in it's constructor. If you are using (or deriving from) the UserStore<TUser> implementation in the Microsoft.AspNet.Identity.EntityFramework Library, the things like Roles, Claims and Logins are being included in the queries being made to the database. If you are creating your own class implementing IUserStore<TUser> then you will need to include the related data yourself. an example is shown below.
public class ApplicationUser : IdentityUser
{
//Add your Custom Properties here..
}
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
//Add your Custom Properties here..
}
public class ApplicationUserStore : IUserStore<ApplicationUser>
{
private readonly ApplicationDbContext _context;
public ApplicationUserStore(ApplicationDbContext context)
{
_context = context;
}
public async Task<ApplicationUser> FindByIdAsync(string userName)
{
return await _context.Users.Include(x => x.Roles).FirstOrDefaultAsync(n => n.UserName == userName);
}
}

Entity framework, consuming context in webform app

Rather simple question regarding entity framework and how to consume the objectcontext.
In a asp.net webform application I am implementing most of data getters in a data class and trying to determine if its better (in a generic sense of better) to have a private context for the entire class or declare a context in each of the methods.
Example 1:
public class Data
{
private MyEntity context = new MyEntity();
public Customer GetCustomer()
{
return context.Customer.Single();
}
public Order GetOrder()
{
return context.Order.Single();
}
}
Or Example 2:
public class Data
{
public Customer GetCustomer()
{
using (MyEntity ctx = new MyEntity())
{
return context.Customer.Single();
}
}
public Order GetOrder()
{
using (MyEntity ctx = new MyEntity())
{
return context.Order.Single();
}
}
}
Personally im a big fan of using a shared context across your whole post back, however neither of these scenarios really achieve this. My personal preference is to use a dependency injection container such as ninject to manage the lifecycle of your EF context. This means that you can make your whole postback transactional.
in terms of implementation I would go for soemthing like the following:
public class Data
{
private MyContext _context;
public Data(MyContext context)
{
_context = context;
}
public Customer GetCustomer()
{
return _context.Customer.Single();
}
public Order GetOrder()
{
return _context.Order.Single();
}
}
with a binding similar to:
Bind<MyContext>().ToSelf().InRequestScope();

Resources