We are using forms authentication on our ASP.NET website but are wanting to upgrade to the new Identity Provider. Currently we are using the database first approach and are ultimately wanting to just extend our current User table (not aspnet_users) to use the new identity format. We are using StructureMap to inject our context into our business logic classes. For instance our User service currently has this as its constructor:
private readonly SiteModelContainer _context;
public UserService(SiteModelContainer context)
{
this._context = context;
}
And in our IoC registry we have this:
var ecsbuilder = new EntityConnectionStringBuilder();
ecsbuilder.Provider = "System.Data.SqlClient";
ecsbuilder.ProviderConnectionString = #"data source=***;initial catalog=***;persist security info=True;User ID=***;Password=***;multipleactiveresultsets=True;App=EntityFramework";
ecsbuilder.Metadata = #"res://*/Data.***.csdl|res://*/Data.***.ssdl|res://*/Data.***.msl";
string connectionString = ecsbuilder.ToString();
For<SiteModelContainer>().Use<SiteModelContainer>().Ctor<string>("connectionString").Is(connectionString);
For<IUserService>().Use<UserService>();
...all the rest of our services
We are also using database first with EDMX and entity framework. Previously we just used ASP.NET authentication as it came out the box and had a separate user table to store profile information, but would like to have everything working off one users class instead.
1)Is it possible to extend our userservice to handle everything related to using Identity? So that Identity uses the same context that we inject into our classes? If so, I am unable to find any articles about it?
2) Are we able to extend our User object if it is created in the EDMX file?
Thanks
I have migrated 2 fairly large projects from MembershipProvider into Asp.Net Identity and both of the times I ended up rewriting most parts of the user-management and everything that touched user. A fairly chunky rewrites.
What you ask for is possible, but hard and very time consuming. You may start from this question - the OP have got his db-first project running with identity. And we had a discussion in comments with some links that might help you.
Related
I'm currently working on a C# UWP application that runs on Windows 10 IoT Core OS on an ARM processor. For this application, I am using a SQLite DB for my persistence, with Entity Framework Core as my ORM.
I have created my own DBContext and call the Migrate function on startup which creates my DB. I can also successfully create a DBContext instance in my main logic which can successfully read/write data using the model. All good so far.
However, I've noticed that the performance of creating a DbContext for each interaction with the DB is painfully slow. Although I can guarantee that only my application is accessing the database (I'm running on custom hardware with a controlled software environment), I do have multiple threads in my application that need to access the database via the DbContext.
I need to find a way to optimize the connection to my SQLite DB in a way that is thread safe in my application. As I mentioned before, I don't have to worry about any external applications.
At first, I tried to create a SqliteConnection object externally and then pass it in to each DbContext that I create:
_connection = new SqliteConnection(#"Data Source=main.db");
... and then make that available to my DbContext and use in in the OnConfiguring override:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite(_connection);
}
... and then use the DbContext in my application like this:
using (var db = new MyDbContext())
{
var data = new MyData { Timestamp = DateTime.UtcNow, Data = "123" };
db.MyData.Add(data);
db.SaveChanges();
}
// Example data read
MyDataListView.ItemsSource = db.MyData.ToList();
Taking the above approach, I noticed that the connection is closed down automatically when the DbContext is disposed, regardless of the fact that the connection was created externally. So this ends up throwing an exception the second time I create a DbContext with the connection.
Secondly, I tried to create a single DbContext once statically and share it across my entire application. So instead of creating the DbContext in a using statement as above, I tried the following:
// Where Context property returns a singleton instance of MyDbContext
var db = MyDbContextFactory.Context;
var data = new MyData { Timestamp = DateTime.UtcNow, Data = "123" };
db.MyData.Add(data);
db.SaveChanges();
This offers me the performance improvements I hoped for but I quickly realized that this is not thread safe and wider reading has confirmed that I shouldn't do this.
So does anyone have any advice on how to improve the performance when accessing SQLite DB in my case with EF Core and a multi-threaded UWP application? Many thanks in advance.
Secondly, I tried to create a single DbContext once statically and share it across my entire application. So instead of creating the DbContext in a using statement as above, I tried the following...This offers me the performance improvements I hoped for but I quickly realized that this is not thread safe and wider reading has confirmed that I shouldn't do this.
I don't know why we shouldn't do this. Maybe you can share something about what you read. But I think, you can make the DBContext object global and static and when you want to do CRUD, you can do it in main thread like this:
await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
//App.BloggingDB is the static global DBContext defined in App class
var blog = new Blog { Url = NewBlogUrl.Text };
App.BloggingDB.Add(blog);
App.BloggingDB.SaveChanges();
});
But do dispose the DBContext at a proper time as it won't automatically get disposed.
I have a problem with ASP.Net Identity used in application scaled to two instances. It seems that we have problem with checking password (checking hashes?) or verifying generated tokens (for example: password reset). When application works on one instance, then everything seems to be fine. It's weird because I read that using ASP.Net Identity in Azure cloud should be safe - it should use the same machine key on both instances.
Our user manager use token provider created below:
public DataProtectorTokenProvider<ExternalUser> Create(string purpose = "GeneralPurpose")
{
var provider = new DpapiDataProtectionProvider(_appName);
var result = new DataProtectorTokenProvider<ExternalUser>(provider.Create(purpose));
return result;
}
What is more, our user manager is singleton, but I think it shouldn't be a difference.
Any idea why it doesn't work on two instances in Azure? I will appreciate any help or advice.
I'm very new to MVC (as in this is my first project working in it, I apologize for any misguided or bad explanations) and I am not fully understanding how to make a user role. I know there are lots of these questions on here but none of them have been able to help my understanding of the role manager.
I am using ASP NET identity to make my users, edit them, the whole deal. I have all the Identity generated tables in my database with all their relationships. Now, I can make users but I have no real idea with how to get those users roles.
I figured that roles were just a part of the Account model/controller, like login and registration, where I just needed to alter a few lines of code to get it to work in my project with my own db. But I can barely find any code files that have a mention of roles. Anything I've seen through Google has just flown over my head (namely since that code first and seeding stuff is a little much for me to swallow as an admitted newbie and someone who added a database and used the code-first generator in visual studio)
Is there something in the Account or Identity files controller/model I need to alter or write in or is this like creating CRUD actions for my own tables where I make a controller based on my model and add in roles from there?
I am using Identity 2.0 with MVC 4.5. Thanks in advance for the help!
---- Edit ------
I've done a bunch more Googling and I think that when I made my project I wasn't given the code behind for RoleController or ApplicationRoleManager, etc. Is this normal or did I set something up wrong?
I managed to figure out my problem and with a lot of trying to convert c# into vb here we go!
I ended up seeding my roles into my database. In the startup.vb---in the root directory not the views directory---write the following:
Imports Owin
Imports Microsoft.Owin
Imports Microsoft.AspNet.Identity
Imports Microsoft.AspNet.Identity.EntityFramework
<Assembly: OwinStartupAttribute(GetType(Startup))>
Partial Public Class Startup
Public Sub Configuration(app As IAppBuilder)
ConfigureAuth(app)
createRoles()
End Sub
Private Sub createRoles()
Dim UserManager As New UserManager(Of ApplicationUser)(New UserStore(Of ApplicationUser)(New ApplicationDbContext))
Dim RoleManager As New RoleManager(Of IdentityRole)(New RoleStore(Of IdentityRole)(New ApplicationDbContext))
Dim role = New IdentityRole()
If Not RoleManager.RoleExists("Administration") Then
role.Name = "Administration"
RoleManager.Create(role)
UserManager.AddToRole("user id", "Administration")
End If
If Not RoleManager.RoleExists("User") Then
role.Name = "User"
RoleManager.Create(role)
UserManager.AddToRole("user Id", "User")
End If
End Sub
End Class
I added in a new sub procedure to my Startup class to create my roles (administration and user). In addition, the two imports of Identity and Identity.EntityFramework need to be added to the file for this to work.
In the createRoles Sub, we need to define a new UserManager (to add the roles to users) and RoleManager (to add roles to the db). Inside of the two Managers, the 'ApplicationDbContext' will be replaced with your DbContext (which is found in the IdentityModel in the Model folder) After that we need to add a new IdentityRole object, also so we can add a role.
Next, we check if a certain role exists 'administration', it doesn't exist so we add a name to the the role object and use the RoleManager to create it. Since I already have users in my database I can use the UserManager to add the user a role. I grabbed my user id from the database and wrote that in and then the name of the role.
I do this again for the other role I want to add 'User'. Build the project and volia! You can users with roles and now you can apply those roles to your page controller to have pages only accessed by 'administration' or 'user'.
I commented out the call to the function after I ran it so it wouldn't call it every build but since those roles exist it won't add anymore
You can apply those role to your pages in your controllers like so:
<Authorize(Roles:="Administration")>
Public Function yourPage() as ActionResult
Return View()
End Function
<Authorize(Roles:="Administration, User")>
Public Function yourPage2() as ActionResult
Return View()
End Function
I hope this can help someone else out in the future!
I am creating dynamic connection strings in my project. They're created on the fly with the information provided specifically for every user. When the application first fires off, if a database doesn't exist (first time user logs on), a new database is created without problems with this initializer:
public DataContext() : base()
{
// ProxyCreation and LazyLoading doesn't affect the situation so
// comments may be removed
//this.Configuration.LazyLoadingEnabled = false;
//this.Configuration.ProxyCreationEnabled = false;
string conStr = GetDb();
this.Database.Connection.ConnectionString = conStr;
}
The problem is, with this method, I have to restart the application pool on the server and the new user should be the first accessor to the application.
I need the same thing without a requirement of restarting the app. Is that possible?
(This is a SPA using AngularJS on MVC views and WebApi as data provider - May be relevant somehow, so thought I should mention)
I already tried this, but this creates an error for EF and the application doesn't start at all...
You could try a little bit different approach to connect directly (and create) the right database.
public class DataContext : DbContext
{
public DataContext(DbConnection connection) : base(connection, true) { }
}
Here you create the DbContext already with the right connection.
Take also care because you need to specify to migrations that the right connection should be used (not the Web.Config connection but the connection that raised the database creation).
See the second overload here https://msdn.microsoft.com/en-US/library/hh829099(v=vs.113).aspx#M:System.Data.Entity.MigrateDatabaseToLatestVersion.
I have models based on EF Code First and I want to use them with the default MembershipProvider, but I don't know how to write the model correctly, so it won't erase all my data on recreating the tables when there were changes made to the model.
Have a look at this project
http://codefirstmembership.codeplex.com/
It has entity classes for users and roles, as well as a roleprovider and membershipprovider implementation. If you include the users and roles in your datacontext class the tables will be created in your database.
Your question has two parts.
How to use asp.net membership API with EF code first?
How to preserve existing data when model changes?
as for How to preserve existing data when model changes, as far as with EF 4.0/ asp.net mvc 3, database migrations are not yet supported. You will have to move to asp.net mvc 4.0/ EF 4.3 where database migrations are supported or use similar alternatives , but its still beta release.
asp.net mvc 4.0 database migration in scott gu's blog
Now coming to the point on how to use asp.net membership provider with EF code first. There are couple of challenges :
We cannot/should not do an join with asp.net membership provider tables. Its not recommended, so my suggestion will be to create a "adapter class" for asp.net membership provider classes. For ex :
public class UserAdapter
{
// all user related attributes. Not stored in membership schema, but your schema
public string UserProxyName;
// some attributes stored in membership schema
[NotMapped]
public string Email {
get
{
Membership.GetUser(UserProxyName).Email;
}
}
// some attributes stored in membership schema and not in your schema
[NotMapped]
public string[] UserRoles
{
get
{
return Roles.GetRolesForUser(UserProxyName);
}
}
}
Now for updating information , you may write some functions in Model itself, however i would suggest create a UserRepository with repository design pattern to handle user CRUD operations.
Second challenge is how to create the database on first run. As seeding becomes an issue, if you want to seed user information then seperately running aspnet_regsql is not efficient as membership schema is expected before the seeding happens. I came across this nice article , with some fine tuning it worked for me :
asp.net membership and EF
Currently (EF 4.1 CTP) EF Code First doesn't have that option. It always drops a table if you made changes to model.
Update:
EF 4.1 RTM allows you to create a custom database initializer and specify creation of db objects and data seeding.
If you are using SQL Server, then check this :
http://www.paragm.com/ef-v4-1-code-first-and-asp-net-membership-service/
There is also my library which basically allows you to define how almost everything should be configured including: key type, where the your context object is, and where your user/role entities are located. Extendable using abstract base classes or interfaces. Also works quite well out of the box with repository pattern / unit of work / IoC Containers.
Source: https://github.com/holyprin/holyprin.web.security
NuGet: https://nuget.org/packages/Holyprin.Web.Security
In my DbContext.cs file I have a Seed function where I call the ApplicationServices.InstallServices() to install the ASP.NET Membership to my database. Now everytime my initializer drop the database it recreates ASP.NET Membership schema again.
public class PanelInitializer : DropCreateDatabaseAlways<PanelContext>
{
protected override void Seed(PanelContext context)
{
//Install ASP.NET Membership
ApplicationServices.InstallServices(SqlFeatures.Membership | SqlFeatures.RoleManager);
new List<Panel>
{
The ApplicationServices class
using System.Configuration;
using System.Data.SqlClient;
using System.Web.Management;
namespace Lansw.Panels.DataAccess.Contexts
{
public class ApplicationServices
{
readonly static string DefaultConnectionString = ConfigurationManager.AppSettings["DefaultConnectionString"];
readonly static string ConnectionString = ConfigurationManager.ConnectionStrings[DefaultConnectionString].ConnectionString;
readonly static SqlConnectionStringBuilder MyBuilder = new SqlConnectionStringBuilder(ConnectionString);
public static void InstallServices(SqlFeatures sqlFeatures)
{
SqlServices.Install(MyBuilder.InitialCatalog, sqlFeatures, ConnectionString);
}
public static void UninstallServices(SqlFeatures sqlFeatures)
{
SqlServices.Uninstall(MyBuilder.InitialCatalog, sqlFeatures, ConnectionString);
}
}
}
Thanks to #ImarSpaanjaars http://imar.spaanjaars.com/563/using-entity-framework-code-first-and-aspnet-membership-together.