ASP.NET MVC5 Entity Design Linking Tables in Different Databases - asp.net

I am working with a Database First design using ASP.NET and MVC5. I have followed several tutorials in an effort to learn this and in every one of these I end up with my security-related tables (users, roles, etc.) in a completely separate database from my other application data. This is not a problem however I can't seem to figure out how to create relationships between my application tables (which often need to contain the UserId for example) and the AspNetUsers table. I've searched for examples online and can't find any.
I have attached a very simple example; a table in which I need to link specific users (from the table AspNetUsers) to specific companies (from the table Company). As you can see, I know how to create the relationship between this new table and the Company table, but how do I create the relationship between this new table and the AspNetUsers table?
Can someone help me understand how this is done? Thanks!!

You can't. Foreign keys can't span databases, so there's no way to create a hard relationship between the two entities in two separate databases. All you can do is store the id, and then manually query the other database for that id when you need that entity.
For example, given:
public class Company
{
...
public virtual ICollection<UserCompany> UserCompanies { get; set; }
}
public class UserCompany
{
[Key]
public int Id { get; set; }
public int UserId { get; set; }
// Can't create a foreign key here, because the user table is
// in another database
[ForeignKey("Company")]
public int CompanyId { get; set; }
public virtual Company Company { get; set; }
}
Then, you could do:
var userIds = someCompany.UserCompanies.Select(m => m.UserId);
var users = userContext.Users.Where(m => userIds.Contains(m.Id));

Not possible.
For the best solution you probably want to go with something like this: Multiple contexts with Entity Framework 6, reference entities across dbcontexts
That abstracts away your concerns so you can use a single context. However, if you're dead set on using two contexts and need to insert data you're going to have to maintain these relationships yourself in your code. For example, you can override ValidateEntity for the dependent entity such that it checks the other context and verifies that the required entity exists. This adds a lot of complication though, because you need to ensure your save changes is called in the correct order (i.e. the required entity context is saved before the dependent entity context is saved), and it could also add some snafus with TransactionScopes. I don't know how well a transaction across two databases will be rolled back.
For reads though, you just have to read the objects from both contexts and manually build up your object graph yourself.
Seriously though, I highly recommend abstracting this away at the database.

Related

How can I inherit using TPT using EntityFramework from different Views?

Supposing I have two tables Meetings and Attendees.
My database looks like this.
// Table Meeting
Id
Description
// other properties ...
// Attendee
Id
Name
MeetingId
// other properties ...
I have two views that are mapped to these tables. One is ViewMeetings with just the meeting data the other ViewMeetingAttendees joined with Attendees.
I want to use table per type(TPT), mapping each table to a view.
public abstract class MeetingBase
{
// Some properties here
}
public class ViewMeeting : MeetingBase
{
}
public class ViewMeetingAttendee : MeetingBase
{
public String AttendeeName { get;set; }
}
// Configuration
moduleBuilder.Entity<ViewMeeting>().ToTable("ViewMeetings");
moduleBuilder.Entity<ViewMeetingAttendee>().ToTable("ViewMeetingAttendees");
// NOTE fixed the ViewMeeting error as stated in HansVG answer below.
Every time I try to run this code I get an error Invalid Column Name "Discriminator"
I understand that the entity framework is trying to resolve the types as a table per hierarchy(TPH). However, I still want to map the properties using inheritance without the inferred TPH. This is reasonable since all the columns are the same except for one. I have ten other columns and two views. Also I have a single meeting entity needing most of the same columns for CRUD operations.
Is there a way to keep the inheritance but lose the discriminator error? [NotMapped] is not an option since I am still pulling the data from the database. Also, I don't prefer to join the tables locally using LINQ since there are joined entities that don't need to be mapped otherwise.
You defined 'ViewMeeting' twice and didn't configure 'ViewMeetingAttendee'.
Your configuration should be:
moduleBuilder.Entity<ViewMeeting>().ToTable("ViewMeetings");
moduleBuilder.Entity<ViewMeetingAttendee>().ToTable("ViewMeetingAttendees");

Can i add new custom tables inside my aspnet database inside asp.net MVC-5 project

I have the following:-
Visual Studio 2013.
i created a new asp.net MVC-5 web project.
the project is using asp.net identity 2.2.
for the authentication method i chose "Individual user accounts"
this process created a new database named aspnet-OurProjectNanme-number
inside the automatically generated database, i have a table named AspNetUSers which store the user info.
now i am working on building an ERP system. and inside the ERP system i want to add the following:-
a table named "Asset" to store the asset info.
the "Asset" table will have 2 columns named "CreatedBy" + "ModifiedBy" which should store the userId who created and modified the asset item.
now i am not sure how i need to achieve this? as i need to add a foreign key between my custom table "Asset" and the "AspNetUsers" table which have been created automatically.. so can i add my custom table "Asset" inside the automatically generated database, and build the foreign key between the Asset.CreatedBy and AspNetUsers.Id ??
if the answer is Yes then can this relation break in the future if we want to upgrade our aspnet identity version ? as upgrading the identity might result in creating new tables or renaming existing ones etc.. which might break the relation between the Asset table and the AspNetUsers table?
If the answer is No (i should not add custom tables inside the automatically generated database ) then how i can build the foreign key ?? and where i need to add the Asset table in this case??
The most common approach to what you want to do is simply to add your additional model as a DbSet in your ApplicationDbContext.
public class Asset
{
public string CreatedBy { get; set; }
public string UserId { get; set; }
public ApplicationUser User { get; set; }
}
public class ApplicationUser : IdentityUser
{
public IList<Asset> Assets { get; set; }
}
public class ApplicationDbCotext : IdentityDbContext<ApplicationUser>
{
public DbSet<Asset> Assets { get; set; }
}
As I mentioned this is the most common approach as updating the Identity packages should have no impacting affects on your schema. That said you should always test updates before pushing to production.
UPDATE:
Note that when you're working with One to Many relationship's you will see in our Asset model a property for the User Id foreign key as well as the User object. Because of the relationship we are then able to create a List<Asset> in our User to complete the One to Many relationship. This will then allow us to directly query Assets belonging to a User.
As for Code First vs Database First the difference really comes down to how you define the mapping between Entity Framework and the Database.
As I mentioned below there is no one size fits all answer to should you separate the Identity context from your business context, or should you separate them into separate databases. The reality is that only you can answer that question for your needs. It is far more common to have all of the data in a single database. That said, there is something to be said for the security of having identifying information about a user such as their name, email and password hash separated from information like their address or payment information. The trade off is that you can find yourself trying to maintain objects that are supposed to be tied together but are only loosely related because they reside in different databases. Also you would then need to make sure you're using different users/passwords to connect to the different databases, and it's better to have the databases on different servers because if the server gets compromised you went through the entire exercise for nothing. The trade off to get the theoretical security ends up being so impractical with there consistently being another thing you have to do, that you end up seeing everything in one database where you can focus all of your hardening efforts.
Both the ApplicationDbContext and ApplicationUser objects should typically be created for you when you File -> New a project with Individual Authentication. You can add as many properties and relationships to your User as you require.
-- Update --
The answer is growing and growing and so is the discussion. I think I've shown all kinds of variations, which may not have helped to make it understandable. So here is a summary. For explanation, read the full answer and discussion.
Out of the box you have two contexts, identity and business. These are decoupled, so you can change your security without interfering with your business. This way, upgrading security won't break your application or other models. Since the contexts are seperate, changes to either one of them won't affect the other.
As a sidenote: you are not intended to directly access the AspNet identity tables. Implement the UserManager and use the avaiable methods of the manager to perform actions.
Now it comes to logic, where should information be stored? As a simple rule just ask yourself the question: is it part of security or business?
In both contexts you have users. For your requirement this is a logical 1:1 relation. But they are actually seperate. You can create people without supplying a login or delete a login, without deleting a user (people), e.g. for historical reasons.
All you want is to find all information for the current user. So all you need is the People.Id.
Without having to change the IdentityUser you can create the 1:1 relation by just overriding the AspNetUser.Id.
var appUser = new IdentityUser
{
UserName = model.Email,
Email = model.Email,
Id = Convert.ToString(People.Id)
};
var identityResult = await userManager.CreateAsync(appUser, model.Password);
You do not need the identity context for your business. All you need is People.Id. The identity context is only used when tokens are issued and users are created / modified.
To obtain the id use something like this:
var peopleId = int.Parse(Request.User.Identity.GetUserId());
Now you can query your business model using the Id.
When registering, extend the View and ViewModel with the People information you want to store. This will allow you to add both People and AspNetUser at the same time. Though this is not one transaction. But I think it is highly unlikely that creating either one would fail if you perform checks first.
You can validate the username and password (use the methods in the UserManager) and check the ModelState of the viewmodel before creating the user. Use attributes to force Required fields to be filled.
-- Original answer --
In order not to repeat myself, read my answer here.
In short, keep identity and business seperated.
Just in case the identity logic is removed from the same database, like when implementing IdentityServer.
It seems you have business information in AspNetUser. If so, create a Person table and move the information to that table. Relate to that table in your model. In table Person you can add a reference to AspNetUser.
-- update --
I think you understand correctly, but I will just add the details to this answer.
In most cases all tables are defined in one database. But that doesn't mean they are all part of the same model. There can be multiple contexts. In this case one for Identity and one (or more) for Business.
Now why seperate those two? The most important difference between the Business model and Identity model is that Identity tables are not to be called directly. We use the Owin context to call the UserManager / RoleManager.
That is why we cannot add these tables to the business model. Things can be altered in a way that is not secure. Also we do not want the business to have any knowledge about authorization. It shouldn't matter how this is done, as long as a user is identified and authorized.
Also you may want to implement OpenId and claim based authorization. In that case information doesn't have to be available in the database.
The idea is to create a 1:1 relation of the identity table AspNetUsers and business table People. There can be some redundancy, like email or (user)name. But that isn't a problem. The People table should contain all information you want to use in your business model. And the business tables should only relate to People, not AspNetUsers.
Now about the link between AspNetUsers and People. There are four options:
Set People.Id = AspNetUser.Id. Please note that AspNetUser.Id doesn't have to be a GUID. You can add your own value as key.
Set AspNetUser.Id = People.Id.
Add column AspNetUserId to People. No modifications to Identity are needed. You can add People to the Identity Model as well, but I don't think you can create both records in one transaction. You can use User.Identity.GetId() to get AspNetUser.Id. You may however ask yourself if the business should have knowledge about this information.
Add column PeopleId to AspNetUsers. You'll need to extend the IdentityUser to add PeopleId. An advantage is that you don't need the AspNetUser Id, but you can use the actual Id of People. When using OpenId or claims you can get People.Id from claims and you won't have to add AspNetUser.Id to the business. Optionally you can add People to the Model and as navigation property of the extended IdentityUser. When creating the user, you can do this in one transaction.
In case you are creating the user in seperate contexts, you'll need to handle the rollback yourself. But before adding a record to People, you can already test if an AspNetUser can be added: has a valid name/email and password.
Since your business model relates to the People table, you can query all assets and join with the People table for additional information. Or you can get all assets for the current user.
o yes, there are two contexts. The identity model, which contains the AspNet... tables + optionally People. And the business model, which contains all ERP tables + Asset + People.
You may consider to use code first for identity framework 2 and database first for the business model.
I hope that this helps. If not, let's continue in chat.
-- update --
The answer focused on seperation of domains: identity and business. That is why I didn't discuss one possible alternative concerning the AspNetUsers table.
The two models are representations of the database, which means that the database doesn't have to be an exact match. You are free to map tables and fields as you like, as long as they don't break database logic.
Since AspNetusers and People has a 1:1 relation and when both tables are present in the same database, you may as well merge the two into the AspNetUsers table. You can also add relations to the AspNetUsers table, though you may want to add an extra Id (int) column instead of using the current Id (string).
This does not mean the People class can be discarded, except that we have to change the table mapping: AspNetUsers.
Example:
[Table("AspNetUsers")]
public class People
{
[Required]
[StringLength(128)]
public string Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
As you can see, the sensitive fields are not mapped. We need however the Id field. You can now read and update the mapped fields.
You don't have to extend IdentityUser. You can add an AspNetUser and then update the fields using People in the other context. But if you want to add a user in one single transaction it may be easier to extend the IdentityUser (make sure you'll define the new fields in both People and ApplicationUser):
public class ApplicationUser : IdentityUser
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
There are multiple advantages:
There is only one transaction to add the user.
You cannot expose the sensitive fields since they are not mapped in People.
You cannot add People to the database, since some required fields are not mapped in People.
Please note that this may not work for all types of models (code first/database first + migrations).

Store seperate sets of users in two tables or single table using ASP.NET MVC5 Identity framework?

I am building a SaaS application using ASP.NET MVC 5, where I need to store two different types of users who will have different field information to be stored for each. There will be Subscibers and General Users, where every General user will belong to a Subscriber. So, every user will have a parent subscriber.
Should I have a single ASPNETUSERS table to store all the users (subscriber / general users) with custom columns stored for each within the same table? I believe there has to be a better practice to achieve this. Since this will be a SaaS application where we are storing user information across multiple entities in a single database, what kind of database design should we go for?
For reference, here is the database design I have for the moment -
id [primary key of the user]
email
password
sub_name [subscriber name. will be populated only if the user is a subscriber]
sub_address [subscriber address, will be populated only if the user is a subscriber]
status [Stores if the user is active]
parent_id [Id of the parent. This will be populated only if the user is a General User]
Apart from the above, we will be registering the role of every user (Subscriber/General User)
Please advise. Thank you for your help.
You should use a single table and create two classes in your code for the different types of users. The ASP tables with entity framework use table per hierarchy (given away by the discriminator column):
public class Subscriber : IdentityUser {
public virtual List<GeneralUser> GeneralUsers {get; set;}
//other properties
}
and then create your general users:
public class GeneralUser : IdentityUser {
public virtual Subscriber Subs {get; set;}
//other properties
}

Displaying a list of a customer's contacts in my details view

My overall goal is to be able to display all Customer Information in my CustomerController->Details View and have no idea how to pass all of this data:
Customer info - (fields directly in Customer table)
Customer contacts
Customer locations
Customer documents
I am able to display the customer info just fine because I am doing something like this and passing it to my view:
public ActionResult Details(int? id)
{
Model.Customer customer = _custService.GetCustByID(id);
return View(customer);
}
I have absolutely no idea how to go from just this which gives me access to direct Customer properties in my view to displaying all of these other lists of related customer items that come from separate tables.
Here are a few details on my project setup:
I am using EF6DbFirst and I also create a Dto for each one of my entities and use AutoMapper to map everything. My Customer.cs model has all of the customer's direct properties and also a couple that look like this for the things that are one-to-many relationships:
public List<CustomerContact> Contacts { get; set; }
Now in my AutoMapper config I did the following since my table is named CustomerContacts in EF:
CreateMap<Customer, Model.Customer>()
.ForMember(dest => dest.Contacts, opt => opt.MapFrom(src => src.CustomerContact));
CreateMap<Customer_GetAll_Result, Model.Customer>();
I am not sure if this is the proper way to do this with AutoMapper or if there is anything else I have to tell it when accessing other tables. I am thinking the only reason I have to map this property is because I want to change the name to just c.Contacts rather than c.CustomerContacts.
Side question:
As you can see I am also trying to map values that come from my GetAll stored procedure which my custService.GetAll() uses in my controller to bind a grid of customers. I don't think this matters in this case but I am assuming if I ever need to loop through that list that comes from the proc and get the contacts for each customer they will not be available since my stored proc only returns direct customer properties which is why I can't do the mapping for that one. Is there any workaround for this?

Creating a foreign key reference to a User in ASP.NET MVC

I am fairly new to ASP.NET MVC and working on a small project in which I have a table called Tests in which I would like to add a foreign key reference to the UserID in the aspnet_Users table.
Which is the best way to do this? Is there any recommended way of doing it?
Should I have a Guid property in my Tests model:
public virtual Guid UserID { get; set; }
or it's better to have a navigation property:
public virtual ???? User { get; set; }
Which is the best way to do this and any hints on how I could implement this?
Thank you in advance!
Since the UserID in sapnet_Users is indeed a Guid (a uniqueidentifier in the SQL Server table), then yes - if you want to reference a specific user, you need to have a Guid value at hand.
If you want to have a navigation property that references the User type directly - you'd need to make sure you have the User as a type in your app, and you might need to specify the FK constraint name (in whatever data model you have).
I would think, both approaches work. If you only need to set the Guid and nothing else - approach #1 is fine and sufficient.
If you will need to read out (and possibly set) other properties of the User class - then I think approach #2 would be the better choice, even though it's slightly more involved / more effort on your part.

Resources