I'm creating a EF5 entity model with the designer (VS2012), and used the EF5 DbContext generator as code generation item.
My model contains an entity deriving from another (not abstract).
So let's say the base entity is called BaseEntity, and the derived entity is DerivedEntity.
Now I see in the generated context class, that there is no
Public DbSet<DerivedEntity> DerivedEntities { get; set; }
defined.
Only
Public DbSet<BaseEntity> BaseEntities { get; set; }
is defined.
Is this normal ? And if yes, how do I query the derived entities in linq ?
I'm used to query like this:
using(var ctx = new EntityContainer)
{
var q = from e in ctx.DerivedEntities <-- but this is now not possible since it doesn't exist
select e;
return q.ToList();
}
Thanks for replying.
EDIT:
As requested, generated classes posted:
public partial class Scheduling
{
public int Id { get; set; }
public string Subject { get; set; }
public System.DateTime BeginDate { get; set; }
public System.DateTime EndDate { get; set; }
}
public partial class TeamScheduling : Scheduling
{
public int TeamId { get; set; }
public Nullable<int> AssignmentId { get; set; }
public virtual Team Team { get; set; }
public virtual Assignment Assignment { get; set; }
}
public partial class EntityContainer : DbContext
{
public EntityContainer()
: base("name=EntityContainer")
{
}
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
throw new UnintentionalCodeFirstException();
}
public DbSet<Team> Teams { get; set; }
public DbSet<Location> Locations { get; set; }
public DbSet<Country> Countries { get; set; }
public DbSet<Assignment> Assignments { get; set; }
public DbSet<ProductType> ProductTypes { get; set; }
public DbSet<AssignmentPreference> AssignmentPreferences { get; set; }
public DbSet<Scheduling> Schedulings { get; set; }
}
As you see, the EntityContainer class does not contain
public DbSet<TeamScheduling> TeamSchedulings { get; set; }
This is expected when you use inheritance the way you have. context.Schedulings contains both Scheduling objects and TeamScheduling objects. You can get the TeamScheduling objects only by asking for context.Schedulings.OfType<TeamScheduling>(). Note that you cannot meaningfully use context.Schedulings.OfType<Scheduling>() to get the others: that will also include the TeamScheduling objects.
You could alternatively try context.Set<TeamScheduling>(), but I'm not entirely sure that will work.
If your intention is to have two tables come up, say a parent Scheduling entity as well as a child TeamScheduling entity that has a foreign key back to the Scheduling entity, consider using a Table-per-Type (TPT) mapping as discussed here.
In essence, you should modify your "OnModelCreating" method to have the following code:
modelBuilder.Entity<TeamScheduling>().ToTable("TeamScheduling");
This explicitly tells EF that you want to have the TeamScheduling subclass to be represented as its own table. Querying it via LINQ would be simple as you would be able to do something like the following:
var teamScheds = context.Set<TeamScheduling>().Where(s => s.Id == 1).FirstOrDefault();
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'm trying to make EfCore ignore a wrapping object similarly to the owned type concept.
How can I turn this object:
public class Entity
{
public Guid Id { get; set; }
public object SomeProperty { get; set; }
public ICollection<Item> Items { get; set; }
public ICollection<OtherItem> OtherItems { get; set; }
}
public class Item
{
public Entity Entity { get; set; }
public Guid EntityId { get; set; }
}
public class OtherItem
{
public Entity Entity { get; set; }
public Guid EntityId { get; set; }
}
Into this object
public class Entity
{
public Guid Id { get; set; }
public Aggregate Aggregate { get; set; } // This should not be mapped to the Database but only the properties
}
[Owned] // I think this is what i'm looking for
public class Aggregate
{
public object SomeProperty { get; set; }
public ICollection<Item> Items { get; set; }
public ICollection<OtherItem> OtherItems { get; set; }
public void SomeUsefulFunction()
{
// do Something Useful on the Aggregate
}
}
I would like EfCore to completely ignore the Aggregate object and threat his properties as if they were from the entity object. I thought the concept of owned entities was exactly this but I get the following error:
Unable to determine the relationship represented by navigation 'Aggregate.OtherItems' of type 'ICollection<OtherItem>'. Either manually configure the relationship, or ignore thi
s property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
Like this?
public class Entity
{
public Guid Id { get; set; }
public Aggregate Aggregate => new Aggregate(this);
protected object SomeProperty { get; set; }
protected ICollection<Item> Items { get; set; }
protected ICollection<OtherItem> OtherItems { get; set; }
}
public class Aggregate
{
public object SomeProperty => _entity.SomeProperty;
public ICollection<Item> Items => _entity.Items;
public ICollection<OtherItem> OtherItems => _entity.OtherItems;
public Aggregate(Entity entity)
{
_entity = entity;
}
public void SomeUsefulFunction()
{
// do Something Useful on the Aggregate
}
}
public class SampleContext : DbContext
{
public DbSet<Entity> Entities { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Entity>().Ignore(c => c.Aggregate);
}
}
PS Just showing how to get, you can easly adapt it to use set too.
I suggest you to check out dotnet reference architecture https://github.com/dotnet-architecture/eShopOnContainers. There Ordering service is build using DDD approach - https://github.com/dotnet-architecture/eShopOnContainers/tree/dev/src/Services/Ordering.
It has good examples how to model Aggregates, Domain Events (Ordering.Domain folder) and also repository implementation along with configuration of EF entities (Ordering.Infrastructure folder).
Also there're couple of solution templates available to avoid typing boilerplate code :
https://github.com/jasontaylordev/CleanArchitecture
https://github.com/ardalis/CleanArchitecture
From microsoft docs :
Applications that follow the Dependency Inversion Principle as well as the Domain-Driven Design (DDD) principles tend to arrive at a similar architecture. This architecture has gone by many names over the years. One of the first names was Hexagonal Architecture, followed by Ports-and-Adapters. More recently, it's been cited as the Onion Architecture or Clean Architecture
Hello everybody and a happy new 2017,
I have the following table-/object structure.
[Table("Table1")]
public class Table1
{
[Key]
public long Table1Id { get; set; }
public virtual ICollection<Table2> ItemsOfTable2 { get; set; }
}
[Table("Table2")]
public class Table2
{
[Key]
public long Table2Id { get; set; }
public long Table1Id { get; set; }
[ForeignKey("Table1Id")]
public virtual Table1 Table1Object { get; set; }
public virtual ICollection<Table3Base> ItemsOfTable3 { get; set; }
[NotMapped]
public virtual ICollection<Table3Red> RedItems
{
get { return this.ItemsOfTable3.OfType<Table3Red>().ToList(); }
}
[NotMapped]
public virtual ICollection<Table3Blue> BlueItems
{
get { return this.ItemsOfTable3.OfType<Table3Blue>().ToList(); }
}
}
[Table("Table3Base")]
public abstract class Table3Base
{
[Key]
public long Table3Id { get; set; }
public long Table2Id { get; set; }
[ForeignKey("Table2Id")]
public virtual Table2 Table2Object { get; set; }
}
public class Table3Red : Table3Base
{
public string SpecialPropertyForRed { get; set; }
}
public class Table3Blue : Table3Base
{
public int SpecialPropertyForBlue { get; set; }
public virtual ICollection<Table4> ItemsOfTable4 { get; set; }
}
[Table("Table4")]
public class Table4
{
[Key]
public long Table4Id { get; set; }
public long Table3Id { get; set; }
[ForeignKey("Table3Id")]
public virtual Table3Blue Table3BlueObject { get; set; }
}
public class MyContext : DbContext
{
public virtual IDbSet<Table1> Table1DbSet { get; set; }
public virtual IDbSet<Table2> Table2DbSet { get; set; }
public virtual IDbSet<Table3Red> Table3RedDbSet { get; set; }
public virtual IDbSet<Table3Blue> Table3BlueDbSet { get; set; }
public virtual IDbSet<Table4> Table4DbSet { get; set; }
}
So, in the middle of this "tree", there is a TPH structure (classes Table3Base, Table3Red, Table3Blue stored in database table "Table3Base"). And we only have IDbSets for Table3Red and Table3Blue, not for Table3Base. Every object has a collection navigation property of the next table objects.
Class Table3Blue has another collection navigation property to items of Table4 objects.
As further (but hopefully irrelevant) information: The default discriminator is mapped to an internal enum:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
// TPH: Map Standard-Discriminator to Enum
modelBuilder.Entity<Table3Base>()
.Map<Table3Red>(m => m.Requires("Typ").HasValue((int)Table3TypEnum.Red))
.Map<Table3Blue>(m => m.Requires("Typ").HasValue((int)Table3TypEnum.Blue));
}
Due to performance issues (loading every single record one by one is very slow; Lazy Loading is active), we want to read this structure from Table1 to Table4 via include like this:
var table1Records = this.m_Context.Table1DbSet
.Include(t => t.ItemsOfTable2)
.Include(t => t.ItemsOfTable2.Select(t2 => t2.ItemsOfTable3))
.Include(t => t.ItemsOfTable2.Select(t2 => t2.ItemsOfTable3.OfType<Table3Blue>().Select(t3 => t3.ItemsOfTable4)))
.ToList();
The first and the second include seem to work, but the third include throws an Argument exception "The Include path expression must refer to a navigation property defined on the type. Use dotted paths for reference navigation properties and the Select operator for collection navigation properties.
Parametername: path".
What am I doing wrong? How do I include Table4-objects on my way to the database?
Kind regards, Mate
This is our workaround. I doubt this is the best solution, so better ways are highly appreciated...
Table3Base gets the collection navigation property to Table4 as a virtual property.
Table3Red (the object without Table4-objects) overrides this property with a getter returning an empty list of Table4 objects and no setter.
Therefore, we can cascade our Include down to Table4 without any type checks. There are no unnecessary records in our PTH database table "Table3Base". So everything is fine, except the clumsy definition of Table3Red with an unused navigation property. :-(
BTW: Include with a long path includes all objects along this path, so the explicit ".Include(A).Include(A.B).Include(A.B.C)" is not necessary; ".Include(A.B.C)" will do the same. The iterating .Include in the code sample is for clarity.
HTH, Mate
I have an abstract object which have two list of abstract objects in it. The Model is being created and the Database looks fine, but I am unable to make the queries I would expect.
The Data model looks something like this
public abstract class Vehicle
{
protected Vehicle()
{
this.CrashIncidents = new List<Incident>();
this.SpeedingIncidents = new List<Incident>();
}
[Key]
public int Id { get; set; }
public virtual ICollection<Incident> CrashIncidents { get; set; }
public virtual ICollection<Incident> SpeedingIncidents { get; set; }
}
public class Car : Vehicle
{
public string Color { get; set; }
}
public class Lorry : Vehicle
{
public int MaxCarryWeight { get; set; }
}
public abstract class Incident
{
[Key]
public int Id { get; set; }
public virtual ICollection<Incident> VehicleCrashIncidents { get; set; }
public virtual ICollection<Incident> VehicleSpeedingIncidents { get; set; }
}
public class CrashIncident : Incident
{
public string Severity { get; set; }
}
public class SpeedingIncident : Incident
{
public string MPHRegistered { get; set; }
}
Any my OnModelCreating in the Context class looks something like this
modelBuilder.Entity<Vehicle>().HasMany<Incident>(o => o.CrashIncident).WithMany(a => a.VehicleCrashIncidents).Map(m => m.MapLeftKey("Id").MapRightKey("VehicleCrashIncidentId").ToTable("VehicleCrashIncident"));
modelBuilder.Entity<Vehicle>().HasMany<Incident>(o => o.SpeedingIncident).WithMany(a => a.VehicleSpeedingIncidents).Map(m => m.MapLeftKey("Id").MapRightKey("VehicleSpeedingIncidentId").ToTable("VehicleSpeedingIncident"));
modelBuilder.Entity<CrashIncident>().ToTable("CrashIncident");
modelBuilder.Entity<SpeedingIncident>().ToTable("SpeedingIncident");
However I am unable to query things like: Get all Vehicles (or concrete classes) with an Incident of Severity of X i.e. something like this:
var problems = context.Vehicle.Where(x => x.CrashIncidents.Any(y => y.Severity == "High");
The problem is the last part of the query (in the y-part) where I am not able to select Severity, only the Properties of the Abstract class is visible. I am unable to determine (and thus Google) if the problem lies with my Data model or with my Query.
Inspired by this:
Issue with many-to-many relationship + TPH inhertitance in Entity Framework 6
I got this working. I removed the specific virtual parts from the Abstract part of my model like this:
public abstract class Vehicle
{
protected Vehicle()
{
this.Incidents= new List<Incident>();
}
[Key]
public int Id { get; set; }
public virtual ICollection<Incident> Incidents{ get; set; }
}
And changed the navigation properties to
public abstract class Incident
{
[Key]
public int Id { get; set; }
public virtual ICollection<Incident> VehicleIncidents { get; set; }
}
In the OnModelCreating I could remove the two "modelBuilder.Entity().HasMany" lines. In the end I could execute this query:
var problems = context.Vehicle.Where(x => x.Incidents.OfType<CrashIncidents>.Any(y => y.Severity == "High");
I must admit I am unsure of whether I tried that specific query before, so I am not sure if it is the changes in my Data model that allowed me to make that query or it was possible all along and I just didn't know it.
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");