Entity Framework - Modeling multiple roles for same entity - entity-framework-6

I have a contact class
[Table("Contacts")]
public class Contact
{
public string Id { get; set; }
public string Name { get; set; }
}
I have two classes inheriting from Contact:
[Table("Employees")]
public class Employee : Contact
{
public decimal Salary { get; set; }
}
[Table("Suppliers")]
public class Supplier : Contact
{
public string TIN { get; set; }
}
I am using Entity Framework 6.3 as my ORM.
Can I model something where the same contact can be both Employee and
Supplier with the same unique Id.
Employee emp = new Employee();
emp.Id = "C1";
emp.Name = "Employees";
emp.Salary = 10000;
emp.TrackingState = TrackingState.Added;
Supplier sup = new Supplier();
sup.Id = "C1";
sup.Name = "Employees";
sup.TIN = "ABC";
sup.TrackingState = TrackingState.Added;
When I do:
context.Employee.Add(emp);
context.Supplier.Add(sup);
context.Save();
Obviously, it will not allow me to add the record.
I get an error:
"Violation of PRIMARY KEY constraint 'PK_dbo.Contacts'. Cannot insert duplicate key in object 'dbo.Contacts'. The duplicate key value is (C1).\r\nThe statement has been terminated."
Can I make the Supplier also share the same Id as that of employee and
enable insert / update of employee and supplier?
Thanks

I assume you are not doing any specific inheritance mapping which by default will map this as Table per Hierarchy (TPH) inheritance. With this mapping you get a single Db table that contains the aggregate of all the fields in the base type and the dependent types with a discriminator field to know which type the row should be materialized. This allows for doing a query where you ask for a type.
With TPH each row can only be a single type, so you can not have the same row be both an Employee and Supplier.
There is another type of mapping you can do called Table Per Type (TPT) which will create a table for each type, so in your case 3, one for the shared fields, and one for each dependent type. This should allow for what you are asking about. (YMMV)
However, it seems to be that Employee and Supplier would be used in much different domains so I would suggest that you create your Contact table and relate it to both your employee and supplier.
[Table("Contacts")]
public class Contact
{
public string Id { get; set; }
public string Name { get; set; }
}
[Table("Employees")]
public class Employee
{
public string Id { get; set; }
public string ContactId { get; set; }
public decimal Salary { get; set; }
public Contact Contact { get; set; }
}
[Table("Suppliers")]
public class Supplier
{
public string Id { get; set; }
public string ContactId { get; set; }
public string TIN { get; set; }
public Contact Contact { get; set; }
}
Now you can query for an employee:
db.Employees.Include(e => e.Contact).First();
Or for a Supplier:
db.Employees.Include(e => e.Contact).First();
Which might be cleaner than the inheritence query you would need:
db.Contacts.OfType<Employee>().First();
In both the Has A modeling I show above and the Is A Modeling with TPT you are getting three tables. You just have the FK in what I show rather than the same ID in 3 tables with TPT.
There is also Table Per Class you can look at, which is similar to TPT but you don't get a table for the abstract/parent class, rather you get table for each dependent type with all the fields in it. I don't think this is what you want because it would have duplicate data, however, it does mean less joins.

Related

Multiple tables update MVC .net

I am new to MVC and this is my function. There are three tables (Order, OrderNotes, Notes), ID is their primary key. One Order can have many Notes, the table OrderNotes has foreign key OrderID(from Booking table) and NotesID(from Notes table). I want to have a Order Edit page to display individual Order (FirstName, LastName), also display a list of its Notes. Here is my DB structure:
Booking table:
{ID,
FirstName,
LastName
}
BookingNotes table:
{ID,
BookingID,
NotesID
}
Notes table:
{ID,
NoteName,
StatusID
}
So how can I implement the list of Notes since it's from multiple tables? It will be able to Create New Note, Delete existing Note in the list row record, not Edit. Linq used in DB query. Thanks.
It would be a better idea to have only 2 tables:
public class Book
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
// Navigational properties
public virtual List<Note> Notes { get; set; }
}
public class Note
{
public int ID { get; set; }
public int BookID { get; set; }
public string NoteName { get; set; }
public int StatusID { get; set; }
// Navigational properties
public virtual Book Book { get; set; }
public virtual Status Status { get; set; }
}
A third table is useful when you want to reuse the same Note for a different booking. However i think this is not the case.
So to retrieve data for your context make sure you have the DbSet<Book>
public class ApplicationDbContext : DbContext
{
public virtual DbSet<Book> Bookings { get; set; }
}
In your controller (or better in a repository class):
var BookingID = 10; // this is parameter passed to the function
var myBooking = this.dbContext.Bookings
.Include(p => p.Notes)
.ThenInclude(p => p.Status)
.FirstOrDefault(p => p.ID == BookingID);
Map the retrieved booking to a ViewModel, pass it to the View and you're good to go.

How to configure a many to many relationship with primary key too in it

This is what i am trying to achieve :
modelBuilder.Entity<ApplicationUser>()
.HasMany(u => u.Following)
.WithMany(u => u.Followers)
.Map(m =>
{
m.ToTable("FollowTables");
m.MapLeftKey("UserId");
m.MapRightKey("FollowId");
});
In application user class, i have configured following and followers like this:
public ICollection<ApplicationUser> Following { get; set; }
public ICollection<ApplicationUser> Followers { get; set; }
follow table should be something like this:
public class FollowTable
{
[Key]
public int autoId { get; set; }
public int UserId { get; set; }
public int? FollowId { get; set; }
}
autoId is primary key and UserId and FollowId both are foreign key to ApplicationUser class where UserId is user's own id and FollowId are the ids which user is following.Its data could be following:
autoId UserId FollowId
1 4 11
2 4 12
3 4 13
Now, i problem is when i update database through pmc, it is creating two database tables one is FollowTables with column (USerId, FollowId) and one is FollowTables1(autoId, USerId, FollowId).
If i remove this line from applicationDbContext class:
public DbSet<FollowTable> FollowTables { get; set; }
then its creating only one table but with no primary key.
please someone help me out . how to properly configure UserId and followId as foreign key and these two should map to ApplicationUser's Id.
I want to use those Collection's following and Followers too.how to do it.
You have to decide if you want to work with an entity that represents the junction table or not. If you don't need to add any other properties to that table, excluding the FKs, then I suggest you don't map the junction table as entity. It is going to be more easy for you due to Entity Framework will handle that table for you.
Now if you really need to map that table then you need to delete many-to-many fluent api configuration and change the type of your navigation properties:
public ICollection<FollowTable> Following { get; set; }
public ICollection<FollowTable> Followers { get; set; }
That is going to create two one-to-many relationships with the junction table, an explicit representation of a many-to-many relationship. To do that you also need to do some changes in that entity:
public class FollowTable
{
[Key]
public int autoId { get; set; }
public int UserId { get; set; }
[ForeignKey("User")]
public ApplicationUser User{ get; set; }
[ForeignKey("Follow")]
public int? FollowId { get; set; }
public ApplicationUser Follow{ get; set; }
}
Also, I don't think FollowId Fk property should be a nullable FK, because you want to represent a relationship between two persons.
If you ask my opinion about what option you should take, I suggest you don't map the junction table if you are going to have only those properties.

Limit Value of a text field

I am new to MVC
I have an Employee POCO like this
[PetaPoco.TableName("tblEmployee")]
[PetaPoco.PrimaryKey("EmployeeId")]
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
public string Gender { get; set; }
public string City { get; set; }
public int DepartmentId { get; set; }
}
Department Id is actually a foreign key coming from Table tblDepartment. So I want to limit the value of DepartmentId in creating new Employee as the values existing in table tblDepartment(column : Id ).How to do this?
Existing code in Create View
#Html.EditorFor(model => model.DepartmentId)
Why not use DropDownList for Department selection?
#Html.DropDownListFor(m => m.DepartmentID, Model.DepartmentList, string.Empty)
public class Yourclass
{
public SelectList DepartmentList { get; set; }
public Yourclass()
{
FillModel();
}
internal void FillModel()
{
this.DepartmentList = GetDepartmentListFromDb();
}
}
Usually reference integrity is managed at a database level.
Make the relationship on your database between Employees and Departments to enforce reference integrity, so if you try to save an Employee whose department id don't exist on Department table it should return an integrity error.
Capture this error on your saving controller and return it to user.
Also, it will help if you allow your users only to load data from a limited list loaded with existing data on the database, as it is suggested in comments.
However this could not be enough, as a valid value could be deleted from database between the moment it was posted to the list in your form and the moment the form is sent to the controller to save data.

Entity Framework one-to-one relationships errors on insert

I'm having some troubles getting a set of Entities setup to work correctly. I'm using EF v5 in VS2012 against SQL Server 2008 R2 Express.
Everything seems to be correct with the generated database but I'm not having any success writing to some of the tables.
I have a UserProfile object defined like:
[Table("UserProfile")]
public class UserProfile
{
[Key]
public long UserId { get; set; }
[StringLength(56)]
public string UserName { get; set; }
public Country Country { get; set; }
...
}
I also have the Country entity defined like:
[Table("Country")]
public class Country
{
[Key]
public int Id { get; set; }
[StringLength(2)]
public string CountryCode { get; set; }
[StringLength(100)]
public string CountryName { get; set; }
public ICollection<UserProfile> UserProfiles { get; set; }
...
}
The generated columns look correct in the database, if I script it out it looks good:
ALTER TABLE [dbo].[UserProfile] WITH CHECK ADD CONSTRAINT [UserProfile_Country] FOREIGN KEY([Country_Id])
REFERENCES [dbo].[Country] ([Id])
GO
Just for testing purposes in a controller action I have the following:
WebSecurity.CreateUserAndAccount(model.UserName, model.Password, new
{
Country = new Country { Id = 223, CountryCode = "UK", CountryName = "UK" },
...
});
When this WebSecurity method is executed I get the following error:
No mapping exists from object type MyApi.Data.Entities.Country to a known managed provider native type.
I tried setting up a configuration file to specify the relationship in code as well as the database but it's still not playing friendly.
The config looks like:
public class CountryEntityConfig : EntityTypeConfiguration<Country>
{
public CountryEntityConfig()
{
this.HasMany(x => x.UserProfiles)
.WithRequired()
.HasForeignKey(FKey => FKey.Country);
}
}
It feels really odd having a list of profiles in the country object am I getting some of the basics completely wrong with this? Any input appreciated.
EDIT:
I've revisted some classes and made a few changes, in the user profile I now have:
public int CountryId { get; set; }
public virtual Country Country { get; set; }
Country.cs
public class Country
{
public int CountryId { get; set; }
[StringLength(2)]
public string CountryCode { get; set; }
[StringLength(100)]
public string CountryName { get; set; }
public virtual ICollection<UserProfile> UserProfiles { get; set; }
}
And the database now contains two CountryId related fields in the UserProfile table with relationships to the Country table and I still get the original error message.
TIA,
The No mapping error is probably because you did not add the Country entity to the DbContext.
In the AccountModels.cs file, you will find a UsersContext that derives from DbContext, in there you must add a DbSet<Country>.
There are, however, other issues here. For instance, you are creating your Country table with a Key, and EF by default will give the Country table an autogenerated identity column. This means you can't insert a value for the ID, it will be auto generated.
Country is probably a lookup table anyways that you will populate with country values. So you would probably just set CountryId to the value of the country you want

Bind comments to my entites in EF

I´am just in the beginning of creating a comment system for my website. I´am using EF and I want to bind a few of my tables to the Comments table. We can say that I have a Car entity and a Bike entity in two separate tables, and I would like to bind a collection of comments of these two tables.
In my mind I have a picture that the comments table would contain:
CommentID | EntityID | CommentText
1 Bike_2 Hello world..
2 Car_2 --
3 Bike_3 --
Am I thinking right? How do a setup this with entity framework?
Best regards.
(The following is for Entity Framework 4.1 to 4.3.1 and Code-First/DbContext.)
The type of mapping which comes closest to your idea is Table-per-Type (TPT) inheritance mapping. It would look like this:
public abstract class EntityWithComments
{
public int Id { get; set; }
public ICollection<Comment> Comments { get; set; }
}
public class Comment
{
public int Id { get; set; }
public string CommentText { get; set; }
public int EntityId { get; set; }
public EntityWithComments Entity { get; set; }
}
public class Car : EntityWithComments
{
public string Manufacturer { get; set; }
public string Color { get; set; }
}
public class Bicycle : EntityWithComments
{
public int Weight { get; set; }
public bool HasThreeWheels { get; set; }
}
EntityWithComments is a base class for Car and Bicycle and perhaps other entities. Then you have a derived DbContext class:
public class MyContext : DbContext
{
public DbSet<EntityWithComments> EntitiesWithComments { get; set; }
public DbSet<Comment> Comments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Car>()
.ToTable("Cars");
modelBuilder.Entity<Bicycle>()
.ToTable("Bicycles");
}
}
As a result you have four tables in the database:
A Comments table which looks like your proposal but EntityId won't refer directly to the Cars and Bicycles tables. Instead it refers to the base type table EntitiesWithComments.
A table EntitiesWithComments representing the abstract base class and which only has a single column, namely the Id column.
A table Cars with a one-to-one shared primary key constraint between the Id and the Id in table EntitiesWithComments
A table Bicycles with a one-to-one shared primary key constraint between the Id and the Id in table EntitiesWithComments
You can then - for example - load all blue cars:
using (var ctx = new MyContext())
{
var blueCars = ctx.EntitiesWithComments.OfType<Car>()
.Where(c => c.Color == "Blue")
.ToList();
}
Because the EntitiesWithComments base table does not contain any column except the Id there is no join between the tables necessary. The generated SQL looks like this and only touches the table for the derived type:
SELECT
'0X0X' AS [C1],
[Extent1].[Id] AS [Id],
[Extent1].[Manufacturer] AS [Manufacturer],
[Extent1].[Color] AS [Color]
FROM [dbo].[Cars] AS [Extent1]
WHERE N'Blue' = [Extent1].[Color]
(I guess, the strange 0X0X value in this query is kind of a type descriptor EF uses to check if the returned rows are really cars, but I am not sure.)
If you want to load all bicycles with three wheels including their comments the following query works:
using (var ctx = new MyContext())
{
var bicyclesWithThreeWheelsWithComments = ctx.EntitiesWithComments
.Include(e => e.Comments)
.OfType<Bicycle>()
.Where(b => b.HasThreeWheels)
.ToList();
}

Resources