I'm working on multitenant application (ASP.NET 5 + EF7). Each tenant will have separate database. I will have one separate database for tenant account data. I have registered service for EF in startup class for this separate database. I have problem with migrations. I cant create EF migration, until tenantDbContext is registered as service with specific connection string. But this conection string must be dynamic for each tenant... Any idea please? What is the best option to manage DbContexts for tenants?
Future edit - protected override void OnConfiguring was the key how to do: Is this good solution please?
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration["Data:DefaultConnection:ConnectionString"]));
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<TenantDbContext>();
public class TenantDbContext : IdentityDbContext<ApplicationUser>
{
public TenantDbContext() //development database with no connectionString in constructor
{
this._connectionString = "Connection String";
}
public TenantDbContext(string ConnectionString)
{
this._connectionString = ConnectionString;
}
private string _connectionString { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(_connectionString);
}
...etc
As I mentioned in comments I have not tried multi-tenant/multi-db myself but try the following:
You can use DbContext CreateIfNotExists() method. https://msdn.microsoft.com/en-us/library/system.data.entity.database.createifnotexists(v=vs.113).aspx
If you have a Migrations/Configuration.cs you can set AutomaticMigrationsEnabled property to false
Setting the initializer off is probably needed as well: Database.SetInitializer<DatabaseContext>(null);
Sorry without knowing more details like workflow of creating a new tenant (automatic from DB or is a screen filled out with the connection string and name etc.) I can't make more detailed suggestions. I would suggest that your data layer be quite abstracted from the context. It seems like a bad idea for developers to have to select the correct context. Hence the use of a factory.
An option is always requiring a tenant id to be passed into all service or repository methods. I'm guessing this would be in some kind of user claim available in the controller.
Related
I want to create an owin authentication using my own external sql database instead of the base asp context. I can create an edmx model from the database but i dont know how to use it to identify the users.
When i change the base Database connection used by ApplicationUser to my own one i get this exception.
I have already created at least 30 projects to test the solutions i found on google but none of them was worked for me.
[SOLUTION]
The solution is much more easier than i thougth..
All i had to do was inheriting my User table from IdentityUser (AspNetUser is my own table)
public partial class AspNetUser : IdentityUser
and using my custom connectionstring's name (BCTSDb) instead of DefaultConnection in IdentityModels.cs.
public ApplicationDbContext()
: base("BCTSDb", throwIfV1Schema: false)
This is codefirst from database, database first is not working with this solution (i dont know why).
This solved the FIRST problem. After this i couldn't scaffold any controllers with views because of missing keys and ambiguous references.
[WHATTODO]
1. Ambiguous references
In my AspNetUser.cs class i made all ORIGINAL attributes override:
for example UserName:
public override string UserName { get; set; }
Its possible that this is not necessary, i haven't tried without this, maybe this solve the ambiguous references between my dbcontext and ApplicationDbContext.
2. Missing keys
I dont know where i left my keys but there is the solution:
At the end of my OnModelCreating function (in my dbcontext) i wrote the following lines:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// your table operations, references
// ...
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 });
base.OnModelCreating(modelBuilder);
}
After this i could do anything i want.
I'm looking into asp.net core and the new security policies and claims functionality. Having just looked at it I don't see how it is much better than the existing authorize attribute logic in the past where hard-coded roles or users are decorated on controllers, methods etc. To me the issues has just been moved from hard-coding in attributes to hard-coding policies.
Ideally I would like to perform activity/resource based authorization where everything would be database driven. Each activity or resource would be stored in the database and a permission/role would be assigned to the resource.
While researching the topic I found this fantastic article by Stefan Wloch that pretty much covers exactly what I'm looking to do.
http://www.codeproject.com/Articles/1079552/Custom-Roles-Based-Access-Control-RBAC-in-ASP-NE
So my question is with the new core features how does it prevent us from having to hard-code and recompile when the time comes to change what roles/permissions are allowed to access a controller or method in a controller? I understand how claims can be used to store anything but the policy portion seems susceptible to change, which gets us back to square one. Don't get me wrong, loving asp.net core and all the great changes, just looking for more information on how to handle authorization.
There are at least 2 things that need to be consider in implementing what you want. The first one is how to model the Controller-Action access in database, the second one is to apply that setting in asp.net core Identity.
The first one, there are too many possibilities depend on the application itself, so lets create a Service interface named IActivityAccessService that encapsulate. We use that service via dependency injection so that anything that we need can be injected to it.
As for the second one, it can be achieved by customize AuthorizationHandler in a policy-based authorization. The first step is to setup things in Startup.ConfigureServices :
services.AddAuthorization(options =>
{
options.AddPolicy("ActivityAccess", policy => policy.Requirements.Add( new ActivityAccessRequirement() ));
});
services.AddScoped<IAuthorizationHandler, ActivityAccessHandler>();
//inject the service also
services.AddScoped<IActivityAccessService, ActivityAccessService>();
//code below will be explained later
services.AddHttpContextAccessor();
next we create the ActivityAccessHandler:
public class ActivityAccessHandler : AuthorizationHandler<ActivityAccessRequirement>
{
readonly IActivityAccessService _ActivityAccessService;
public ActivityAccessHandler (IActivityAccessService r)
{
_ActivityAccessService = r;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext authHandlerContext, ActivityAccessRequirement requirement)
{
if (context.Resource is AuthorizationFilterContext filterContext)
{
var area = (filterContext.RouteData.Values["area"] as string)?.ToLower();
var controller = (filterContext.RouteData.Values["controller"] as string)?.ToLower();
var action = (filterContext.RouteData.Values["action"] as string)?.ToLower();
var id = (filterContext.RouteData.Values["id"] as string)?.ToLower();
if (_ActivityAccessService.IsAuthorize(area, controller, action, id))
{
context.Succeed(requirement);
}
}
}
}
public class ActivityAccessRequirement : IAuthorizationRequirement
{
//since we handle the authorization in our service, we can leave this empty
}
Since we can use dependency injection in AuthorizationHandler, it is here that we inject the IActivityAccessService.
Now that we have access to what resource is being requested, we need to know who is requesting it. This can be done by injecting IHttpContextAccessor. Thus services.AddHttpContextAccessor() is added in code above, it is for this reason.
And for the IActivityAccessService, you could do something like:
public class ActivityAccessService : IActivityAccessService
{
readonly AppDbContext _context;
readonly IConfiguration _config;
readonly IHttpContextAccessor _accessor;
readonly UserManager<AppUser> _userManager;
public class ActivityAccessService(AppDbContext d, IConfiguration c, IHttpContextAccessor a, UserManager<AppUser> u)
{
_context = d;
_config = c;
_accessor = a;
_userManager = u;
}
public bool IsAuthorize(string area, string controller, string action, string id)
{
//get the user object from the ClaimPrincipals
var appUser = await _userManager.GetUserAsync(_accessor.HttpContext.User);
//get user roles if necessary
var userRoles = await _userManager.GetRolesAsync(appUser);
// all of needed data are available now, do the logic of authorization
return result;
}
}
Please note that the code in IsAuthorize body above is an example. While it will works, people might say it's not a good practice. But since IActivityAccessService is just a common simple service class, we can inject anything that wee need to it and modify the IsAuthorize method signature in any way that we want to. For example, we can just pass the filterContext.RouteData instead.
As for how to apply this to a controller or action:
[Authorize(Policy = "ActivityAccess")]
public ActionResult<IActionResult> GetResource(int resourceId)
{
return Resource;
}
hope this helps
What are the best practices for connecting to a Azure Table Storage from a ASP.NET MVC or Web API app?
Right now I've made a StorageContext class which holds a reference to the CloudStorageAccount and CloudTableClient, like this:
public class StorageContext
{
private static CloudStorageAccount _storageAccount;
private static CloudTableClient _tableClient;
public StorageContext() : this("StorageConnectionString") { }
public StorageContext(string connectionString)
{
if (_storageAccount == null)
_storageAccount = CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings[connectionString].ConnectionString);
if (_tableClient == null)
_tableClient = _storageAccount.CreateCloudTableClient();
}
public CloudTable Table(string tableName)
{
var table = _tableClient.GetTableReference(tableName);
table.CreateIfNotExists();
return table;
}
}
And my controller I'm using it like this:
public class HomeController : ApiController
{
private StorageContext db;
public HomeController() : this(new StorageContext()) { }
public HomeController(StorageContext context)
{
this.db = context;
}
public IHttpActionResult Get()
{
var table = db.Table("users");
var results = (from user in table.CreateQuery<User>()
select user).Take(10).ToList();
return Ok<List<User>>(results);
}
}
Is this the preferred way of doing it?
The API is going to be used on a high traffic site with > 1000 req/sec.
I also need unit tests. Using it like above it I can pass in another connString name and instead connect to the Azure Storage emulator in my unit tests.
Am I on the right track or are there better ways to connect?
Actually your question
What are the best practices for connecting to a Azure Table Storage
from a ASP.NET MVC or Web API app?
could be restated like "What are the best practices to use data access layer in web application". It is the same.
You can find a lot of answers about data access layer best practices. But iron rule here keep your data access layer separated from your controller or presentation. The best way to use it through Model in scope of MVC pattern, or you can think about Repository and/or Unit of work pattern if you like them.
In your example your data access logic is already wrapped in StorageContext, which is fine, I would additionally extract interface and use DI/IoC and dependency resolver for it. That's all when speaking about your code snippet. You are on right way.
I'm having problems with custom membership within MVC 4 I keep getting a context lifetime related error when I do a ajax call to get a partial result from the server(controller), the error is always {"The provider has been closed"} or {"There is already an open DataReader associated with this Command which must be closed first."} the error always lands within the custom RoleProvider.
I will try to explain the current setup im using.
I have inherited from the Membership and RoleProvier and overridden all the methods like so
public class CustomRoleProvider : RoleProvider
{
private IAccountService _accountService;
public CustomRoleProvider()
{
_accountService = new AccountService();
}
public override string[] GetRolesForUser(string username)
{
return _accountService.GetRolesForUser(username);
}
}
The Membership provider is implemented in the same way the IAccountService above is the service layer that deals with all user accounts & roles all the service layer classes implement a base service class called ServiceBase that creates the DB context
public class ServiceBase
{
protected Context Context;
protected ServiceBase() : this("Context") {}
protected ServiceBase(string dbName)
{
IDatabaseInitializer<Context> initializer = new DbInitialiser();
Database.SetInitializer(initializer);
Context = new Context(dbName);
}
}
The Controller that has the ajax to made to it
[Authorize(Roles = "Administrator,Supplier")]
public class AuctionController : Controller
{
private IAuctionService _service;
public AuctionController()
{
_service = new AuctionService();
}
public AuctionController(IAuctionService service)
{
_service = service;
}
[CacheControl(HttpCacheability.NoCache), HttpGet]
public ActionResult RefreshAuctionTimes(int auctionId)
{
return PartialView("_AuctionTimer", BusinessLogic.Map.ConvertAuction(_service.GetAuction (auctionId)));
}
}
The problem only started when I added the [Authorize(Roles = "Administrator,Supplier")] attribute to the controller that handled the ajax call, I know this is the lifetime of the DbContext being for the life of the app and the controllers service layer being destroyed and recreated on every post but I'm not sure of the best way to handle this, I have used this setup before but with DI and Windsor and never had this problem as the IOC was controlling the context.
Would it be best to create the providers its own DB context or is the conflict between the 2 providers and really they need to share the same db context?
Any help would be great thanks
The problem is exactly what you're suspecting. Is due to the fact that you're creating a single instance of the DbContext and therefore you're having connection issues. If you use it with an IOC/DI schema, you're going to fix it. The other option is to manually handle the connections.
An example of how to do this using Ninject as IOC container is here
They need to share the same context in order for the problem to stop.
I would suggest you create your service layer class on each call to GetRolesForUser:
public override string[] GetRolesForUser(string username)
{
return new AccountService().GetRolesForUser(username);
}
HI,
I am implementing a custom role provider in my nhibernate application
I have a repository that I call whenever I want to access the nhibernate session.
So when my role provider initializes itself
public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config) {
base.Initialize(name, config);
Repository = new Repository();
}
Then I override
public override string[] GetRolesForUser(string username) {
var users = Repository.QueryAll<Users>();
//I then filter and so on
}
But when this function is called I always get an error that the NHibernate session is closed.
I debugged the nhibernate source code and it turns out that the session here has a different guid that the session in my controllers(I am using ASP.NET MVC also).
And this particular session is closed by the time I get here.
I don't know who closes it. I know it is started when the application starts and only then.
Does anyone have any idea what I am doing wrong?
I want to still use Nhibernate in this provider but not get the error any more.
Thank you
I had what appears to be the exact same problem. Don't forget that Role and Membership providers are only instantiated once and exist for the lifetime of the application. If you're using a Session per Web request pattern, the ISession will be closed after the first request and then any reference to an ISession internal to the provider will likely be null for subsequent requests.
You can get around this by injecting a reference to the ISessionFactory and calling GetCurrentSession, instead of directly holding a reference to an ISession.
This is how I evetually fixed it.
in my repository class I had this:
public Repository()
{
this.Session = SessionManager.GetCurrentSession();
}
I deleted the constructor entirely
I put in this instead:
private ISession _session;
protected ISession Session
{
get
{
if (_session == null)
{
_session = SessionManager.GetCurrentSession();
}
return _session;
}
}