I'm reviewing some code that was written in the EF 4 days because it stands out during performance benchmarking.
The purpose of the code is to materialize an ICollection<MyBaseClass> using Entity Framework (we're now on EF 6.1).
The code exists because references present in specific subclasses aren't materialized when retrieving
public Parent
{
public virtual ICollection<MyBaseClass>() Base { get; set; }
}
from the database, when the actual types stored are subclasses of MyBaseClass.
Example subclass:
public SubA : MyBaseClass
{
public virtual ICollection<Options> Ref1 { get; set; }
}
Currently, the code does something like this:
var parent = ctx.Parents.Include(p => p.Base).Where(...).Single();
LoadSubclasses(parent.Base);
...
private void LoadSubclasses(IEnumerable<MyBaseClass> myBase)
{
foreach (var my in myBase)
{
if (my is SubA)
{
this.Entry(my).Reference("Ref1").Load();
this.Entry((SubA)my).Ref1).Collection("Options").Load();
}
else... // Similar for other subclasses
}
}
Note that ICollection<MyBaseClass>() Base contains a mix of several concrete subclasses. There are generally a few hundred objects in the ICollection.
Is there a more efficient way to materialize Base?
It cannot be said in advance if the performance will be better (sometimes executing a single complex query, especially with sub collection includes may have actually negative impact), but you can minimize the number of database queries to K, where K is the number of subclass types that need additional includes.
You need to base the LoadSubclasses method on IQueryable<TBase> representing all base entities, and execute one query per each subclass type using OfType filter:
private void LoadSubclasses(IQueryable<MyBaseClass> baseQuery)
{
// SubA
baseQuery.OfType<SubA>()
.Include(x => x.Ref1.Options)
.Load();
// Similar for other subclasses
}
The usage with your sample would be:
var parent = ctx.Parents.Include(p => p.Base).Where(...).Single();
LoadSubclasses(ctx.Entry(parent).Collection(p => p.Base).Query());
or more generally:
var parentQuery = ctx.Parents.Where(...);
var parents = parentQuery.Include(p => p.Base).ToList();
LoadSubclasses(parentQuery.SelectMany(p => p.Base));
For those on EF Core 2.1 or later, this feature is now supported out-of-the-box.
Request from 2010:
When in an data model for entity framework has a navigation property
it is not posseble to eager load that navigation property besides when
using OfType<> or when eager loading the derived type itself by a
navigation property.
Response from 2018:
The feature is part of EF Core 2.1, which is currently in preview.
Please create issues in our issue tracker if you find any problems.
Related
I have a simple hierarchy
public abstract class CommunicationSupport
{
public SupportTypeEnum Type { get; set; }
public Country Origin { get; set; } // National or Foreign support
}
public class TelecomSupport : CommunicationSupport
{
public string Number { get; set; }
}
public class PostalSupport : CommunicationSupport
{
public Address Address { get; set; }
}
I plan to use the Table-per-type hierarchy for my DB. So 3 tables will be created, one base and two child using the same PK as the base.
My problem is that I want to be able to update a CommunicationSupport by changing it's type.
Let's say that I create a TelecomSupport, save it and then change it's type to a PostalSupport and save it again (update). The result I expect is for EF to keep the same base record (CommunicationSupport Id) but delete the record in the TelecomSupport table and create a new one in the PostalSupport.
So TelecomSupport and PostalSupport are exclusive and cannot share the same base CommunicationSupport.
How can I do that using EntityFramework 5?
Thanks for your help!
I don't have a good answer, but I can think of four "solutions" that are really workarounds:
Don't use DBMS-computed values for your primary keys (if you already use natural keys, it's fine).
Use DBMS-computed surrogate keys.
Follow something like the state pattern.
Do some evil voodoo with the object state manager.
Update: There seems to be a popular consensus that trying isn't even worth it; most people thus simply use stored procedures instead to work around the problem.
Changing Inherited Types in Entity Framework
Entity Framework: Inheritance, change object type
Changing the type of an (Entity Framework) entity that is part of an inheritance hierarchy
Changing the type of an entity that is part of an inheritance hierarchy
Using natural keys
First, remember that the objects tracked by the EF are part of your DAL, not your domain model (regardless of whether you use POCOs or not). Some people don't need a domain model, but keep it in mind, as we can now think of these objects as representations of table records we manipulate in ways we wouldn't with domain objects.
Here, we use IDbSet.Remove to delete the records of the entity, then add new ones with the same primary key using IDbSet.Add, all in a single transaction. See the ChangeType method in the sample code below.
In theory, integrity is OK, and in theory, EF could detect what you're trying to do and optimize things. In practice, it currently doesn't (I profiled the SQL interface to verify this). The result is that it looks ugly (DELETE+INSERT instead of UPDATE), so if system beauty and performance are issues, it's probably a no-go. If you can take it, it's relatively straightforward.
Here is some sample code I used to test this (if you want to experiment, simply create a new console application, add a reference to the EntityFramework assembly, and paste the code).
A is the base class, X and Y are subclasses. We consider Id to be a natural key, so we can copy it in the subclasses copy constructors (here only implemented for Y). The code creates a database and seeds it with a record of type X. Then, it runs and changes its type to Y, obviously losing X-specific data in the process. The copy constructor is where you would transform data, or archive it if data loss is not part of the business process. The only piece of "interesting" code is the ChangeType method, the rest is boilerplate.
using System;
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity;
using System.Linq;
namespace EntitySubTypeChange {
abstract class A {
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int Id { get; set; }
public string Foo { get; set; }
public override string ToString() {
return string.Format("Type:\t{0}{3}Id:\t{1}{3}Foo:\t{2}{3}",
this.GetType(), Id, Foo, Environment.NewLine);
}
}
[Table("X")]
class X : A {
public string Bar { get; set; }
public override string ToString() {
return string.Format("{0}Bar:\t{1}{2}", base.ToString(), Bar, Environment.NewLine);
}
}
[Table("Y")]
class Y : A {
public Y() {}
public Y(A a) {
this.Id = a.Id;
this.Foo = a.Foo;
}
public string Baz { get; set; }
public override string ToString() {
return string.Format("{0}Baz:\t{1}{2}", base.ToString(), Baz, Environment.NewLine);
}
}
class Program {
static void Main(string[] args) {
Display();
ChangeType();
Display();
}
static void Display() {
using (var context = new Container())
Console.WriteLine(context.A.First());
Console.ReadKey();
}
static void ChangeType()
{
using (var context = new Container()) {
context.A.Add(new Y(context.A.Remove(context.X.First())));
context.SaveChanges();
}
}
class Container : DbContext {
public IDbSet<A> A { get; set; }
public IDbSet<X> X { get; set; }
public IDbSet<Y> Y { get; set; }
}
static Program() {
Database.SetInitializer<Container>(new ContainerInitializer());
}
class ContainerInitializer : DropCreateDatabaseAlways<Container> {
protected override void Seed(Container context) {
context.A.Add(new X { Foo = "Base Value", Bar = "SubType X Value" });
context.SaveChanges();
}
}
}
}
Output:
Type: EntitySubTypeChange.X
Id: 0
Foo: Base Value
Bar: SubType X Value
Type: EntitySubTypeChange.Y
Id: 0
Foo: Base Value
Baz:
Note: If you want an auto-generated natural key, you can't let EF ask the DBMS to compute it, or EF will prevent you from manipulating it the way you want (see below). In effect, EF treats all keys with computed values as surrogate keys, even though it still happily leaks them (the bad of both worlds).
Note: I annotate the subclasses with Table because you mentioned a TPT setup, but the problem is not actually related to TPT.
Using surrogate keys
If you consider a surrogate key to be truly internal, then it doesn't matter if it changes under your nose as long as you can still access your data the same way (using a secondary index for example).
Note: In practice, many people leak surrogate keys all around (domain model, service interface, ...). Don't do it.
If you take the previous sample, simply remove the DatabaseGenerated attribute and the assignment of the Id in the copy constructor of the subtypes.
Note: With its value generated by the DBMS, the Id property is completely ignored by EF and doesn't serve any real purpose other than being analyzed by the model builder to generate the Id column in the SQL schema. That and being leaked by bad programmers.
Output:
Type: EntitySubTypeChange.X
Id: 1
Foo: Base Value
Bar: SubType X Value
Type: EntitySubTypeChange.Y
Id: 2
Foo: Base Value
Baz:
Using the state pattern (or similar)
This solution is probably what most people would consider the "proper solution", since you can't change the intrinsic type of an object in most object-oriented languages. This is the case for CTS-compliant languages, which includes C#.
The problem is that this pattern is properly used in a domain model, not in a DAL like one implemented with EF. I'm not saying it's impossible, you may be able to hack things up with complex types or TPH constructs to avoid the creation of an intermediary table, but most likely you'll be swimming up the river until you give up. Hopefully someone can prove me wrong though.
Note: You can decide that you want your relational model to look different, in which case you may bypass this problem altogether. It wouldn't be an answer to your question though.
Using internal EF voodoo
I've rather quickly looked around the reference documentation for DbContext, ObjectContext and ObjectStateManager, and I can't immediately find any way to change the type of an entity. If you have better luck than me, you may be able to use DTOs and DbPropertyValues to do your conversion.
Important note
With the first two workarounds, you'll likely hit a bunch of problems with navigational properties and foreign keys (because of the DELETE+INSERT operation). This would be a separate question.
Conclusion
EF is not that flexible when you do anything non-trivial, but it keeps improving. Hopefully this answer won't be relevant in the future. It's also possible that I'm not aware of an existing killer-feature that would make what you want possible, so don't make any decisions based on this answer.
Using MVC4 with Entoty Framework CodeFirst I am having problems with the following scenario:
public class Survey
{
public QuestionCollection Questions {get;set;}
}
public class QuestionCollection : List<IQuestion> //Just for MVC
{ }
public class QuestionType1 : IQuestion { ... }
public class QuestionType2 : IQuestion { ... }
Seems straightforward. Now in my controller I want to get the Survey so i have:
DataContext context = new DataContext ();
var survey = context.Surveys.Include(s => s.Questions).SingleOrDefault(s => s.Id == id);
It does compile but runtime it gives me the error:
A specified Include path is not valid. The EntityType 'Survey' does not declare a navigation property with the name 'Questions'.
What am I doing wrong here?
Is there any good tutorial on this topic?
Entity Framework Code First requires that navigation collections be declared as an ICollection<T>. Also, to enable lazy loading of the associations, it should be virtual. This is because, unless otherwise specified, EF will return a proxy object wrapping your declared class. Since your original QuestionCollection property is a concrete implementation, it can't override that in the proxy and enable the navigation. Questions has to be declared as the interface.
Your concerns and requirements in EF are different than in MVC, and they aren't always compatible. If you really, really wanted QuestionCollection, you'll have to map that.
Your Surveys entity should look like this:
public class Survey
{
public virtual ICollection<Question> Questions { get; set; }
}
Also, EF can't implement entity types declared as an interface. This won't work: public virtual ICollection<IQuestion> - your individual question types must all inheirit from a common abstract or concrete instance. They can still implement the interface, but your entity type properties cannot be declared that way.
I would highly recommend going through this series of blog posts on inheritance structures in
EF.
The way you would set up your questions entities would look like this:
// You can still keep the IQuestion interface around for MVC
public abstract class Question : IQuestion
{
// ... properties ...
}
public class QuestionType1 : Question
{
// ... properties ...
}
public class QuestionType2 : Question
{
// ... properties ...
}
public class Survey
{
// Note, collection of Question, not the interface.
public virtual ICollection<Question> Questions { get; set; }
}
Depending on how exactly you want your table structure to look, the base Question class may or may not be abstract. Refer to the above blog posts to see the various options - Table per Type, Table per Hierarchy, Table per Concrete.
Have a look at this link,I think you need to set some auto policies http://forums.asp.net/t/1816051.aspx/1
I'm using the newest Entity Framework and ran into a problem with Many To Many Relationship when I want to create an extra column.
The issue is the same raised in this older post:
EF Code First Additional column in join table for ordering purposes
Is it still the problem today that one can not add an extra column without loosing the many to many relation ship (link from object A to B as A.B because the mapping becomes and entity it self) ?
What are the work a rounds ?
Look up the a of class A I need and then query for mapping table where(e=>e.A == a) to get my Bs? And when I need the extra colums i would do MappingTable.find(a,b) ?
Are there other modeling options, linq to sql that would make it easier ?
As far as I know things haven't changed with EF 5. You would need to do it as the link says to. I like to stick with EF as its easy to use, but that's just my opinion...
I had the same problem. What I did to work-around it was create another derivative DbContext specifically to handle joins. I.E.:
public class JoinContext : DbContext
{
internal JoinContext() : base("name=SampleConnectionString")
{
PreventErrorIfDatabaseSchemaChanges();
// Get the ObjectContext related to this DbContext
var objectContext = (this as IObjectContextAdapter).ObjectContext;
}
public DbSet<StudentImage> StudentImages { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<StudentImage>()
.ToTable("StudentImages");
.HasKey(joinTable => new { joinTable.StudentId, joinTable.ImageId });
base.OnModelCreating(modelBuilder);
}
private static void PreventErrorIfDatabaseSchemaChanges()
{
Database.SetInitializer<JoinContext>(null);
}
}
I left the other application context with the Student/Image many-to-many join mapping as-is. Don't forget to specify a compounded key for the join table (refer to HasKey method above), else EF bombs on databse initialization.
After you have your special join context, use a repository to access this context and get or set the desired fields from mapped join table:
public class StudentRepository
{
public int GetImageSortOrder(int studentId, int imageId)
{
var joinContext = new JoinContext();
var joinTuple = joinContext.StudentImages.Find(studentId, imageId);
return joinTuple.SortOrder;
}
}
Hope this helps!
I'm using entity framework with POCOs and the repository pattern and am wondering if there is any way to filter a child list lazy load. Example:
class Person
{
public virtual Organisation organisation {set; get;}
}
class Organisation
{
public virtual ICollection<Product> products {set; get;}
}
class Product
{
public bool active {set; get;}
}
Currently I only have a person repository because I'm always starting from that point, so ideally I would like to do the following:
Person person = personRepo.GetById(Id);
var products = person.organisation.products;
And have it only load products where active = true from the database.
Is this possible and if so how?
EDIT My best guess would be either a filter can be added to the configuration of the entity. Or there might be a way to intercept/override the lazy load call and modify it. Obviously if I created an Organisation Repository I could manually load it as I please but I am trying to avoid that.
There's not a direct way to do this via lazy loading, but if you were willing to explicitly load the collection, you could follow whats in this blog, see the Applying filters when explicitly loading related entities section.
context.Entry(person)
.Collection(p => p.organisation.products)
.Query()
.Where(u => u.IsActive)
.Load();
You can do what Mark Oreta and luksan suggest while keeping all the query logic within the repository.
All you have to do is pass a Lazy<ICollection<Product>> into the organization constructor, and use the logic they provided. It will not evaluate until you access the value property of the lazy instance.
UPDATE
/*
First, here are your changes to the Organisation class:
Add a constructor dependency on the delegate to load the products to your
organization class. You will create this object in the repository method
and assign it to the Person.Organization property
*/
public class Organisation
{
private readonly Lazy<ICollection<Product>> lazyProducts;
public Organisation(Func<ICollection<Product>> loadProducts){
this.lazyProducts = new Lazy<ICollection<Product>>(loadProducts);
}
// The underlying lazy field will not invoke the load delegate until this property is accessed
public virtual ICollection<Product> Products { get { return this.lazyProducts.Value; } }
}
Now, in your repository method, when you construct the Person object you will assign the Organisation property with an Organisation object containing the lazy loading field.
So, without seeing your whole model, it will looks something like
public Person GetById(int id){
var person = context.People.Single(p => p.Id == id);
/* Now, I'm not sure about the cardinality of the person-organization or organisation
product relationships, but let's assume you have some way to access the PK of the
organization record from the Person and that the Product has a reference to
its Organisation. I may be misinterpreting your model, but hopefully you
will get the idea
*/
var organisationId = /* insert the aforementioned magic here */
Func<ICollection<Product>> loadProducts = () => context.Products.Where(product => product.IsActive && product.OrganisationId == organisationId).ToList();
person.Organisation = new Organisation( loadProducts );
return person;
}
By using this approach, the query for the products will not be loaded until you access the Products property on the Organisationinstance, and you can keep all your logic in the repository. There's a good chance that I made incorrect assumptions about your model (as the sample code is quite incomplete), but I think there is enough here for you to see how to use the pattern. Let me know if any of this is unclear.
This might be related:
Using CreateSourceQuery in CTP4 Code First
If you were to redefine your properties as ICollection<T> rather than IList<T> and enable change-tracking proxies, then you might be able to cast them to EntityCollection<T> and then call CreateSourceQuery() which would allow you to execute LINQ to Entities queries against them.
Example:
var productsCollection = (EntityCollection<Product>)person.organisation.products;
var productsQuery = productsCollection.CreateSourceQuery();
var activeProducts = products.Where(p => p.Active);
Is your repository using something like:
IQueryable<T> Find(System.Linq.Expressions.Expression<Func<T, bool>> expression)
If so you can do something like this:
var person = personRepo.Find(p => p.organisation.products.Any(e => e.active)).FirstOrDefault();
You could possibly use Query() method to achieve this. Something like:
context.Entry(person)
.Collection(p => p.organisation.products)
.Query()
.Where(pro=> pro.Active==true)
.Load();
Have a look at this page click here
I have a base class and two derived classes.
Each of the derived classes implements the same type as a property - the only difference is the property name.
Sadly I don't have much influence on the class design -> they have been generated from a wsdl file.
I then have a property on the BaseType to encapsulate the common property. The plan was to use this property in my web views etc.
I have used the famous "Fruit-Example" to demonstrate the problem:
public class FruitBase
{
public virtual int ID { get; set; }
//
// The plan is to use this property in mvc view
//
[NotMapped]
public virtual FruitnessFactor Fruitness
{
get
{
if (this.GetType().BaseType == typeof(Apple))
return ((Apple)this).AppleFruitness;
else if (this.GetType().BaseType == typeof(Orange))
return ((Orange)this).OrangeFruitness;
else
return null;
}
}
}
public class FruitnessFactor { }
In my MVC controller, the following query works absolutely fine:
return View(context.FruitEntities
.OfType<Apple>().Include(a =>a.AppleFruitness)
.ToList());
But this one doesn't:
return View(context.FruitEntities
.OfType<Apple>().Include(a =>a.AppleFruitness)
.OfType<Orange>().Include(o => o.OrangeFruitness)
.ToList());
The error message I get is:
DbOfTypeExpression requires an expression argument with a polymorphic result type that is compatible with the type argument.
I am using EF 5.0 RC and the Code First approach.
Any help is much appreciated!
As far as I can tell you can't apply Include on multiple subtypes in a single database query. You can query one type (OfType<Apple>().Include(a => a.AppelFruitness)) and the same for another subtype. The problem is that you can't concat the results in the same query because the result collections have different generic types (apples and oranges).
One option would be to run two queries and copy the result collection into a new collection of the base type - as you already indicated in the comment section under your question.
The other option (which would only need a single query) is a projection. You would have to define a projection type (you could also project into an anonymous type)...
public class FruitViewModel
{
public FruitBase Fruit { get; set; }
public FruitnessFactor Factor { get; set; }
}
...and then can use the query:
List<FruitViewModel> fruitViewModels = context.FruitEntities
.OfType<Apple>()
.Select(a => new FruitViewModel
{
Fruit = a,
Factor = a.AppleFruitness
})
.Concat(context.FruitEntities
.OfType<Orange>()
.Select(o => new FruitViewModel
{
Fruit = o,
Factor = o.OrangeFruitness
}))
.ToList();
If you don't disable change tracking (by using AsNoTracking) the navigation properties get populated automatically when the entities get attached to the context ("Relationship fixup") which means that you can extract the fruits from the viewModel collection...
IEnumerable<FruitBase> fruits = fruitViewModels.Select(fv => fv.Fruit);
...and you'll get the fruits including the FruitnessFactor properties.
This code is pretty awkward but a direct approach without using a projection has been asked for several times without success:
bottleneck using entity framework inheritance
Entity Framework - Eager loading of subclass related objects
How do I deeply eager load an entity with a reference to an instance of a persistent base type (Entity Framework 4)