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");
Related
Related to this previous topic : Database : Account table with Joined Tables Inheritance
I've created a joined tables inheritance in my database (MySQL) :
client_id attributes are both PK and FK.
Now I need to represent it with Doctrine ORM but I can't find a solution of how to make that.
I created, with Symfony commands, the Client entity and for the ClientCompany entity I don't know which relationship to use nor how to use the Client ID as primary key of the CompanyClient entity.
Does anyone know how to do it?
First of all, be really carefull with inheritance, this is a really good feature of doctrine but need to be used with a lot of cautious because of it's counterpart.
For your case, i would advise to not try to put "person" and "company" under the same abstract "client" class for conception reason i already explain in this answer since a company and a person are totally different things: Symfony 6 inheritance Mapping : How to submit a form depends on a clicked radio button?
But i will still answer on how to properly do a join table inheritance :
AbstractClient.php
#[Entity]
#[InheritanceType('JOIN_TABLE')]
#[DiscriminatorColumn(name: 'discr', type: 'string')]
#[DiscriminatorMap(['person' => Person::class, 'company' => Company::class])]
abstract class Client
{
// you do not need clientType since it is hold by the "discr" column
// And if you want to know what type your client is you can do it using
// if($client instanceof Person::class) { do something}
}
Person.php
#[Entity]
class Person extends Client
{
// ...
}
Company.php
#[Entity]
class Company extends Client
{
// ...
}
Take a look at #[InheritanceType('JOIN_TABLE')]
It will create one table for each entity and they will share ids.
If you create a company with id 2, there will be a client with id 2. So a Person with id 2 will never be possible.
But if you use 'SINGLE_TABLE' it will create only one table with all the field of all the entity which will be empty depending on which child you inserted inside.
But again i strongly advise you to not use join table inheritance for your usecase
I have to implement this form, which at first seemed easy to me. For the models part, I created a one-to-many relationship between GeneralInformation and CourseList (very obvious). In the GeneralInformation I included the bottom section as well with the 'Comments', 'Remarks'etc.
The problem is that before submitting the General Information section, you fill the Course List which will give an error for the FK constraint (again, obvious). For the Course List I'm using DevExtreme datagrid.
The only solution I came up with is to create another table, which keeps the ID of GeneralInformation and each Course ID. A similar solution for many-to-many relationships. If this seems like a viable solution, then how do you store the IDs of Courses in the controller, and then the ID of GeneralInformation, to put them in the database. Now I think I'm handling two models with the same controller, which might not be an optimal solution or even against guidelines of .NET Core.
If someone has a better solution, it would be greatly appreciated.
Your Model can be like this
public class Model_Name //enter your model name
{
public string FirstName {get;set;}
public string LastName {get;set;}
//other properties from General Information form
public List<Course> Courses {get;set;} //Course is a separate model
public string Remarks {get;set;}
}
In HTML do not map course to General info, Post the form and pass all the fields as json like below
{'FirstName':'Test','LastName':'Test','Courses':[{'Id':1}], 'Remarks':'test'}
So you're basically asking how to bind to a collection in a model.
Property binding would look like this:
<input asp-for="#Model.Collection[i].Property" />
What i did was store the course table (or grid) row as a partial view that takes an index as an argument with the course model and at the same time i maintain an index of the courses count using data-id attribute on each row and then every time a user wants to add a course i use ajax with the index of the last course as a parameter to fetch a new row rendered with an incremented index
and i append it to the table.
That way when your submit your form it will bind to a collection what ever the length is.
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).
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.
I am working on asp.net MVC 5 application. I am using Identity for creating users. I am using it first time and creating first mvc 5 application. I have two types of users. I want two tables for both types of users. So first record should be inserted in aspnetuser table and then in related tables and aspnetuser table should be linked with both tables so that i can access them. I have created models for both types of users. How can i link them with identity in model ?
Please suggest
Well, first, you can only truly have one user, at least in terms of the stuff Identity does. You can subclass that single user to create other types, though. So, essentially, you'd have a setup like:
public class ApplicationUser : IdentityUser
{
...
}
public class FooUser : ApplicationUser
{
...
}
public class BarUser : ApplicationUser
{
...
}
public class BazUser : ApplicationUser
{
...
}
In other words, just one class inherits directly from IdentityUser, while all your various types will inherit from that one class (in this case, ApplicationUser).
By default, Entity Framework handles inheritance via STI or Single Table Inheritance. How this works is that you'll have just one table AspNetUsers (the default table name for the Identity user), and any properties your subclasses add will be added as columns on that table. This means that all your subclass properties have to be nullable, since if a different subclass or just the base ApplicationUser class are persisted that property wouldn't be set.
If that's not acceptable, there's other inheritance strategies you can implement, namely TPT (Table-Per-Type) and TPC (Table-Per-Concrete Type). In TPT, the base, shared properties go all in one table and then each subtype has its own table with just columns for the properties it adds. In TPC, every class gets it's own table for everything, so each of your individual subtypes would have all of the Identity columns added. I don't believe this particular option would be compatible with Identity, though, or at the very least it would be highly burdensome as all the foreign key relationships between things like roles and claims would have to be duplicated for each table. Even if you could hack Identity to make that work, you'd lose any cohesive way of doing anything with all users, regardless of type, at once.
Basically, your options are the default, also known as TPH (Table-Per-Hierarchy), or TPT. However, TPT is less efficient because it essentially requires a join for every query just to get the full instance. If you absolutely need to have non-nullable properties on your subclass, it might be an appropriate option. However, remember that the property only needs to be nullable at the database-level. If you go with TPH, you can still require that those properties have values via validation in the application.