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.
Related
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.
I'm trying to map nested ICollection of one of my models to existing Dto, but I'm struggling to Map it properly with AutoMapper
Models:
public class Ingredient : BaseEntity<long>
{
[MaxLength(100)]
public string Name { get; set; }
[ForeignKey("Id")]
public int CustomerId { get; set; }
public bool IsPackaging { get; set; }
public virtual ICollection<ProductIngredient> ProductIngredient { get; set; }
public virtual ICollection<IngredientComposition> IngredientComposition { get; set; }
}
Collection Model:
public class IngredientComposition : BaseEntity<int>
{
[MaxLength(20)]
public string Type { get; set; }
[MaxLength(200)]
public string Key { get; set; }
[MaxLength(200)]
public string Value { get; set; }
}
Dto:
public class IngredientDto
{
public long Id { get; set; }
public DateTime CretedOn { get; set; }
public DateTime UpdatedOn { get; set; }
public string Name { get; set; }
public int CustomerId { get; set; }
public int UsedCount { get; set; }
public bool IsPackaging { get; set; }
public IList<Composition> Ingredients { get; set; }
}
public class Composition
{
public string Type { get; set; }
public string Key { get; set; }
public string Value { get; set; }
}
My maps looks as follows as I'm struggling to properly set "ForMemeber" method(s):
CreateMap<Ingredient, IngredientDto>();
CreateMap<IngredientDto, Ingredient>();
Any help much appropriated!
Thanks
EDIT:
This is how I'm getting data:
return await _context.Ingredients
.Where(i => i.CustomerId ==_userResolverService.GetCustomerId())
.Include(i => i.IngredientComposition)
.Select(i => _mapper.Map<Ingredient, IngredientDto>(i))
.OrderBy(i => i.Name)
.ToListAsync();
First, you must do add CreateMap<IngredientComposition, Composition>(); and after doing this you must do change your Linq Query. You can use AutoMapper.EF6
return _context.Ingredients
.Where(i => i.CustomerId ==_userResolverService.GetCustomerId())
.Include(i => i.IngredientComposition)
.ProjectToList<IngredientDto>();
after use this you donot need use Select.
note: donot forget add _mapper.ConfigurationProvider in ProjectToList
ProjectToList<IngredientDto>(_mapper.ConfigurationProvider);
if you don't set it to get this Exception:
Mapper not initialized. Call Initialize with Appropriate configuration. If you are trying to use mapper instances through a container or otherwise, make sure you do not have any calls to the static Mapper.Map methods, and if you're using ProjectTo or UseAsDataSource extension methods, make sure you pass in the appropriate IConfigurationProvider instance.
more detail.
Update: your properties must have the same name.if you change Dto property Ingredients to IngredientComposition don't need use ForMember.
Actually this worked for me:
Query:
return await _context.Ingredients.Where(i => i.CustomerId == _userResolverService.GetCustomerId())
.Include(sx=>sx.IngredientComposition)
.ProjectTo<IngredientDto>()
.ToListAsync();
Maps:
First of All as you suggested, internal collection mapping then main objects mapping + ForMember which worked once internal objects were mapped
CreateMap<IngredientComposition, Composition>().ReverseMap();
CreateMap<Ingredient, IngredientDto>().ForMember(d => d.Ingredients, opt=>opt.MapFrom(c=>c.IngredientComposition)).ReverseMap();
Thanks for all help!
If you make a map for the child DTO, Automapper is smart enough to figure it out without the ForMember:
CreateMap<IngredientComposition , Composition>()
.ReverseMap(); //Reverse map tells AM to go both ways
CreateMap<Ingredient, IngredientDto>()
.ReverseMap();
// CreateMap<IngredientDto, Ingredient>(); ** Not needed with ReverseMap()
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.
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.
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.