Entity Framework TPH - Additional WHERE clause only for one subtype - entity-framework

Suppose I have a class Parent, with two subclasses, AChild and BChild. I have these mapped to a single table using Entity Framework 5.0.0 on .NET 4.5, using TPH.
public abstract class Parent {
public string Type { get; set; } // Column with this name in DB is discriminator.
public string Status { get; set; }
}
public class AChild : Parent {
// Other stuff.
}
public class BChild : Parent {
// Other stuff.
}
The code to configure the mapping:
class ParentConfiguration : EntityTypeConfiguration<Parent> {
Map((EntityMappingConfiguration<AChild> mapping) => mapping
.Requires("Type")
.HasValue("A"));
Map((EntityMappingConfiguration<BChild> mapping) => mapping
.Requires("Type")
.HasValue("B"));
}
I have a need to run a query that returns both AChild and BChild objects. However, it needs to filter ONLY the AChild rows by a second column, which in this example I will call Status.
Ideally I would want to do the following:
public IList<Parent> RunQuery() {
IQueryable<Parent> query =
this.context.Set<Parent>()
.Where((Parent parent) => !parent.Type.Equals("A") || parent.Status.Equals("Foo"))
.OrderBy((Parent parent) => parent.Number);
return query.ToList();
}
This doesn't work. It insisted on looking for a "Type1" column instead of just letting both the discriminator and a "Type" property be mapped to the same "Type" column.
I know of the "OfType" extension method that can be used to completely filter down to one type, but that's too broad a brush in this case.
I could possibly run multiple queries and combine the results, but the actual system I'm building is doing paging, so if I need to pull back 10 rows, it gets messy (and inefficient) to query since I'll either end up pulling back too many rows, or not pull back enough and have to run extra queries.
Does anyone have any other thoughts?

There are few problems. First of all you cannot have discriminator mapped as a property. That is the reason why it is looking for Type1 column - your Type property results in second column because the first one is already mapped to .NET types of your classes. The only way to filter derived types is through OfType.
The query you want to build will be probably quite complex because you need to query for all Bs and concatenate them with result of query for filtered As. It will most probably not allow you to concatenate instances of Bs with As so you will have to convert them back to parent type.

Related

Entity Framework navigation with only foreign key

Following the guide lines from Domain Driven Design, I try to avoid having one aggregate referencing a different aggregate. Instead, an aggregate should reference another aggregate using the other aggregate's id, for example:
public class Addiction
{
private Addiction(){} //Needed for EF to populate non-simple types
//DrugType belongs to the aggregate,
//inflate when retrieving the Addiction from the db
//EF does not need DrugId for navigation
Drug Drug{get;set;}
//The supplier is not part of the aggregate,
//aggregates only reference eachother using Ids
int SupplierId{get;set;}
//Other properties
}
public class AddictionConfiguration : IEntityTypeConfiguration<Addiction>
{
builder.HasOne(addiction => addiction.Drug); //Works
builder.HasOne("SupplierId") //Does not work.
}
In this (not very realistic) example, Drug is part of the Addiction's aggregate. When loading this entity from the database using EF, it will also inflate the Drug property without me having to specify the DrugId as the foreign key.
However, now I need to get a list of all Addictions and their suppliers by mapping the relevant properties to a Dto. I try to achieve this by using AutoMapper's ProjectTo functionality, e.g.
_mapper.ProjectTo<AddictionDto>(_dbContext.Addictions.Where(x => x.Id > 1));
where AddictionDto is defined as
public class AddictionDto
{
DrugDto Drug {get;set;}
SupplierDto Supplier {get;set;}
//other properties
}
And
public class SupplierDto
{
public int Id {get;set;}
public string Name {get;set;}
}
Automapper correctly loads the Addiction and also the Drug, but I cannot get it to load the Supplier. I've tried all the options of the IEntityTypeConfiguration to tell EF that there is a navigation property, but I cannot get it to work. Does anyone know if is even possible to do what I described above?

Get Discriminator in Linq Select results

I'm trying to make a SQL Query with LinqToEntities which will be as efficient as possible so I would like to avoid iterating after every record I get in results to get a new corrected collection. I'm trying to create a ready collection to send as a result in API just with this one query.
I'm trying to get a discriminator value though with inherited models.
My models are somewhat like these:
public abstract class DbEntity
{
[NotMapped]
public string DiscriminatorValue
{
get
{
return ObjectContext.GetObjectType(GetType()).Name;
}
}
}
public class Foo : DbEntity {
}
public class Bar: DbEntity {
}
This model will create a table in database which will have a column called Discriminator. To get the discriminator value I used my custom DiscriminatorValue property but it won't work in queries like this:
var events = context.MyTable.Select(t => new {
Discriminator = t.DiscriminatorValue
}).ToList();
This below one will obviously work, but it will be much slower imho:
var events = context.MyTable.ToList().Select(t => new {
Discriminator = t.DiscriminatorValue
}).ToList();
Is it possible to get Discriminator value without having to write my custom SQL query myself (like real sql which I know is possible too).

Entity Framework - To sum reference object properties

I have the following models which represent my table relationship.
public class A { List<B> B_List }
public class B { public int amount }
I was wondering if it is possible to get top 5 class A objects order by "amount".
I was hoping there is a way to write an EF query to avoid pulling all A objects from database.
I think #Bilal's answer is close, but may not get you exactly what you want. By the way, you haven't been clear on what you mean by "top 5 A objects order by Amount". Each A object has multiple amounts. Are you wanting the top 5 A objects based on the maximum amount? If so, then try this ...
dbContext.AList
.OrderByDescending(a => a.B_List.Sum(b => b.amount))
.Take(5);

Access underlying DbContext (or run stored procedure) from Entity Framework POCO method

Is it possible to access the underlying DbContext (the DbContext that has populated this object/has this object in its cache/is tracking this object) from inside a model object, and if so, how?
The best answer I have found so far is this blog post which is five years old. Is it still the best solution available?
I’m using the latest version of Entity Framework if that matters.
Here's a sample to clarify my question:
I have a hierarchical tree. Let’s say it is categories that could have sub-categories. The model object would be something like this:
class Category
{
string CategoryId { get; set; }
string Name { get; set; }
virtual Category Parent { get; set; }
virtual ICollection<Category> Children { get; set; }
}
Now, if I want to access all descendants of a category (not just its immediate children) I can use a recursive query like this:
class Category
{
//...
IEnumerable<Category> Descendants
{
get
{
return Children.Union(Children.SelectMany(q => q.Descendants));
}
}
}
which works, but has bad performance (due to multiple database queries it needs to perform).
But suppose I have an optimized query that I can run to find descendent (maybe I store my primary key in a way that already contains path, or maybe I’m using SQL Server data type hierarchyid, etc.). How can I run such a query, which needs access to the whole table/database and not just the records available through model object’s navigational properties?
This can be either done by running a stored procedure/SQL command on the database, or a query like this:
class Category
{
//...
IEnumerable<Category> Descendants
{
get
{
// this won't work, because underlying DbContext is not available in this context!
return myDbContext.Categories.Where(q => q.CategoryId.StartsWith(this.CategoryId));
}
}
}
Is there a way at all to implement such a method?

EF4 inheritance and Stored procedures

I implemented inheritance with a discriminator field so all my records are in the same table. My basetype is Person (also the name of the table) and Driver and Passenger inherit from it. I receive instances of the correct type (Driver and Passenger) when I perform a query on the object context to Person. example:
var q = from d in ctx.Person
select d;
But I also create a function that calls a stored procedure and mapped the output of the function to the type Person. But now I get a list of Person and not Drivers or Passengers when I execute this method.
Anybody an idea how to solve this or is this a bug in EF4?
AFAIK, you can't use discriminator mapping (e.g TPH) when dealing with stored procedure mappings.
The stored procedure must be mapped to a complex type or custom entity (e.g POCO), the mapping cannot be conditional.
What you could do is map it to a regular POCO, but then project that result set into the relevant derived type (manual discrimination).
E.g:
public ICollection<Person> GetPeople()
{
var results = ExecuteFunction<Person>(); // result is ObjectResult<Person>
ICollection<Person> people = new List<Person>();
foreach (var result in results)
{
if (result.FieldWhichIsYourDiscriminator == discriminatorForDriver)
{
people.Add((Driver)result);
}
// other discriminators
}
}
If your always expecting a collection of one type (e.g only Drivers), then you wouldn't need the foreach loop, you could just add the range. The above is in case you are expecting a mixed bag of different people types.
Would be interested to see other answers, and if there is a better way though - but the above should work.