Best way to store ASP.NET 4.5 MVC user data - c#

I'm currently creating a web application using ASP.NET 4.5 MVC with MySQL backing my models.
Upon first login (and whenever they want to afterwards), the user enters their persistent information required for the web app (name, DoB, address, etc.)
There will be a couple to a few dozen settings which are required, and I've heard that best practice dictates that even going past as few as 10 columns in a table calls for reevaluation of your paradigm.
Let's say theorietically that I could break the settings down into different types. Would there be any performance/organization/usability/etc. benefit from creating different tables for each different section?
Follow up question: should persistent user data be stored in the same table as your table of users, or should Settings have its own table? I notice that there is an AspNetUsers table which stores some basic information. Could I use this to store settings?
Thank you!

You want to extend on the Identity 2 Framework:
http://typecastexception.com/post/2014/06/22/ASPNET-Identity-20-Customizing-Users-and-Roles.aspx

In my opinion, there is 0 gain on breaking all the user information on different tables. Do it only if you have some 1:N relation, but there is no problem if you use a single table with a lot of columns.
If you use Entity Framework and Asp.Net Identity, it's possible to extend your AspNetUsers table with custom information. Just create a class that extends from IdentityUser:
public class User : IdentityUser
{
[MaxLength(50)]
[Required]
public string MyAdditionalData { get; set; }
[Required]
public int Age { get; set; }
}
And then in the Context class:
class YourAppContext : IdentityDbContext<User>
{
public YourAppContext()
: base("YourAppContext")
{
}
}
Using migrations, Entity Framework is capable to add/modify columns as requested, based on this class.
Greetings

Related

Asp.Net identity and CodeFirst migrations

I installed nuget packages for asp.net identity and followed this MS tutorial which, by creating a new user, creates all of the tables e.g. AspNetRoles, AspNetUsers in my database.
The problem is, I'm trying to use CodeFirst migrations for DB source control and my Visual Studio side has none of the models for these tables. I don't want the "Create user" method to automatically create the tables, I need to get my models in Visual Studio and then push it using a migration.
Currently I have all the [AspNet] tables in my database and no reflection of this in my migrations. I can delete the tables but need to know how to populate the code first!
I'll also probably need to customise the models later, but that's another issue.
You can extent your asp.net identity as bellow
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public DbSet<Employee> Employees { get; set; }
public DbSet<Attendance> Attendances { get; set; }
public DbSet<PunchRecord> PunchRecords { get; set; }
}
Took me two hours to find I simply needed to add a model that inherits IdentityUser and then add a migration. I've called the model "ApplicationUser" - based on a tutorial I found on how to extend AspNetRoles and an answer somewhere in stackoverflow, it seems this name is possibly automatic or scaffolded somewhere... but I just chucked it manually in my Models folder:
public class ApplicationUser : IdentityUser
{
}
Also, I had to tinker with my application's context to inherit from IdentityDbContext - this way I don't need two separate connection strings and my app context is now all inclusive.

Issue with ASP.NET MVC 5 Web Application Entity Framework

I am working on MIT open source license example ASP.NET MVC web applications, and adding them as github public repos, I am also planning to have private github repos for my applications I plan to make money with in the future. I have a developer account with github.com.
I created a BOOKS MVC 5 web application using a TSQL script I was provided during a previous job interview some years ago, and am using GUID for the primary key ID fields with a default value of NEWID(), instead of an INT with IDENTITY, the solution is an ASP.NET MVC 5 web application with database first Entity Framework. I am using LocalDB for my SQL Server with this project, the script to create and populate the database is in my SQL-Server repo and is called BOOKS_Create.sql
I published the solution to my GitHub at the following URL:
https://github.com/ABION-Technology/Books
The TSQL scripts are available in the following repo:
https://github.com/ABION-Technology/SQL-Server
I added links the the shared layout view to show the index view for all Authors in the database, and also links to Index views for the TITLE and SALE EF models.
THe AUTHORS link works just fine, and lists all the authors in the database. But when I click the TITLES link, I get a SQL Exception of 'Author_ID' invalid column name, I did a search through my entire solution and did not find any variable named 'Author_ID' and did not find a C# class with a property called 'Author_ID". So this issue has me very confused there does not appear to be a good way to debug this issue. Thanks.
EF will follow some default conventions to work out FK relationships. The error you are seeing is due to Author having a Titles collection and EF is attempting to automatically set up the 1-to-many between the two. It's expecting to find an "Author_ID" on the Title, which doesn't exist because your schema is set up with a joining table called TitleAuthor.
To resolve this, you will need to map the TitleAuthor entity, in which the Author will contain a collection of TitleAuthors which refer themselves to an Author and Title entity. EF can automatically map joining tables given those tables consist of just the two FKs. As soon as you want to introduce additional fields, then you need to define the joining entity.
public class TitleAuthor
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; internal set;}
public virtual Title Title { get; internal set;}
public virtual Author Author { get; internal set;}
// add other properties as needed..
}
So from your Author entity:
public virtual ICollection<TitleAuthor> Authors {get; internal set;} = new List<TitleAuthor>();
To access the titles for the author:
author.Titles.Select(x => x.Title);
I would recommend reading up on many-to-many mapping with EF. I invariably use deliberate mapping with EF rather than relying on it's conventions. It just helps make it more predictable.
If you are using defaults for PKs then you need to tell EF via the DatabaseGenerated attribute. This isn't needed for read operations, but it will be needed when you go to insert records.
Also, with SQL Server, consider using NewSequentialId() as the default for your UUID PKs. These are more index-friendly than NewId().
The above example using internal (private works too) setters to promote DDD style use of entities. Public setters can lead to misuse/abuse of entities in the sense that the context will diligently attempt to save whatever you set. It's generally a good idea to restrict functionality that would alter an entity's state to a method in the entity with required arguments to be validated, or a repository. I use internal scoping to allow unit tests to still initialize entities. (leveraging InternalsVisibleTo between domain and unit test assemblies)
Reason is you are getting that Author ID error is, you have list of Titles in Author Class. Then there should be relationship between Author and Title entities, which is not exists in your data context. Comment public virtual ICollection<Title> Titles { get; set; } . And it should work.
Reason for you cant search this attribute is, its automatically generated by entity framework. (TableName_PrimaryKey)
If you want to keep this, create relationship in database using foreign keys and add that to your data context also. You may refer this

Xamarin SQLite using LINQ

I got a sqlite table in xamarain (native android / pcl):
[Table("Customer")]
public class Customer
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public Address Address{ get; set; }
}
"Address" represents a second table.
1) Is it possible to automatically create the "Address" Table when I call
connection.CreateTable<CustomerDto>();
because it is it's dependency?
2) Is it possible to use a LINQ expression which automatically maps the correct "Address" to this "Customer?
In my .NET Standard library I'm using:
"sqlite-net": "1.0.8"
"sqlite-net-pcl": "1.3.1"
My approach was to create "initial state models" of all the tables, marked as abstract (so there is no risk that somebody could instantiate them), defining only the fields necessary in the database and the primary keys (GUID in my case), used only to create tables at the beginning. Following modification to the data structures always with ALTER instructions.
In another namespace a duplication of all the models, this time with getters/setters and other utilities, and I used these as "real models".
For representing linked models I used a field as Id and another one as model (refreshed when necessary):
public int IdAddress { get; set; }
[Ignore]
public Address Address { get; set; }
I don't think sqlite-net can do what you are asking because it's a very lightweight orm, and even if it could I prefer don't automate too much because of my past experiences with Hibernate.
https://github.com/praeclarum/sqlite-net
https://components.xamarin.com/view/sqlite-net
It sounds like you should look at using Entity Framework because that will allow you to use LINQ with SQLite. The standard library on the web (not Entity framework) is very light and doesn't have much functionality for the ORM like functionality you are looking for.
If you're looking for a more lightweight library, you can use this, but it will not allow you to write LINQ expressions without writing your own ORM:
https://github.com/MelbourneDeveloper/SQLite.Net.Standard

How can I add custom attributess to my user model using ASP.NET MVC 5 framework?

I am building an app using ASP.Net MVC 5 framework with a database code approach.
I need to add custom fields to my Users Table. What is the best way to add custom attributes and access them later?
Here is what I have done so far
Logged into SQL Server and added the column to the AspNetUsers
table.
I created a new class which ApplicationUser like myUser class below.
public class User : ApplicationUser
{
public int UserId { get; set; }
public int CampId { get; set; }
}
Additionally, I tried adding my custom attributes to the ApplicationUser directly like the insstruction found on the answer in another question, but I get the following error
The model backing the 'ApplicationDbContext' context has changed since the database was created. Consider using Code First Migrations to update the database (http://go.microsoft.com/fwlink/?LinkId=238269).
What is the best way to add column to my AspNetUsers table?
Also, How can I access these field from the controller using the Authorized user?
You have to use code-first migrations.
In VS go to Package Manager Console and type:
enable-migrations
then you can add properties to your ApplicationUser class like Birthday or whatever.
Then go back to Package Manager Console and run:
Add-Migration aNameForYourMigration
So for example if you've added a Birthday property you would perhaps name it BirthdaypropertyAddedMigration or whatever makes sense to you.
Then run:
Update-Database
now all your changes to the ApplicationUser class will be persisted to the database.
But if you do the database approach, you have to add the new fields to the users table and then call update model, to regenerate the code.

EF: Database design issues regarding cross database relations

Summary
I am currently prototyping a (very straight-forward?) multi-tenant web-application where users (stored in database 1) can register to different tenants (stored in a database per tenant (same db schema). An architecture that I thought would apply to a lot of multi tenant solutions.
Sadly, I found out that cross database relations are not supported in Entity Framework (I assumed it's still the case for EF6). I provided the links below.
The next short sections explain my problem, and ultimately my question(s).
The rational behind the design
I choose to have separate databases; one for users (1), and one for each tenant with their customer specific information. That way a user does not have to create a new account when he joins another tenant (one customer can have different domains for different departments).
How it's implemented
I implemented this using two different DbContexts, one for the users, and one for the tenant information. In the TenantContext I define DbSets which holds entities which refer to the User entity (navigation properties).
The 'per-tenant' context:
public class CaseApplicationContext : DbContext, IDbContext
{
public DbSet<CaseType> CaseTypes { get; set; }
public DbSet<Case> Cases { get; set; }
// left out some irrelevant code
}
The Case entity:
[Table("Cases")]
public class Case : IEntity
{
public int Id { get; set; }
public User Owner { get; set; } // <== the navigation property
public string Title { get; set; }
public string Description { get; set; }
public Case()
{
Tasks = new List<Task>();
}
}
The User entity
[Table("Users")]
public class User : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
}
This User entity is also contained by the Users database by my other DbContext derivative:
public class TenantApplicationContext : DbContext, IDbContext
{
public DbSet<Tenant> Tenants { get; set; }
public DbSet<User> Users { get; set; } // <== here it is again
// left out irrelevant code
}
Now, what goes wrong?
Expected:
What I (in all my stupidity) thought that would happen is that I would actually create a cross database relation:
The 'per-tenant' database contains a table 'Cases'. This table contains rows with a 'UserID'. The 'UserID' refers to the 'Users' database.
Actual:
When I start adding Cases I am also creating another table 'Users' in my 'per-tenant' database. In my 'cases' table the UserID refers to the table in the same database.
Cross database relations do not exist in EF
So I started googling, and found that this feature simply is not supported. This made me think, should I even use EF for an application like this? Should I move towards NHibernate instead?
But I also can't imagine that the huge market for multi tenant applications simply is ignored by Microsoft's Entity Framework?! So I most probably am doing something rather stupid.
Finally, the question...
I think the main question is about my 'database design'. Since I am new to EF and learning as I go, I might have taken the wrong turn on several occasions (is my design broken?). Since SO is well represented with EF experts I am very eager to learn which alternatives I could use to achieve the same thing (multi tenant, shared users, deployable in azure). Should I use one single DbContext and still be able to deploy a multi tenant web-application with a shared Users database?
I'd really appreciate your help!
Things learned:
NHibernate does support cross database relations (but I want to deploy into Azure and rather stick to microsoft technologies)
Views or Synomyms can be an alternative (not sure if that will cause more difficulties in Azure)
Cross database relations are not supported by EF:
EF4 cross database relationships
ADO.Net Entity Framework across multiple databases
Entity framework 4 and multiple database
(msdn forum with EF devs) http://social.msdn.microsoft.com/Forums/en/adodotnetentityframework/thread/cad06147-2168-4c20-ac23-98f32987b126
PS: I realize this is a lengthy question. Feel free to edit the question and remove irrelevant parts to improve the readability.
PPS: I can share more code if needed
Thank you so much in advance. I will gladly reward you with upvotes for all your efforts!
I don't quite understand why do you need cross database relations at all. Assuming your application can talk to the two databases, the user database and a tenant database, it can easily use the first database for authentication and then find related user in the tenant database with "by name" convention.
For example, if you authenticate a user JOHN using user database then you search for a user JOHN in the tenant database.
This would be much easier to implement and still match your requirements, users are stored in users database together with their passwords and "shadow copies" of user records but with no passwords are stored in tenant databases and there is NO physical relation between these two.

Categories