EF 6 table mapping error - c#

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.

Related

Automapper maping navigation property on a one to one relationship

I'm trying to map a navigation property on a one to one relationship but I'm getting null values, here are my entities
public class Client
{
public int ClientId { get; set; }
public string Name { get; set; }
public int BranchId { get; set; }
public Branch Branch{ get; set; }
}
public class Branch
{
public int BranchId { get; set; }
public string Name { get; set; }
}
And this is my Dto
public class ClientDto
{
public int ClientId { get; set; }
public string Name { get; set; }
public string BranchName { get; set; }
}
And these are the mapping configurations I have tried
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<Client, ClientDto>().
ForMember(
dest => dest.BranchName,
opt => opt.MapFrom (src => src.Branch.Name)
);
}
}
and
CreateMap<Client, ClientDto>().IncludeMembers(src => src.Branch);
CreateMap<Branch, ClientDto>().ForMember(
dest => dest.BranchName,
opt => opt.MapFrom(b => b.Name)
);
in both cases I got nulls on BranchName, thank you.
You need to Include the navigation property on your repository (if using) implementation:
var client = await _context.Clients.Include(c => c.Branch).FirstOrDefaultAsync(c => c.Id == id);
Please make sure your actual object (usually from database using ef) includes the desired navigation property by using the Include – S. M. JAHANGIR
This is the correct answer.

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.

How to add additional navigational properties that won't be persisted?

I have the following model:
public class Order
{
public int Id { get; set; }
public ItemType ItemType { get; set; }
public int ItemId { get; set; }
}
public enum ItemType
{
Customer, Organization, Department, //etc
}
The ItemId is a foreignkey to a separate model/table. The above model maps as-is with the underlying table.
Now I would like to automatically load the related entities when loading an Order.
Is this possible by using composite foreign keys when mapping, as in:
public class Order
{
public int Id { get; set; }
public ItemType ItemType { get; set; }
public int ItemId { get; set; }
public ItemTypeCustomer => ItemType.Customer;
public ItemTypeOrganization => ItemType.Organization;
public ItemTypeDeparment => ItemType.Department;
public Customer Customer { get; set; }
public Organization Organization { get; set; }
public Department Department { get; set; }
}
modelBuilder.Entity<Order>()
.HasOptional(p => p.Customer)
.WithMany()
.HasForeignKey(p => new { p.ItemTypeCustomer , p.ItemId });
Would the above work? Is it even possible to build something like this using code-first?
`using Microsoft.EntityFrameworkCore;
public IEnumerable GetCandidates() { return context.Orders.Include(C=> C.ItemType); }'

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 How to set up a 1:M relationship with Surrogate Keys

I'm trying to use EF6 (via code first) against an existing db. The underlying DB has no FK's in it. The entities are defined with an identity pk, but can also be referenced by a surrogate value (RefId).
Assuming I have:
public class Cart
{
public int Id { get; set; } // PK
public string RefId { get; set; }
public virtual List<CartItem> CartItems { get; set; }
}
public class CartItem
{
public int Id { get; set; } // PK
public string RefId { get; set; }
public string CartRefId {get;set;}
public virtual Cart Cart { get; set; }
}
public class CartMap : EntityTypeConfiguration<Cart>
{
public CartMap()
{
HasKey(t => t.Id);
}
}
public class CartItemMap : EntityTypeConfiguration<CartItem>
{
public CartItemMap()
{
HasKey(t => t.Id);
HasRequired(t => t.Cart)
.WithMany(t => t.CartItems)
.HasForeignKey(t => t.CartRefId);
}
}
How can I tell EF that it should be joining the CartItem to Cart based on
Cart.RefId = CartItem.CartRefId
By default EF will attempt to join
Cart.Id == CartItem.CartRefId
because Cart.Id is the Key of the Cart entity.
I dont think the EF will try this: Cart.Id == CartItem.CartRefId because the types arent the same. So, is not possible to map to have relationships without PK, if you need the Id fields to be autoincrement you could do some like this:
public class Cart
{
public int Id { get; set; }
public string RefId { get; set; } // PK
public virtual List<CartItem> CartItems { get; set; }
}
public class CartItem
{
public int Id { get; set; }
public string RefId { get; set; } // PK
public string CartRefId { get; set; }
public virtual Cart Cart { get; set; }
}
public class CartMap : EntityTypeConfiguration<Cart>
{
public CartMap()
{
HasKey(t => t.RefId);
Property(i => i.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}
}
public class CartItemMap : EntityTypeConfiguration<CartItem>
{
public CartItemMap()
{
HasKey(t => t.RefId);
Property(i => i.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
HasRequired(t => t.Cart)
.WithMany(t => t.CartItems)
.HasForeignKey(t => t.CartRefId);
}
}
If not, other way could be assumes the default mapping with Id PK, then you can use a join query:
var q = from c in db.Carts
join i in db.CartItens on c.RefId equals i.CartRefId
select i;

Categories

Resources