Entity Framework - Fluent API - Creating Weird Columns PK : FK FK - c#

My project is Database First and I'm using Entity Framework 6.2.0.
So I have following classes in simple illustration:
Person [1] [n] Link_Table [n] [1] Area
====== ========== ====
ID ID ID
PersonID (FK)
AreaID (FK)
Now when using following code it will through me an inner exception of "invalid column name 'Area2_ID'".
Db_Context db = new Db_Context();
// getting all links with specific "personID"
List<Link_Table> links = db.Link_Table.Where(item => item.PersonID == personID).ToList();
// now getting all areas
List<Area> areas= new List<Area>();
foreach (Link_Table link in links)
{
// ERROR
areas.Add(db.Area.Where(item => item.ID == link.areaID).First());
}
I've already read a little bit about this problem by another users and the problem should be in the (auto-generated) OnModelCreating.
modelBuilder.Entity<Area>()
.Property(e => e.Description)
.IsUnicode(false);
modelBuilder.Entity<Area>()
.HasOptional(e => e.Area1)
.WithRequired(e => e.Area2);
For whatever reason using EF in code and creating a new Area-Object, it will show me not only "ID"-Property, but also "Area1"- and "Area2"-Property.
Question
How i have to handle it? The columns "Area1_ID" and "Area2_ID" only exist in EF, not in the database. Can I remove these properties or something else to prevent my exception?
EDIT:
My models:
[Table("Area")]
public partial class Area
{
public Guid ID { get; set; }
[StringLength(40)]
public string Name { get; set; }
[Column(TypeName = "text")]
public string Description{ get; set; }
public virtual Area Area1 { get; set; }
public virtual Area Area2 { get; set; }
}
public partial class Link_Table
{
public Guid ID { get; set; }
public Guid? Person_ID { get; set; }
public Guid? Area_ID { get; set; }
public virtual Person Person{ get; set; }
}
[Table("Person")]
public partial class Person
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Person()
{
Link_Table = new HashSet<Link_Table>();
}
public Guid ID { get; set; }
}

This line looks suspect:
areas.Add(db.Area.Where(item => item.ID == link.areaID).First());
item.ID is the index of the link table record, not an area record.
areas.Add(db.Area.Where(item => item.AreaID == link.areaID).First());

When was the last time you ran a database migration? If your model is correct, and your database is saying there are missing fields, then the database doesn't match the code.

Not very clear for me your goal, but you can refactor your code as follow:
this code:
// now getting all areas
List<Area> areas= new List<Area>();
foreach (Link_Table link in links)
{
// ERROR
areas.Add(db.Area.Where(item => item.ID == link.areaID).First());
}
can be replaced with:
var areas=links.Select(link => link.Area).ToList() // if you decide to add a navigation property
or
var areas=db.Areas.Where(area => links?.Any(link=>link.areaID==area.Id)).ToList();
Most problably the error you get was from .First() for the future use .FirstOrDefault()
Please see below an example of code first implementation:
class Program
{
static void Main(string[] args)
{
var db = new ApplicationDbContext();
var person = new Person();
IQueryable<Link> personLinks = db.Links.Where(x => x.PersonId == person.Id);
List<Area> personAreas = personLinks.GroupBy(x => x.Area).Select(x => x.Key).ToList();
}
}
public class Person
{
public int Id { get; set; }
public ICollection<Link> Links { get; set; }
}
public class Link
{
public int Id { get; set; }
public int PersonId { get; set; }
public Person Person { get; set; }
public int AreaId { get; set; }
public Area Area { get; set; }
}
public class Area
{
public int Id { get; set; }
public ICollection<Link> Links { get; set; }
}
public class ApplicationDbContext : DbContext
{
public DbSet<Person> Persons { get; set; }
public DbSet<Area> Areas { get; set; }
public DbSet<Link> Links { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Link>()
.HasRequired(x => x.Person)
.WithMany(x => x.Links)
.HasForeignKey(x => x.PersonId)
.WillCascadeOnDelete(true);
modelBuilder.Entity<Link>()
.HasRequired(x => x.Area)
.WithMany(x => x.Links)
.HasForeignKey(x => x.AreaId)
.WillCascadeOnDelete(false);
base.OnModelCreating(modelBuilder);
}
}
here you can lear more about entity framework code first follow this link

Related

How to define a hierarchal table using EF core

In the application I am working on, we have a users table, TblUser. Users within this table may belong to a single, parent user. A parent user may have multiple child users.
This relationship is maintained within a table called TblUserMapping with two columns, ParentUserId and ChildUserId corresponding with the parent's and child's TblUser.Id value. TblUser.Id is an auto-incrementing value.
How can I define this within EF Core, and would it be possible to Insert a ChildUser into TblUser and use the auto-generated Id value to also create a TblUserMapping record?
Right now I have:
[Table("TblUser")]
public class TblUser
{
public TblUser()
{
ChildUsers = new List<TblUserMapping>();
}
public int Id { get; set; }
public string UserName { get; set; }
public virtual ICollection<TblUserMapping> ChildUsers { get; set; }
public virtual TblUserMapping ParentUser { get; set; }
}
[Table("TblUserMapping")]
public class TblUserMapping
{
public TblUserMapping()
{
}
public int ChildUserId { get; set; }
public int ParentUserId { get; set; }
public virtual TblUser ChildUser { get; set; }
public virtual TblUser ParentUser { get; set; }
}
public class TblUserMapping : IEntityTypeConfiguration<TblUser>
{
public void Configure(EntityTypeBuilder<TblUser> entity)
{
entity.Property(e => e.Id).ValueGeneratedOnAdd();
entity.Property(e => e.UserName)
.IsRequired()
.IsUnicode(false);
}
}
public class TblUserMappingMapping : IEntityTypeConfiguration<Entities.TblUserMapping>
{
public void Configure(EntityTypeBuilder<Entities.TblUserMapping> entity)
{
entity.HasKey(e => e.ChildUserId);
entity.Property(e => e.ChildUserId)
.IsRequired();
entity.Property(e => e.ParentUserId)
.IsRequired();
entity.HasOne(e => e.ParentUser)
.WithMany(e => e.ChildUsers)
.HasForeignKey(e => e.ParentUserId);
entity.HasOne(e => e.ChildUser)
.WithOne(e => e.ParentUser)
.HasForeignKey<TblUser>(e => e.Id);
}
}
But this isn't working as I had hoped when I do:
var userInformation = await _context
.Users
.Include(entity => entity.ChildUsers)
.ThenInclude(entity => entity.ChildUser)
.Where(s => s.UserName == userName)
.FirstOrDefaultAsync();
var ChildUser = new TblUser
{
UserName = userModel.UserName,
ParentUser = new TblUserMapping()
{
ParentUser = userInfo
}
};
_context.Users.Add(ChildUser);
await _context.SaveChangesAsync();
You can attach navigation properties, and Entity Framework will populate the ids automatically when it creates them. The example you have given should work, you might need to show us how you are getting userInfo before we can see what's going on.
That being said, instead of keeping a separate mapping table, I would have each child user refer directly to their parent:
[Table("TblUser")]
public class TblUser
{
public int Id { get; set; }
public string UserName { get; set; }
public int? ParentId { get; set; }
public TblUser Parent { get; set; }
// Lazy-loading is not enabled by default in EF Core, so you don't need the 'virtual' keyword
// Also, if the initialization of a member does not depend on constructor arguments, I
// prefer this syntax instead of doing it in the constructor
public ICollection<TblUser> Children { get; set; } = new List<TblUser>();
}
You can use InverseProperty attribute in your model:
[Table("TblUser")]
public class TblUser
{
public TblUser()
{
ChildUsers = new List<TblUserMapping>();
}
public int Id { get; set; }
public string UserName { get; set; }
[InverseProperty("ChildUser")]
public virtual ICollection<TblUserMapping> ChildUsers { get; set; }
[InverseProperty("ParentUser")]
public virtual TblUserMapping ParentUser { get; set; }
}
And in other model:
[Table("TblUserMapping")]
public class TblUserMapping
{
public TblUserMapping()
{
}
public int ChildUserId { get; set; }
public int ParentUserId { get; set; }
[ForeignKey("ChildUserId")]
public virtual TblUser ChildUser { get; set; }
[ForeignKey("ParentUserId")]
public virtual TblUser ParentUser { get; set; }
}
As you see I defined these relations with attributes that means no need to came in your configuration.

Many to many relationship issue with column name - EF Core

I have a problem with many to many relationship in EF core. I have the following models:
public class Institution
{
[Key]
public int Id { get; set; }
[JsonIgnore]
public virtual ICollection<InstitutionDepartment> InstitutionDepartments { get; set; }
}
public class InstitutionDepartment
{
[Column("Institution_Id")]
public int InstitutionId { get; set; }
[Column("Department_Id")]
public int DepartmentId { get; set; }
public Institution Institution { get; set; }
public Departments Department { get; set; }
}
public class Departments
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
public bool Published { get; set; }
[JsonIgnore]
public virtual ICollection<InstitutionDepartment> InstitutionDepartments { get; set; }
}
I followed the many tutorials explaining how to correctly map these classes:
modelBuilder.Entity<InstitutionDepartment>()
.HasKey(x => new { x.DepartmentId, x.InstitutionId});
modelBuilder.Entity<InstitutionDepartment>()
.HasOne(pt => pt.Institution)
.WithMany(p => p.InstitutionDepartments)
.HasForeignKey(pt => pt.InstitutionId);
modelBuilder.Entity<InstitutionDepartment>()
.HasOne(pt => pt.Department)
.WithMany(t => t.InstitutionDepartments)
.HasForeignKey(pt => pt.DepartmentId);
I wrote my query:
var institutions = _context.Institutions
.Include(i => i.InstitutionDepartments)
.ThenInclude(id => id.Department);
But no matter what I do, I get the following error:
Invalid column name 'InstitutionId'.
Can anyone tell me what I'm doing wrong here ? D:
Note I don't get the error if I don't write the .ThenInclude(id => id.Department); part.
But that make the data incomplete
The issue came from a line of code that I didn't deem relevant at the time (I'll know better next time)
This was in the Institution model without the [NotMapped] annotation:
[NotMapped]
public IEnumerable<Departments> Departments
=> InstitutionDepartments?.Select(o => o.Department);
This was causing EF to look for a missing One to Many relationship between Institution and Department

Wrong field in table UsersOrders

I'm trying to add some entities using EntityFramework. I need the same model as in image
I created 3 classes:
public class UsersOrders : Entity
{
public int Order_ID { get; set; }
public int User_ID { get; set; }
public virtual User User { get; set; }
public virtual Order Order { get; set; }
}
public class User : Entity
{
public int User_ID { get; set; }
public string Surname { get; set; }
public string Name { get; set; }
public string Patronymic { get; set; }
public string Phone { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public virtual ICollection<UsersOrders> Orders { get; set; }
}
public class Order : Entity
{
public int Order_ID { get; set; }
public virtual Address Address { get; set; }
public virtual User User_ID_Courier { get; set; }
public virtual ICollection<UsersOrders> Users { get; set; }
}
Using FluentAPI trying to set primary keys in my DBContext:
builder.Entity<UsersOrders>()
.HasKey(od => new {od.Order_ID});
It works, but why in DB this field "Order_Order_ID" appears? I'm not sure but I really didn't set this field.
Thanks for any help!
UPD: OnModelCreating
protected override void OnModelCreating(DbModelBuilder builder)
{
//OrderedDishes
builder.Entity<OrderedDishes>().HasKey(od => new { od.Order_ID, od.Dish_ID, od.Number });
builder.Entity<OrderedDishes>().HasRequired(od => od.Order).WithMany(od => od.Dishes).HasForeignKey(od => od.Order_ID);
builder.Entity<OrderedDishes>().HasRequired(od => od.Dish).WithMany(od => od.Orders).HasForeignKey(od => od.Dish_ID);
//OrderStatus
builder.Entity<OrderStatus>().HasKey(os => new { os.Order_ID, os.StatusType_ID });
builder.Entity<OrderStatus>().HasRequired(os => os.Order);
builder.Entity<OrderStatus>().HasRequired(os => os.StatusType);
//DishStatus
builder.Entity<DishStatus>().HasKey(os => new { os.Order_ID, os.Dish_ID, os.Number, os.StatusType_ID });
builder.Entity<DishStatus>().HasRequired(os => os.OrderedDishes);
builder.Entity<DishStatus>().HasRequired(os => os.StatusType);
//user
builder.Entity<UsersOrders>().HasKey(od => new { od.Order_ID });
builder.Entity<UsersOrders>().HasRequired(os => os.User);
builder.Entity<UsersOrders>().HasRequired(os => os.Order);
//PriceOfDish
builder.Entity<PriceOfDish>().HasKey(t => new { t.Dish_ID, t.DateTime });
}
You have the wrong field because you rely on the Code First's convention but your are not following it correctly for the UsersOrders entity. Assuming that Id is the primary key of User class then it will look for UserId (not User_Id) which is not in your UsersOrders entity. The same goes for Order navigational property in UsersOrders.
To fix this you have to follow the convetion by refactoring your UsersOrders and use data annotations like this: (If you use this solution you must remoeve configuration for UsersOrders entity in your OnModelCreating implementation.
public class UsersOrders
{
[Key]
public int OrderID { get; set; }
[Key]
public int UserID { get; set; }
public virtual User User { get; set; }
public virtual Order Order { get; set; }
}
Or in your OnModelCreating implementation make the following changes for UsersOrders entity like this:
modelBuilder.Entity<UsersOrders>().HasKey(od => new { od.Order_ID, od.User_ID });
modelBuilder.Entity<UsersOrders>().HasRequired(os => os.User).WithMany(p => p.Orders).HasForeignKey(p => p.User_ID);
modelBuilder.Entity<UsersOrders>().HasRequired(os => os.Order).WithMany(p => p.Users).HasForeignKey(p => p.Order_ID);
In the two solutions, note that UsersOrders use composite keys using Order_Id and User_Id not only Order_ID.

EF 6 table mapping error

I'm running into an error trying to save some data in my MVC app. We're using code first.
I'm saving my data like this:
var fieldDefinition = db.CustomFields
.FirstOrDefault(x => x.CustomFieldId == thisResp.CustomFieldId);
var newData = new CustomData
{
ProjectId = new Guid("280288D7-7630-E511-8420-00215E466552"),
CustomFieldId = thisResp.CustomFieldId
};
if (fieldDefinition.AllowMultiple)
{
var values = thisResp.Value.Split(',');
foreach (var thisValue in values)
{
var newMulti = new CustomDataMulti
{
CustomDataId = newData.CustomDataId,
CustomValue = thisValue
};
db.CustomDataMulti.Add(newMulti);
}
}
db.CustomData.Add(newData);
db.SaveChanges();
However, I get this message:
Unable to determine the principal end of the 'PublicationSystem.Model.CustomData_CustomDataMultis' relationship. Multiple added entities may have the same primary key.
My classes are set up like this:
public partial class CustomData : BaseEntity
{
[Key]
public int CustomDataId { get; set; }
public Guid ProjectId { get; set; }
public Guid CustomFieldId { get; set; }
//...
public virtual ICollection<CustomDataText> CustomDataTexts { get; set; }
public virtual ICollection<CustomDataMulti> CustomDataMultis { get; set; }
}
CustomDataMapping.cs
public CustomDataMapping()
{
//Primary key
HasKey(t => t.CustomDataId);
//Constraints
Property(e => e.CustomValue).IsUnicode(false);
HasMany(e => e.CustomDataTexts)
.WithRequired(e => e.CustomData)
.WillCascadeOnDelete(false);
HasMany(e => e.CustomDataMultis)
.WithRequired(e => e.CustomData)
.WillCascadeOnDelete(false);
ToTable("CustomData");
}
CustomDataMulti.cs
[Table("CustomDataMulti")]
public partial class CustomDataMulti : BaseEntity
{
[Key]
public int CustomDataMultiId { get; set; }
public int CustomDataId { get; set; }
[Required]
[StringLength(150)]
public string CustomValue { get; set; }
public virtual CustomData CustomData { get; set; }
}
CustomDataMultiMapping.cs
public CustomDataMultiMapping()
{
//Primary key
HasKey(t => t.CustomDataMultiId);
//Constraints
Property(e => e.CustomValue).IsUnicode(false);
ToTable("CustomDataMulti");
}
I'm not sure what I'm doing wrong.
EntityFramework doesnt understand the principal end of the relationship between the following entities :-
CustomData and CustomDataMulti.
This is because the relationship between the two entities must have one side of the relationship that's constant.
In this case, A CustomData entity can have multiple CustomDataMulti objects. (List).
But can a CustomDataMulti ever exist without belonging to a CustomData object?
What you need to do is change the CustomDataMulti class so that the virtual property for CustomData is Required.
See Below :-
[Table("CustomDataMulti")]
public partial class CustomDataMulti : BaseEntity
{
[Key]
public int CustomDataMultiId { get; set; }
public int CustomDataId { get; set; }
[Required]
[StringLength(150)]
public string CustomValue { get; set; }
[Required]
public virtual CustomData CustomData { get; set; }
}
This is the convention that EntityFramework Uses to determine relationships.
To give a clearer example.
Think of an OrderItem, An OrderItem would always belong to an Order.
An OrderItem without a relevant Order is useless.
The Order entity is the prinicpal end of the relationship.
Hope this helps.

Defining foreign key in EF 4.3.1 Code first using TPH

I have created three different classes and one base class which store different types of addresses.
Base class is postal address which has relation to User (to whom is current address attached) and Post, which contain information about zip code and city.
public class PostalAddress
{
public virtual User User { get; set; }
public DateTime LastUsed { get; private set; }
public string OrientationNumber { get; set; }
public int UserId { get; set; }
public int PostId { get; set; }
public int Id { get; set; }
public virtual Post Post { get; private set; }
public string Street { get; set; }
}
public class Post
{
public Post()
{
InvoicingAddresses = new List<InvoicingAddress>();
ShippingAddresses = new List<ShippingAddress>();
UserAddresses = new List<UserAddress>();
}
public virtual City City { get; set; }
public virtual ICollection<InvoicingAddress> InvoicingAddresses { get; private set; }
public virtual ICollection<ShippingAddress> ShippingAddresses { get; private set; }
public virtual ICollection<UserAddress> UserAddresses { get; private set; }
public int CityId { get; set; }
public int Id { get; set; }
public string ZipCode { get; set; }
}
Class PostalAddress is mapped using class PostalAddressMap
public class PostalAddressMap : EntityTypeConfiguration<PostalAddress>
{
public PostalAddressMap()
{
// Primary Key
HasKey(t => t.Id);
// Properties
// Table & Column Mappings
ToTable("PostalAddress");
Property(t => t.Id).HasColumnName("Id");
Property(t => t.LastUsed).HasColumnName("LastUsed").HasColumnType("datetime2");
Property(t => t.OrientationNumber).HasColumnName("OrientationNumber");
Property(t => t.UserId).HasColumnName("UserId");
Property(t => t.PostId).HasColumnName("PostId");
Property(t => t.Street).HasColumnName("Street");
// Relationships
HasRequired(t => t.Post).WithMany(t => t.InvoicingAddresses).HasForeignKey(d => d.PostId);
HasRequired(t => t.User)
.WithMany(t => t.UserAddressess)
.HasForeignKey(d => d.UserId);
}
}
Classes InvoicingAddress, ShippingAddress and UserAddress are inherited from PostalAddress class using Table per hierarchy approach. If I want to set relationships using line
HasRequired(t => t.Post).WithMany(t => t.InvoicingAddresses).HasForeignKey(d => d.PostId);
I receive compiler error Cannot implicitly convert type 'System.Collections.Generic.ICollection<InvoicingAddress>' to 'System.Collections.Generic.ICollection<PostalAddress>'. An explicit conversion exists (are you missing a cast?)
Please, can you help me how can I set foreign key between PostalAddress child classes and other TPT types?
Thank you for any helpful answer.
You must either move the PostId and Post properties from the base class PostalAddress to the derived classes InvoicingAddress, etc...
public class InvoicingAddress : PostalAddress
{
//...
public int PostId { get; set; }
public virtual Post Post { get; private set; }
}
...and then use a mapping for the derived classes:
public class InvoicingAddressMap : EntityTypeConfiguration<InvoicingAddress>
{
public InvoicingAddressMap()
{
HasRequired(t => t.Post)
.WithMany(t => t.InvoicingAddresses)
.HasForeignKey(d => d.PostId);
}
}
Or you must use a single collection in Post for the base class:
public virtual ICollection<PostalAddress> Addresses { get; private set; }
Then you can use your original mapping.
The downside of the latter approach is that when you use eager or lazy loading all PostalAddresses will be loaded and you can't control which type of address you want to load. After the addresses have been loaded you could filter by type in memory though:
var invoicingAddresses = post.Addresses.OfType<InvoicingAddress>();
With explicit loading you can filter too:
var post = context.Posts.Single(p => p.Id == 1);
context.Entry(post).Collection(p => p.Addresses).Query()
.OfType<InvoicingAddress>().Load();
...which would populate the Addresses collection with the InvoicingAddresses only.

Categories

Resources