I'm using Entity Framework Code First.
Given these two classes,
public class Parts
{
public int Id {get; set; }
public string Name {get; set; }
}
public class SubParts
{
public int Id {get; set; }
public string Name {get; set; }
}
I've to define a navigation property to get this (ie):
Part1
SubPart1 - Order 1
SubPart5 - Order 2
SubPart2 - Order 3
Part2
SubPart5 - Order 1
SubPart3 - Order 2
SubPart6 - Order 3
SubPart2 - Order 4
... and so on.
My question is: How must I manage the property named Order?
Thanks in advance.
I found the solution here.
In my example, the classes I've define to implement this model are:
public class Parts
{
public int Id {get; set; }
public string Name {get; set; }
public virtual ICollection<PartsSubParts> SubParts {get; set; }
}
public class SubParts
{
public int Id {get; set; }
public string Name {get; set; }
}
public class PartsSubParts
{
public virtual Parts Part {get; set; }
public virtual SubParts SubPart {get; set;}
public int Order {get; set; }
}
Related
I'm new to EF and I'm facing the following situation with my model.
I have the following entities:
public class ForwardingConstruct
{
public int Id {get; set;}
public virtual ICollection<FcPort> FcPorts { get; set; }
/// Other attributes
}
[Owned]
public class FcPort
{
public virtual ICollection<LogicalTerminationPoint> Ltps { get; set; }
/// Other attributes
}
public class LogicalTerminationPoint
{
public int Id {get; set;}
/// Other attributes
}
Based on this answer I know that it is posible to map a one-to-one relation from the owned entity, but my question is if it is posible to create a one-to-many reference from the same entity
Edit: Initially I forgot to mention that I'm using code-first approach and Entity Framework Core.
public class ForwardingConstruct
{
public int Id {get; set;}
[InverseProperty("ForwardingConstruct")]
public virtual ICollection<FcPort> FcPorts { get; set; }
/// Other attributes
}
[Owned]
public class FcPort
{
[InverseProperty("FcPort")]
public virtual ICollection<LogicalTerminationPoint> Ltps { get; set; }
public int ForwardingConstructId { get; set; }
public virtual ForwardingConstruct ForwardingConstruct { get; set; }
/// Other attributes
}
public class LogicalTerminationPoint
{
public int Id {get; set;}
public int FcPortId { get; set; }
public virtual FcPort FcPort { get; set; }
}
You should specify the foreign key in your tables. LogicalTerminationPoint.FcPortId is what tells to entity framework to create a relationship.
Then with the InverseProperty attribute you can specify which property EF can use to load the relevant children.
I've my entity class I retrieve from database:
public class User{
public string Username {get; set;}
public List<IAddress> Addresses {get; set;}
}
public class Address: IAddress{
public string Line1 {get; set;}
public string Line2 {get; set;}
}
public class AddressExtended:Address, IAddress{
public string Line3 {get; set;}
public string Line4 {get; set;}
}
public interface IAddress{
}
I use Automapper to map this entity to the mirrored DTO:
public class UserDto{
[JsonProperty("username")]
public string Username { get; set; }
[JsonProperty("addresses")]
public List<IAddressDto> Addresses { get; set; }
}
public class AddressDto: IAddressDto{
[JsonProperty("line1")]
public string Line1 { get; set; }
[JsonProperty("line2")]
public string Line2 { get; set; }
}
public class AddressExtendedDto:AddressDto, IAddressDto{
[JsonProperty("line3")]
public string Line3 { get; set; }
[JsonProperty("line4")]
public string Line4 { get; set; }
}
public interface IAddressDto{
}
Automapper configuration is the following:
CreateMap<IAddress, IAddressDto>();
CreateMap<Address, AddressDto>();
CreateMap<AddressExtended, AddressExtendedDto>();
The problem is that when I run my application, if in the entity I have 2 addresses and 1 addressExtended, in DTO the Addresses property () is mapped like this:
[
{Proxy<MyProject.Models.Dto.IAddressDto_MyProject_Version=1.0.0.0_Culture=neutral_PublicKeyToken=null>},
{Proxy<MyProject.Models.Dto.IAddressDto_MyProject_Version=1.0.0.0_Culture=neutral_PublicKeyToken=null>},
{Proxy<MyProject.Models.Dto.IAddressDto_MyProject_Version=1.0.0.0_Culture=neutral_PublicKeyToken=null>}
]
The Username property it is correctly valued.
What I miss?
UPDATE
I added a fiddler here:
https://dotnetfiddle.net/ZkUZgp
As per my knowledge, one approach solving the issue is using Construct using below code
cfg.CreateMap<Address, AddressDto>();
cfg.CreateMap<AddressExtended, AddressExtendedDto>();
cfg.CreateMap<IAddress, IAddressDto>().ConstructUsing((IAddress addressDto) =>
{
if (addressDto is AddressExtended) return Mapper.Map<AddressExtendedDto>(addressDto);
return Mapper.Map<AddressDto>(addressDto);
});
Edit 1:
Here is the final answer and it solves your problem
cfg.CreateMap<Address, AddressDto>();
cfg.CreateMap<AddressExtended, AddressExtendedDto>();
cfg.CreateMap<IAddress, IAddressDto>().ConstructUsing((addressDto, ctx) =>
{
var destination = Mapper.Instance.ConfigurationProvider.GetAllTypeMaps()
.First(t => t.SourceType == addressDto.GetType());
return ctx.Mapper.Map(addressDto, addressDto.GetType(), destination.DestinationType) as IAddressDto;
});
Instead of getting destination type using LINQ you can build a dictionary and get from it for faster execution.
Say I have the following models in my database:
public class LetterEntity
{
public int Id {get; set;}
public string Content {get; set;}
public List<Destination> Destinations {get; set;}
public virtual Folder Folder {get; set;}
public int FolderId {get; set;}
}
Now I want to add a new letter the client has made to my database:
public class SendLetterRequest
{
public string Content {get; set;}
public List<int> DestinationsIds {get; set;}
}
public void SaveLetterToDatabase(SendLetterRequest letter)
{
var letterEntity = new LetterEntity
{
Content = letter.Content;
FolderId = 1;
// How to insert the Destinations Ids in a way that I don't have to load all of those destinations to the context?
}
context.Set<LetterEntity>().Add(letterEntity);
context.SaveChanges();
}
I know that if a LetterEntity only had a single Destination object I could just set it's foreign key value and the insert would work (Just like I do with the FolderId).
How is it done when working with List of entities - how to tell EF that those Ids are already in the database, without fetching all of them to the context, so that it doesn't recreate them?
EDIT:
My Destination model -
public void Destination
{
// Manual key
public int Address {get; set;}
public string DestinationName {get; set;}
public string Information {get; set;}
}
Well, as you probably know, there are two ways to define many-to-many replationship in EF.
(1) Implicit link table
This is what you have used. You create explicitly only the two entitities, define the relation via navigation properties/and or model configuration and let EF maintain the so called "link" table. It's easy, but the downside is that you don't have access to that table, so the only way to add related items is to actually load the entities needed and add them to the navigation property collection.
(2) Explicit link table
Here you define explicitly the link entity and configure 2 one-to-many relations. This way you have access and can add related records w/o having the other entities loaded.
For instance, in your case it could be something like this:
Model:
public class LetterEntity
{
public int Id { get; set; }
// ....
public List<LetterDestinationLink> Links { get; set; }
}
public class Destination
{
public int Id { get; set; }
// ....
public List<LetterDestinationLink> Links { get; set; }
}
public class LetterDestinationLink
{
[Key]
[Column(Order = 0)]
public int LetterId { get; set; }
[Key]
[Column(Order = 1)]
public int DestinationId { get; set; }
public LetterEntity Letter { get; set; }
public Destination Destination { get; set; }
}
Context:
public class YourDbContext : DbContext
{
public DbSet<LetterEntity> LetterEntities { get; set; }
public DbSet<Destination> Destinations { get; set; }
public DbSet<LetterDestinationLink> LetterDestinationLinks { get; set; }
}
Use case:
List<int> destinationIds = ...;
var letterEntity = new LetterEntity { ... };
letterEntity.Links = destinationIds.Select(destinationId =>
new LetterDestinationLink { Letter = letterEntity, DestinationId = destinationId })
.ToList();
context.Set<LetterEntity>().Add(letterEntity);
context.SaveChanges();
I have two entities with following relationShip (these entities are taken for example purpose only)
public class Entity
{
public long ID { get; set; }
}
public class Doctor : Entity
{
public string Name {get; set;}
public string sprcialization { get; set;}
public string Icollection<JrDoctor> childDoctors { get; set;}
}
public class JrDoctor : Entity
{
public long? DoctorId { get; set;}
public virtual Doctor Doctor { get; set;}
public long? JuniorDoctorId { get; set;}
[ForeignKey("JuniorDoctorId")]
public virtual Doctor JuniorDoctor { get; set;}
}
this relationship in entityframework is creating an extra column Doctor_Id in JrDoctor table. Why is it so? and how can I avoid it using data annotations.
Here is how EF works - if it sees navigation property (Doctor in your case), then EF understands that both entities are related to each other. Relation in database is defined by foreign keys. So EF generates foreign key with name PropertyName_KeyColumnOfRelatedEntity. That's why you see column Doctor_Id in JrDoctor table.
If you don't want default generated foreign key column, then you should tell EF what it should use instead. That is done via data annotations attributes or fluent configuration. I prefer latter one:
modelBuilder.Entity<JrDoctor>()
.HasOptional(jd => jd.Doctor)
.WithMany(d => d.childDoctors)
.HasForeignKey(jd => jd.DoctorId); // here you tell which column is FK
Data annotations require modification of entity classes. In your case you should add attribute which tells name of FK for navigation property, just as you did for JuniorDoctor:
public class JrDoctor : Entity
{
public long? DoctorId { get; set;}
[ForeignKey("DoctorId")]
public virtual Doctor Doctor { get; set;}
public long? JuniorDoctorId { get; set;}
[ForeignKey("JuniorDoctorId")]
public virtual Doctor JuniorDoctor { get; set;}
}
InverseProperty did the trick.
public class Entity
{
public long ID { get; set; }
}
public class Doctor : Entity
{
public string Name {get; set;}
public string sprcialization { get; set;}
[InverseProperty("Doctor")]
public string Icollection<JrDoctor> childDoctors { get; set;}
}
public class JrDoctor : Entity
{
public long? DoctorId { get; set;}
[ForeignKey("DoctorId")]
public virtual Doctor Doctor { get; set;}
public long? JuniorDoctorId { get; set;}
[ForeignKey("JuniorDoctorId")]
public virtual Doctor JuniorDoctor { get; set;}
}
How can i add a foreign key to a Model (code first)
i have a model Product which has an ID (primary key) which i want to add to my model order like.
public class Order
{
public int ID {get; set;}
[Required]
public int Total {get; set;}
[Required]
public int ProductId{get; set;}
}
but how can i make the ProductId refer to the id of my Product model like a foreign key?
On Stackoverflow there are alot simmilar questions but all with different answers, but they arent working for me. I really hope someone has a solution or can points me in the right direction with an explanation.
You need to add a navigation property:
public virtual Product Product { get; set; }
So the Order class will look like this:
public class Order
{
public int ID {get; set;}
[Required]
public int Total {get; set;}
[Required]
public int ProductId{get; set;}
[Required]
public virtual Product Product { get; set; }
}
Also, strictly speaking the ProductId property isn't needed once you have the virtual Product property.
You could do something like this:
public class Order
{
public int ID {get; set;}
[Required]
public int Total {get; set;}
[Required]
public int ProductId{get; set;}
[ForeignKey("ProductId")]
public virtual Product Product {get; set;}
}