Given two tables, TrackingTag and TrackingTagStatusUpdate:
public class TrackingTag
{
public int ID { get; set; }
}
public class TrackingTagStatusUpdate
{
public int ID { get; set; }
public int TrackingTagID { get; set; }
public TrackingTag TrackingTag { get; set; }
public int Epoch { get; set; } //32-bit
[MaxLength(32)]
public string APConnectedSSID { get; set; }
}
As there will be many TrackingTagStatusUpdates, I want to add a field "LatestStatusUpdate" to TrackingTag, for performance reasons.
public class TrackingTag
{
public int ID { get; set; }
public int? LatestStatusUpdateID { get; set; }
public TrackingTagStatusUpdate LatestStatusUpdate { get; set; }
}
LatestStatusUpdate is optional, as it may not be set if there are not yet any Status Updates for the Tag.
Entity Framework Core complains that "The child/dependent side could not be determined for the one-to-one relationship between 'TrackingTag.LatestStatusUpdate' and 'TrackingTagStatusUpdate.TrackingTag'.". I then add
modelBuilder.Entity<TrackingTag>().HasOne(x => x.LatestStatusUpdate).WithOne(x => x.TrackingTag).HasForeignKey<TrackingTagStatusUpdate>(x => x.TrackingTagID);
to OnModelCreating(ModelBuilder modelBuilder), however this results in Entity Framework Core creating a relationship with a Unique constraint, which will not work as there will be many TrackingTagStatusUpdate with the same TrackingTagID.
How do I do this correctly?
This seems to have worked:
public class TrackingTag
{
public int ID { get; set; }
[ForeignKey(nameof(LatestStatusUpdate))]
public int? LatestStatusUpdateID { get; set; }
public TrackingTagStatusUpdate LatestStatusUpdate { get; set; }
}
I'm not sure how to achieve the same with the Fluent API though.
Related
I'm working on a trucking API using Entity Framework (EF) Core. Basic CRUD operations are working fine using the repository pattern. There is an error in
configurations I am implementing, however.
I want to obtain multiple trailers and trucks associated with single load, reflecting the one-to-many relationship.
public class LoadConfiguration : IEntityTypeConfiguration<Load>
{
public void Configure(Microsoft.EntityFrameworkCore.Metadata.Builders.EntityTypeBuilder<Load> builder)
{
builder.Property(p=>p.Id).IsRequired();
builder.HasOne(t=>t.Customer).WithMany().HasForeignKey(p=>p.CustomerId);
builder.Property(p=>p.LoadedFrom).IsRequired();
builder.HasMany(p=>p.Trailer).WithOne().HasForeignKey(t=>t.TrailerId);
builder.HasMany(p=>p.Truck).WithOne().HasForeignKey(t=>t.TruckId);
builder.Property(p=>p.Destination).IsRequired();
}
}
public class Truck:BaseEntity
{
public int PlateNo { get; set; }
public string ModelName { get; set; }
public Location StateCode { get; set; }
public int PollutionCertificateValidity { get; set; }
public int DateOfPurchase { get; set; }
public int FitnessCertificateValidity { get; set; }
}
public class Load:BaseEntity
{
public Customer Customer { get; set; }
public int CustomerId { get; set; }
public string LoadedFrom { get; set; }
public Trailer Trailer { get; set; }
public int TrailerId { get; set; }
public Truck Truck { get; set; }
public int TruckId { get; set; }
public string Destination { get; set; }
}
public class Trailer:BaseEntity
{
public int TrailerCapacity { get; set; }
public Truck Truck { get; set; }
public int TruckId { get; set; }
}
public class BaseEntity
{
public int Id { get; set; }
}
A one-to-many relationship is defined by using navigation collections, that has the capacity to hold many Trucks and Trailers. You can choose the collection type freely, but I would suggest ICollection generic type.
Modify your Load class as follows:
public class Load:BaseEntity
{
public Customer Customer { get; set; }
public int CustomerId { get; set; }
public string LoadedFrom { get; set; }
public string Destination { get; set; }
// navigation collections
public ICollection<Trailer> Trailers { get; set; }
public ICollection<Truck> Trucks { get; set; }
}
You will then be able to set up the relationship in your LoadConfiguration class by using
the pluralized name:
builder.HasMany(p=>p.Trailers).WithOne();
builder.HasMany(p=>p.Trucks).WithOne();
.. even though EF Core will be smart enough to figure out the relation by convention so the fluent configuration is redundant.
I have the following two entities/models:
public class Achievement
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<User> Users { get; set; }
}
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public ICollection<Achievement> Achievements { get; set; }
}
How to make a join table in Entity Framework Core using the two entities above? Is what I'm doing suffiecient for Entity Framework Core to know the relation and make a join table for it?
I have Many-To-Many relation and I want Entity Framework Core to create a join table like:
public class UserAchievement
{
public int Id { get; set; }
public int UserId { get; set; }
public int AchievementId { get; set; }
}
I did some more research and found that for now there is currently no conventional way to let Entity Framework Core automatically create a join entity for many-to-many relationships, however in the future this might possible.
For now we have to create our own join entity and configure it using Fluent API.
The join entity:
public class UserAchievement
{
public int UserId { get; set; }
public User User { get; set; }
public int AchievementId { get; set; }
public Achievement Achievement { get; set; }
}
Other entities:
public class Achievement
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<UserAchievement> UserAchievements { get; set; }
}
public class User
{
public int Id { get; set; }
public string Username { get; set; }
public ICollection<UserAchievement> UserAchievements { get; set; }
}
and then configure it using Fluent API in DbContext:
public class DataContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserAchievement>().HasKey(ua => new { ua.UserId, ua.AchievementId });
}
public DbSet<User> Users { get; set; }
public DbSet<Achievement> Achievements { get; set; }
public DbSet<UserAchievement> UserAchievements { get; set; }
}
I have the following 3 (simplified) classes setup in entity framework
public class Service
{
[Key]
public int ServiceId { get; set; }
public int WorkformId { get; set; }
[ForeignKey("WorkformId")]
public virtual Workform Workform { get; set; }
}
public class Workform
{
[Key]
public int WorkformId { get; set; }
[ForeignKey("WorkformId")]
public virtual ICollection<FieldMap> FieldMaps { get; set; }
}
public class FieldMap
{
[Key]
public int FieldMapId{ get; set; }
public int WorkformId{ get; set; }
}
And if I try to do something like this
var service = db.Services.Include("Workform.FieldMaps")
.FirstOrDefault(p => p.ServiceId == serviceId);
service.Workform.FieldMaps is pulling the entire collection, not just the records with the matching relational id.
Am I missing some here?
I want that UserMessage entity point with ReciverIdFK to SharedManWoman entity and with SenderIdFK to another SharedManWoman entity
public class SharedManWoman
{
public List<UserMessage> UserMessages { get; set; }
}
public class UserMessage
{
[Key]
public long Id { get; set; }
[ForeignKey("UserMessages")]
public long SenderIdFK { get; set; }
[InverseProperty("UserMessages")]
public virtual SharedManWoman UserMessages { get; set; }
[ForeignKey("UserMessages")]
public long ReciverIdFK { get; set; }
[InverseProperty("UserMessages")]
public virtual SharedManWoman UserMessages { get; set; }
}
Well, first of all, you can't have two properties with the same name.
But, using the following fluent api on at least one of the properties should do the job:
modelBuilder.Entity<UserMessage>()
.HasOptional(t => t.UserMessages)
.WithMany()
.WillCascadeOnDelete(false);
EDIT
If you want to do it with annotations, the following should do the job. Note that your SharedManWoman object needs an Id. Also, as it stands, the SharedManWoman's pretty much useless since it doesn't have any properties of its own.
public class SharedManWoman {
public long Id { get; set; }
[InverseProperty("Sender")]
public ICollection<UserMessage> SenderMessages { get; set; }
[InverseProperty("Receiver")]
public ICollection<UserMessage> ReceiverMessages { get; set; }
}
public class UserMessage {
[Key]
public long Id { get; set; }
// Note that these are NULLABLE
public long? SenderIdFK { get; set; }
public long? ReceiverIdFK { get; set; }
[ForeignKey("SenderIdFK")]
public virtual SharedManWoman Sender { get; set; }
[ForeignKey("ReceiverIdFK")]
public virtual SharedManWoman Receiver { get; set; }
}
More Info Here
I want to implement many one-to-zero-or-one relationships in one entity, but I am having problems getting it to work then generating the migration for it.
public class Invoice
{
public int Id { get; set; }
public int? MorningExpenseId { get; set; }
public int? EveningExpenseId { get; set; }
public Expense MorningExpense { get; set; }
public Expense EveningExpense { get; set; }
}
public class Expense
{
public int Id { get; set; }
public int InvoiceId { get; set; }
public Invoice Invoice { get; set; }
}
modelBuilder.Entity<Invoice>()
.HasOptional<Expense>(p => p.MorningExpense)
.WithRequired(g => g.Invoice);
modelBuilder.Entity<Invoice>()
.HasOptional<Expense>(p => p.EveningExpense)
.WithRequired(g => g.Invoice);
But I am getting an error of Schema specified is not valid. Errors: The relationship '...' was not loaded because the type '...' is not available..
I also was experimenting with using a primary composite key in the ´Expense´ class like:
public enum ExpenseType { Morning, Evening };
public class Expense
{
public int Id { get; set; }
public ExpenseType ExpenseType { get; set; }
public Invoice Invoice { get; set; }
}
But also no luck with getting it to work. How this should be implemented using Fluent API?
In Entity framework, appliation types must match Database types. Relationships must have the virtual keywork.
You must code like this
public class Invoice
{
public int Id { get; set; }
public int MorningExpenseId { get; set; }
public int EveningExpenseId { get; set; }
public virtual Expense MorningExpense { get; set; }
public virtual Expense EveningExpense { get; set; }
}
public class Expense
{
public int Id { get; set; }
public int InvoiceId { get; set; }
public virtual Invoice Invoice { get; set; }
}