I want to define a one to many relationship on an owned type.
Here in my example,
InboundRequest: principal entity
RequestHistory: owned type
RequestHistoryEntry : Dependency entity
public class InboundRequest : IAggregateRoot
{
public int Id { get; private set; }
public RequestHistory History { get; private set; }
}
public class RequestHistory
{
public IList<RequestHistoryEntry> HistoryEntries { get; set; }
}
public class RequestHistoryEntry
{
public RequestState State { get; private set; }
public DateTime Timestamp { get; private set; }
public int Id { get; set; }
}
builder.Entity<InboundRequest>().OwnsOne(x => x.History);
Unfortunately EF Core gives me the following error:
The relationship from 'RequestHistoryEntry' to
'InboundRequest.History#RequestHistory.HistoryEntries' is not
supported because the owned entity type
'InboundRequest.History#RequestHistory' cannot be on the principal
side.
Is there a way to have a one to many relationship between an owned type and a list of dependencies?
The only way to have a one to many relationship with owned types is if the owned entity is on the many side of the relationship, in which case ef core will map it to a separate table by convention. What you are attempting is per se not possible by design. However, in your particular case, why don't you get rid of RequestHistory altogether and make the list of RequestHistoryEntry as a collection of owned type to your InboundRequest as follows:
public class InboundRequest : IAggregateRoot
{
public int Id { get; private set; }
public IList<RequestHistoryEntry> HistoryEntries { get; set; }
}
public class RequestHistoryEntry
{
public RequestState State { get; private set; }
public DateTime Timestamp { get; private set; }
public int Id { get; set; }
}
builder.Entity<InboundRequest>().OwnsMany(x => x.HistoryEntries);
That should get rid of the error as well as achieve your desired relationship to your dependent entity.
You cannot set a list of objects as a owned type, because the columns of your History table are added to the InboundRequest table. The column count of the table must be fixed and not dynamic.
If you write the following:
public RequestHistoryEntry HistoryEntries { get; set; }
Then your InboundRequest table looks like that (or similar):
| Id | RequestHistory_State | RequestHistory_Timestamp | RequestHistory_Id |
If you are using a List of RequestHistoryEntry, then the column count is not clear.
Therfore you have to use a separate table for it.
Related
I have create this model:
[Table("vwpWeekReportArchive")]
public class WeekReportArchive {
[Column("ID")]
public int Id { get; set; }
// other properties omited
}
[Table("vwpWeekReportArchiveWithActivity")]
public class WeekReportArchiveWithActivity : WeekReportArchive {
[Column("ActivityDate")]
public DateTime ActivityDate { get; set; }
[Column("ExpensePayoutType")]
public ExpensePayoutType ExpensePayoutType { get; set; }
}
In the DbContext, I have declared this DbSets:
public DbSet<WeekReportArchive> WeekReportArchives { get; set; }
public DbSet<WeekReportArchiveWithActivity> WeekReportArchivesWithActivity { get; set; }
When I use context.WeekReportArchivesWithActivity all is working great.
When I use context.WeekReportArchives EFCore creates entities of type WeekReportArchiveWithActivity too. But I want entities of type WeekReportArchiveWithActivity. The reason is, that vwpWeekReportArchive is a view, that I can update and vwpWeekReportArchiveWithActivity cannot be updated. Now for updating the entities on the database, I need to generate entities of type WeekReportArchive. How can I do this, or. what do I have to do, that EFCore will creating Entities of Type WeekReportArchive when I use context.WeekReportArchive?
I am using EF Core 3.1 and I have five Models: Plant, Area, Unit, Schema, and EntitiesSchema.
In the EnititiesSchema, the EntityId may be a foreign key of Plant(PlantId), Area(AreaId), Unit(UnitId) tables.
How to handle this optional Relationship between these tables?
Thanks
public class EntitiesSchema
{
public int Id { get; set; }
public int EntityId { get; set; }
public int TopicId { get; set; }
public int SchemaId { get; set; }
public string Description { get; set; }
public Schema Schema { get; set; }
public ICollection<Topic> Topic { get; set; }
}
No, you can't relate a foreign key to multiple tables. But you can put another property named EntityType to store the type of entity. Then on the client-side, you can handle it. The EntityType can be an enum type.
Another approach is that storing "EntitesSchemaId" in the Plant, Area, Unit, etc models and relate them to the EntitiesSchema.
You can create an intermediary entity to map to different entity types. :
Public class EntityMap
{
public int Id {get;set;}
public string EntityKind {get;set;} // could be "Plant", "Area", "Unit", "Schema"
}
public class Plant
{
public int Id {get;set;}
public string EntityKind {get;set;} = "Plant";
}
public class EntitySchema
{
public int Id {get;set;}
public int EntityMapId {get;set;}
public EntityMap Map {get;set;}
}
The logic to read data from individual schema, has to be implemented in the client,but common properties of the entities can be added in EntityMap.
Here's a similar answer you might want to reference : https://stackoverflow.com/a/53649452/7491048
[Table("FirstClass")]
public class FirstClass {
public int ID { get; set; }
public int SpecialID { get; set; }
public virtual ICollection<SecondClass> SecondClassList { get; set; }
}
[Table("SecondClass")]
public class SecondClass {
public int ID { get; set; }
public int ParentSpecialID { get; set;}
public virtual FirstClass FirstClass { get; set; }
}
I want to map these classes with 'SpecialID' and 'ParentSpecialID'.
Is there an any way to do it?
I want to map these classes with 'SpecialID' and 'ParentSpecialID'. Is there an any way to do it?
Yes, but only in EF Core which supports Alternate Keys.
Well you can sort of do this in EF 6 too, if you declare SpecialID to be the Entity Key of FirstClass. Both ID and SpecialID must have unique indexes in the database, and so either one can be used as the Entity Key. But then all relationships refering to FirstClass must use SpecialID.
I'm fairly new to Entity Framework and feel more in control using the Code-First pattern rather than DB-First.
I was wondering what is more preferred when it comes to programmatically setting up ForeignKey relations between the entities.
Is it better to declare a FK_ property in the class which relates to the another class or is it better to declare an IEnumerable<> property in the class that gets related to?
public class IRelateToAnotherClass
{
...
public int FK_IGetRelatedToByAnotherClass_ID { get; set; }
}
or
public class IGetRelatedToByAnotherClass
{
...
public IEnumerable<IRelateToAnotherClass> RelatedTo { get; set; }
}
It all depends on what type of relationships you want between your entities (one-to-one, one-to-many, many-to-many); but, yes, you should declare foreign key properties. Check out this site for some examples.
Here's a one-to-many for your two classes:
public class IRelateToAnotherClass
{
public int Id { get; set; } // primary key
public virtual ICollection<IGetRelatedToByAnotherClass> IGetRelatedToByAnotherClasses { get; set; }
}
public class IGetRelatedToByAnotherClass
{
public int Id { get; set; } // primary key
public int IRelateToAnotherClassId { get; set; } // foreign key
public virtual IRelateToAnotherClass IRelateToAnotherClass { get; set; }
}
and with some Fluent API mapping:
modelBuilder.Entity<IGetRelatedToByAnotherClass>.HasRequired<IRelateToAnotherClass>(p => p.IRelateToAnotherClass).WithMany(p => p.IGetRelatedToByAnotherClasses).HasForeignKey(p => p.Id);
If I understand what you're asking correctly, you'd want both. You want an int FK property and an object property to use as the navigation property.
The end result would look something like this:
public class Employee
{
[Key]
public int EmployeeID { get; set; }
[ForeignKey("Store")]
public int StoreNumber { get; set; }
// Navigation Properties
public virtual Store Store { get; set; }
}
public class Store
{
[Key]
public int StoreNumber { get; set; }
// Navigation Properties
public virtual List<Employee> Employees { get; set; }
}
If you haven't already, take a look at navigation properties and lazy-loading. Note that EF is clever enough to figure out that an int StoreID property corresponds to an object Store property, but if they are named differently (such as without the ID suffix), you must use the [ForeignKey] annotation.
I was curious if it is possible to map an intermediate table through a containing object.
public class Subscriber : IEntity
{
[Key]
public int Id { get; set; }
public string Name { get; set; }
private ChannelList _subscribedList { get; set; }
public int NumSubscribedChannels { get { return _subscribedList.Count(); } }
}
public class HelpChannel : IEntity
{
[Key]
public int Id { get; set; }
public string name { get; set; }
public string category { get; set; }
public int group { get; set; }
}
I need to have a subscriber table, channel table and an intermediate table to link a subscriber to his/her channels.
Is it possible to map the list that is within the ChannelList object to the Subscriber Model?
I figured that's probably not possible and that I would need to just have a private List for EF to map. But I wasn't sure if EF will do that for private variables. Will it?
I'm hoping that is does because if it has to be public to maintain the encapsulation.
You can map private properties in EF code-first. Here is a nice description how to do it. In your case it is about the mapping of Subscriber._subscribedList. What you can't do is this (in the context's override of OnModelCreating):
modelBuilder.Entity<Subscriber>().HasMany(x => x._subscribedList);
It won't compile, because _subscribedList is private.
What you can do is create a nested mapping class in Subscriber:
public class Subscriber : IEntity
{
...
private ICollection<HelpChannel> _subscribedList { get; set; } // ICollection!
public class SubscriberMapper : EntityTypeConfiguration<Subscriber>
{
public SubscriberMapper()
{
HasMany(s => s._subscribedList);
}
}
}
and in OnModelCreating:
modelBuilder.Configurations.Add(new Subscriber.SubscriberMapping());
You may want to make _subscribedList protected virtual, to allow lazy loading. But it is even possible to do eager loading with Include:
context.Subscribers.Include("_subscribedList");