EF 4.1 abstract collection loading problem - c#

I have a problem regarding EF 4.1 code first.
I am trying to demonstrate with a simplified example.
Let's say we have a Country class which contains a States collection.
State collection contains schools collection.
School is an abstract class.
It has specializations of ElementarySchool and HighSchool.
HighSchool has a collection property of DrivingCourses.
DrivingCourses and all other data saves into the db successfully.
My problem is when I'm loading the Country class the DrivingCourses collection remains null. (everything else is ok)
As I understand the problem is because when ef loads and populates the HighSchool class it's not aware of the courses collection.
I am unable to add this mapping because with the fluent api's static reflecion I can only map properties of the (abstract) School class.
I'm using the default config for abstraction: Table per Hierarchy
Could someone please brighten me, if it's possible to solve my problem with EF 4.1?
Thanks in advance,
Sandor

If I understand your description correctly then your model looks roughly like this (I omit key properties and so on):
public class Country
{
public ICollection<State> States { get; set; }
}
public class State
{
public ICollection<School> Schools { get; set; }
}
public abstract class School { ... }
public class ElementarySchool : School { ... }
public class HighSchool : School
{
public ICollection<DrivingCourse> DrivingCourses { get; set; }
}
public class DrivingCourse { ... }
And you have a DbContext which includes public DbSet<Country> Countries { get; set; }.
Now you want to load all Countries (or a filtered collection of Countries) including all navigation properties (especially also the DrivingCourses).
I don't know if this is possible with a single roundtrip to the database (by eager loading all collections). A solution which will require multiple roundtrips though might be this one:
// Load all Countries including `States` and `Schools` collection
// but not the `DrivingCourses` collection
var countryList = context.Countries
.Include(c => c.States.Select(s => s.Schools))
.ToList();
// Create in-memory list of all loaded Schools of type HighSchool
IEnumerable<HighSchool> highSchoolList =
countryList.SelectMany(c =>
c.States.SelectMany(s => s.Schools.OfType<HighSchool>()));
// Explicitely load the DrivingCourses one by one
foreach (var item in highSchoolList)
context.Entry(item).Collection(h => h.DrivingCourses).Load();
Just as a first idea. It's likely that there are better solutions.
Edit
Using Load on the Countries DbSet doesn't change the problem. Load is the same as ToList() without actually returning a result, the entities are just loaded into the context. The code above could be rewritten like so:
context.Countries.Include(c => c.States.Select(s => s.Schools)).Load();
IEnumerable<HighSchool> highSchoolList =
context.Countries.Local.SelectMany(c =>
c.States.SelectMany(s => s.Schools.OfType<HighSchool>()));
foreach (var item in highSchoolList)
context.Entry(item).Collection(h => h.DrivingCourses).Load();
But this is basically the same as before and it also doesn't solve the problem to load the DrivingCourses in the first Load statement in a single DB roundtrip.

Related

One to many relationship doesn`t retrieve data in entity framework

I`m in process of learning C# & .NET and EF (with aspnetboilerplate) and I came up with idea to create some dummy project so I can practice. But last 4 hour Im stuck with this error and hope someone here can help me.
What I create( well at least I think I create it correctly ) is 2 class called "Ingredient" and "Master"
I want to use it for categorize Ingredient with "Master" class.
For example ingredient like
Chicken breast
chicken drumstick
Both of them belong to Meat ( witch is input in "Master" database ) and here is my code
Ingredient.cs
public class Ingrident : Entity
{
public string Name { get; set; }
public Master Master { get; set; }
public int MasterId { get; set; }
}
Master.cs
public class Master : Entity
{
public string Name { get; set; }
public List<Ingrident> Ingridents { get; set; } = new();
}
IngridientAppService.cs
public List<IngridientDto> GetIngWithParent()
{
var result = _ingRepository.GetAllIncluding(x => x.Master);
//Also I try this but doesn`t work
// var result = _ingRepository.GetAll().Where(x => x.MasterId == x.Master.Id);
return ObjectMapper.Map<List<IngridientDto>>(result);
}
IngridientDto.cs
[AutoMap(typeof(IndexIngrident.Entities.Ingrident))]
public class IngridientDto : EntityDto
{
public string Name { get; set; }
public List<MasterDto> Master { get; set; }
public int MasterId { get; set; }
}
MasterDto.cs
[AutoMap(typeof(IndexIngrident.Entities.Master))]
public class MasterDto : EntityDto
{
public string Name { get; set; }
}
When I created ( for last practice ) M -> M relationship this approach with .getAllIncluding work but now when I have One -> Many it won`t work.
Hope someone will be able to help me or at least give me some good hint.
Have a nice day !
Straight up the examples you are probably referring to (regarding the repository etc.) are overcomplicated and for most cases, not what you'd want to implement.
The first issue I see is that while your entities are set up for a 1-to-many relationship from Master to Ingredients, your DTOs are set up from Ingredient to Masters which definitely won't map properly.
Start with the simplest thing. Get rid of the Repository and get rid of the DTOs. I'm not sure what the base class "Entity" does, but I'm guessing it exposes a common key property called "Id". For starters I'd probably ditch that as well. When it comes to primary keys there are typically two naming approaches, every table uses a PK called "Id", or each table uses a PK with the TableName suffixed with "Id". I.e. "Id" vs. "IngredientId". Personally I find the second option makes it very clear when pairing FKs and PKs given they'd have the same name.
When it comes to representing relationships through navigation properties one important detail is ensuring navigation properties are linked to their respective FK properties if present, or better, use shadow properties for the FKs.
For example with your Ingredient table, getting rid of the Entity base class:
[Table("Ingredients")]
public class Ingredient : Entity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int IngredientId { get; set; }
public string Name { get; set; }
public int MasterId { get; set; }
[ForeignKey("MasterId")]
public virtual Master Master { get; set; }
}
This example uses EF attributes to aid in telling EF how to resolve the entity properties to respective tables and columns, as well as the relationship between Ingredient and Master. EF can work much of this out by convention, but it's good to understand and apply it explicitly because eventually you will come across situations where convention doesn't work as you expect.
Identifying the (Primary)Key and indicating it is an Identity column also tells EF to expect that the database will populate the PK automatically. (Highly recommended)
On the Master side we do something similar:
[Table("Masters")]
public class Master : Entity
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int MasterId { get; set; }
public string Name { get; set; }
[InverseProperty("Master")]
public virtual ICollection<Ingredient> Ingredients { get; set; } = new List<Ingredient>();
}
Again we denote the Primary Key, and for our Ingredients collection, we tell EF what property on the other side (Ingredient) it should use to associate to this Master's list of Ingredients using the InverseProperty attribute.
Attributes are just one option to set up the relationships etc. The other options are to use configuration classes that implement IEntityConfiguration<TEntity> (EF Core), or to configure them as part of the OnModelCreating event in the DbContext. That last option I would only recommend for very small projects as it can start to become a bit of a God method quickly. You can split it up into calls to various private methods, but you may as well just use IEntityConfiguration classes then.
Now when you go to fetch Ingredients with it's Master, or a Master with its Ingredients:
using (var context = new AppDbContext())
{
var ingredients = context.Ingredients
.Include(x => x.Master)
.Where(x => x.Master.Name.Contains("chicken"))
.ToList();
// or
var masters = context.Master
.Include(x => x.Ingredients)
.Where(x => x.Name.Contains("chicken"))
.ToList();
// ...
}
Repository patterns are a more advanced concept that have a few good reasons to implement, but for the most part they are not necessary and an anti-pattern within EF implementations. I consider Generic repositories to always be an anti-pattern for EF implementations. I.e. Repository<Ingredient> The main reason not to use repositories, especially Generic repositories with EF is that you are automatically increasing the complexity of your implementation and/or crippling the capabilities that EF can bring to your solution. As you see from working with your example, simply getting across an eager load through to the repository means writing in complex Expression<Func<TEntity>> parameters, and that just covers eager loading. Supporting projection, pagination, sorting, etc. adds even more boiler-plate complexity or limits your solution and performance without these capabilities that EF can provide out of the box.
Some good reasons to consider studying up on repository implementations /w EF:
Facilitate unit testing. (Repositories are easier to mock than DbContexts/DbSets)
Centralizing low-level data rules such as tenancy, soft deletes, and authorization.
Some bad (albeit very common) reasons to consider repositories:
Abstracting code from references or knowledge of the dependency on EF.
Abstracting the code so that EF could be substituted out.
Projecting to DTOs or ViewModels is an important aspect to building efficient and secure solutions with EF. It's not clear what "ObjectMapper" is, whether it is an Automapper Mapper instance or something else. I would highly recommend starting to grasp projection by using Linq's Select syntax to fill in a desired DTO from the models. The first key difference when using Projection properly is that when you project an object graph, you do not need to worry about eager loading related entities. Any related entity / property referenced in your projection (Select) will automatically be loaded as necessary. Later, if you want to leverage a tool like Automapper to help remove the clutter of Select statements, you will want to configure your mapping configuration then use Automapper's ProjectTo method rather than Map. ProjectTo works with EF's IQueryable implementation to resolve your mapping down to the SQL just like Select does, where Map would need to return everything eager loaded in order to populate related data. ProjectTo and Select can result in more efficient queries that can better take advantage of indexing than Eager Loading entire object graphs. (Less data over the wire between database and server/app) Map is still very useful such as scenarios where you want to copy values back from a DTO into a loaded entity.
Do it like this
public class Ingrident:Entity
{
public string Name { get; set; }
[ForeignKey(nameof(MasterId))]
public Master Master { get; set; }
public int MasterId { get; set; }
}

Use AutoMapper to map two VM to one Entity object

I'm using AutoMapper to map a lot of Entity models to View Model that I use in my controllers and views (.Net MVC)
There is a lot of relations in the DB and so our VM have a lot of childs (who have childs, and so and so)
public class InvoiceVMFull : VMBase
{
public int Id { get; set; }
public InvoiceType InvoiceType { get; set; }
public string Reference { get; set; }
//.... shortened code for readability
// list all entity fields
public List<string> InvoiceMainAddress { get; set; }
public List<string> InvoiceDlvAddress { get; set; }
}
It works just fine, but is very slow and always load from the DB all relations whereas I usually need only a few datas...
So I created some light VM that I want to use for the majority of our pages.
public class InvoiceVMLite : VMBase
{
public int Id { get; set; }
public string Reference { get; set; }
//.... shortened code for readability
// list only some of the entity fields (most used)
public StoredFileVM InvoiceFile { get; set; }
}
The problem is I can't find how :
to map one Entity object to the two VMs and how to choose the right one (to load from DB) using the context (the page or event called)
to map two VMs to one entity and save (on the DB) only the fields that are present in the VM used and don't erase the absent ones
I tried to create the mapping both VM :
Mapper.CreateMap<Invoice, InvoiceVMLite>();
Mapper.CreateMap<Invoice, InvoiceVMFull>();
But when I try to call the mapping for Lite, it doesn't exist (have been overridden by Full) :
Mapper.Map(invoice, InvoiceEntity, InvoiceVMLite)
Correct Use of Map function
It looks like you are calling map incorrectly. Try these instead
var vmLite = Mapper.Map<Invoice, InvoiceVMLite>(invoice);
var vmFull = Mapper.Map<Invoice, InvoiceVMFull>(invoice);
var vmLite = Mapper.Map(invoice); // would work if it were not ambiguous what the destination was based on the input.
Entity to two view models
You would usually create two mappings, one for each view model from the one entity. I'd suggest the cleanest is to have two separate views (separate Actions in a controller) for each view model. This may involve a quick redirect after you've decided on context which one to use.
View models to entity
Automapper is not meant for mapping from view models to Entities for many reasons, including the challenge you'd face. Instead you would pass specific parameters. The author of Automapper, Jimmy Bogard, wrote a good article on why this is the case.
I couldnt manage to do that with AutoMapper, and so I created my own convert methods (Entity <=> VM) with a lot of reflexivity, and with specific cases handled in each of the VM classes.
Now I can easily get a full or lite VM from an Entity, and also specify the depth in relation I want to go. So it's A LOT faster and more adaptable than AutoMapper
And I can save a VM to an entity (only saving modified fields if I want) that I create or that i got from base. So it's A LOT faster and adaptable than AutoMapper
In conclusion : Don't use autoMapper, it seem easy but create so many performance issues that it isn't worth it

EF tables with Many to many relationship, Will this consume memory?

I have 2 tables that saved family members, when I use include to retrieve the family members, the generated T-SQL is what I'm expected, but when I see the result from VS, like image below, it's look like never ending.
My questions:
It's this normal?
Should I avoid include when the relationship becomes complex?
If it is normal, will this very memory consumption?
POCO
public class Cust_ProfileTbl
{
[Key]
public long bintAccountNo { get; set; }
public string nvarCardName { get; set; }
public string varEmail { get; set; }
public virtual ICollection<Cust_ProfileFamilyTbl> profileFamilyParents { get; set; }
public virtual ICollection<Cust_ProfileFamilyTbl> profileFamilyChildren { get; set; }
}
public class Cust_ProfileFamilyTbl
{
[Key]
public int intProfileFamily { get; set; }
public long bintAccountNo { get; set; }
public long bintAccountNoMember { get; set; }
public virtual Cust_ProfileTbl custProfileParent { get; set; }
public virtual Cust_ProfileTbl custProfileChild { get; set; }
}
LINQ
var rs = from family in context.member.Include("profileFamilyParents.custProfileChild")
select family;
rs = rs.Where(x => x.bintAccountNo.Equals(1));
var result = rs.ToList();
In onModelCreating
modelBuilder.Entity<Cust_ProfileFamilyTbl>()
.HasRequired(m => m.custProfileParent)
.WithMany(t => t.profileFamilyParents)
.HasForeignKey(m => m.bintAccountNo)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Cust_ProfileFamilyTbl>()
.HasRequired(m => m.custProfileChild)
.WithMany(t => t.profileFamilyChildren)
.HasForeignKey(m => m.bintAccountNoMember)
.WillCascadeOnDelete(false);
When people use an ORM like EF in their application, many times the application design gets driven by this ORM and the entities defined in its model. When the app is a simple "CRUD" application, that's not a problem, but an advantage, because you spare a lot of time.
However when things start to get more complicated, an "ORM guided design" becomes a problem. This looks to be the case.
There are at least two problems, recovered from the comments:
the data retrieved from the DB is more than needed
in this case, because of some particular relationships between entities, there is a circular reference, which creates an endless loop and a stack overflow when trying to show the model in the view
When this kind of situation shows up, the most advisable is to break the tight tie between the ORM and the rest of the app, which can be dine by defining a new class, and projecting the data into it. Let's give a generic ProfileDto name.
public class ProfileDto { ... }
DTO is a generic name for this kind of classes: Data Transfer Objects - but, when they have specific purposes, they can get other names like view models, when they're going to be used as the model sent to an MVC view
And then, what you need to do is to project the result of the query into the DTO:
var model = theQuery.Select(i => new ProfileDto { a = i.a, b = i.b...}).ToList();
With a good design of the Dto you'll only recover the needed data from the DB, and you'll avoid the loop problem (by not including the navigation property that creates the loop).
NOTE: many times people uses mappers, like AutoMapper or ValueInjecter to make the mapping, or part of the mapping, automatic
Code standardization is a very good idea until it becomes a source of problems. The main purpose of writing code is implementing the business logic. If code standardization, technology, or whatever, makes it harder to implement business logic, instead of contributing to the solution, they become a problem, so you need to avoid them.
Mapping you created is Normal but use of Include depends upon its usage
Use of Include depends on situation of use for example if you want to cache it in memory then you may use include, Where as if you are using only showing properties of Cust_ProfileTbl
class in some grid and on click you want show details of Cust_ProfileFamilyTbl then you might don't want to use include. But be careful if you are using Automapper or something because when It will try to map related properties it will query database.
It will consume memeory when you execute ToList() as doing so you are Loading query result into List collection. Where as If you again want to query the result then you can use ToQueryable() or just want to iterate the you can don't load them to List.

Entity framework map entity to multiple tables in cascade

I'm facing a problem using EF.
I have the following situation:
From this database schema i'd like to generate the following entity by merge tables data:
// Purchases
public class Purchase
{
//Fields related to Purchases
public int IdPurchase { get; set; }
public string CodPurchase { get; set; }
public int IdCustomer { get; set; }
public decimal Total { get; set; }
//Fields related to Customers table
public string CodCustomer { get; protected set; }
public string CompanyTitle { get; protected set; }
public string CodType { get; protected set; }
//Fields related to CustomersType table
public string DescrType { get; protected set; }
}
As you can see, in my context i don't want 3 separated entities for each table. I want a single one with the fields related to all tables. All fields of Customers and CustomersType tables must be readonly (so i've set the relative setters protected) and the others must be editables so that EF can track changes. In particular, i'd like to have the ability to change the "IdCustomer" field and let EF to automatically update "CodCustomer", "CompanyTitle", "DescrType"....and so on by doing cross table select.
To do that, i wrote this configuration class:
internal class PurchaseConfiguration : EntityTypeConfiguration<Purchase>
{
public PurchaseConfiguration(string schema = "dbo")
{
ToTable(schema + ".Purchases");
HasKey(x => x.IdPurchase);
Property(x => x.IdPurchase).HasColumnName("IdPurchase").IsRequired().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(x => x.IdCustomer).HasColumnName("IdCustomer").IsRequired();
Property(x => x.Total).HasColumnName("Total").IsRequired().HasPrecision(19, 4);
Map(mc =>
{
mc.Properties(n => new
{
n.CodCustomer,
n.CompanyTitle,
n.CodType
});
mc.ToTable("Customers");
});
Map(mc =>
{
mc.Properties(n => new
{
n.DescrType,
});
mc.ToTable("CustomersType");
});
}
}
I've tested it but it doesn't work as expected. I always get this message:
Properties for type 'Purchase' can only be mapped once. The non-key
property 'CodCustomer' is mapped more than once. Ensure the
Properties method specifies each non-key property only once.
Maybe there's something wrong or i forget something (for example the join fields of Map<> that i don't know where to specify them).
How can i accomplish in the correct way this task?
I don't want to have "Customers" and "CustomersType" DBSets in my context.
Is there a way to avoid it?
I even thought to add into the "IdCustomer" setter a custom query to update manually "Customers" and "CustomersType" related fields, but i don't want to do that for 2 reasons:
I don't have any DbConnection avaiable into the "Purchases" class, so i can't create a DbCommand to read data from DB.
I want entity class to be persistent-ignorant
EF seems to be a powerfull tool that can do these sort of things and i don't want to reinvent the wheel by writing custom procedures.
I've uploaded the example C# source and the tables CREATE scripts (MS SQLServer) here.
All entities are autogenerated by the "EF reverse POCO generator" T4 template (the T4 template is disabled, to activate it set CustomTool = TextTemplatingFileGenerator).
Do not forget to update the ConnectionString in the app.config.
Thanks in advance.
Not the right mapping
I'm afraid the bad news is that this mapping is not possible with this table structure. What you're trying to achieve here is known as entity splitting. However, entity splitting requires 1:1 associations, because sets of records in the involved tables represent one entity. With this mapping, you can't have a Customer belonging to more than one Purchase. That would mean that you could modify multiple Purchase entities by modifying a Customer property of only one of them.
Maybe the news isn't that bad, because I think you actually want to have 1-n associations. But then you can't have these "flattened" properties in Purchase.
As an alternative you could create delegated properties like so:
public string CodCustomer
{
get { return this.Customer.CodCustomer; }
set { this.Customer.CodCustomer = value; }
}
You'd have to Include() Customers and CustomersTypes when you fetch Purchases.
Another alternative is to use a tool like AutoMapper to map Purchase to a DTO type having the flattened properties.
But what does the exception tell me?
You map the Purchase entity to the Purchases table. But you don't specify which properties you want to map to this table. So EF assumes that all properties should be mapped to it. So that's the first (implicit) mapping of CodCustomer. The second one is the one in the mc.ToTable statement. (EF only reports the first problem.)
To fix this, you should add a mapping statement for the left-over Purchase properties:
Map(mc =>
{
mc.Properties(n => new
{
n.IdPurchase,
n.CodPurchase,
n.IdCustomer,
n.Total,
});
mc.ToTable("Purchases");
});
By the way, you should also remove the mapping configuration classes of Customer and CustomersType, they're redundant.
But, as said, the database schema doesn't match the required structure. If you try to save a Purchase you will get a foreign key constraint exception. This is because EF expects the following table structure:
Where the columns IdPurchase in Customer and CustomersType are both primary key and foreign key to Purchase. I don't think this is what you had in mind when designing the database.

Filtering navigation properties in EF Code First

I'm using Code First in EF. Let's say I have two entities:
public class Farm
{
....
public virtual ICollection<Fruit> Fruits {get; set;}
}
public class Fruit
{
...
}
My DbContext is something like this:
public class MyDbContext : DbSet
{
....
private DbSet<Farm> FarmSet{get; set;}
public IQueryable<Farm> Farms
{
get
{
return (from farm in FarmSet where farm.owner == myowner select farm);
}
}
}
I do this so that each user can only see his farms, and I don't have to call the Where on each query to the db.
Now, I want to filter all the fruits from one farm, I tried this (in Farm class):
from fruit in Fruits where fruit .... select fruit
but the query generated doesn't include the where clause, which is very important because I have dozens of thousands of rows and is not efficient to load them all and filter them when they're Objects.
I read that lazy loaded properties get filled the first time they're accessed but they read ALL the data, no filters can be applied UNLESS you do something like this:
from fruits in db.Fruits where fruit .... select fruit
But I can't do that, because Farm has no knowledge of DbContext (I don't think it should(?)) but also to me it just loses the whole purpose of using navigation properties if I have to work with all the data and not just the one that belongs to my Farm.
So,
am I doing anything wrong / making wrong assumptions?
Is there any way I can apply a filter to a navigation property that gets generated to the real query? (I'm working with a lot of data)
Thank you for reading!
Unfortunately, I think any approach you might take would have to involve fiddling with the context, not just the entity. As you've seen, you can't filter a navigation property directly, since it's an ICollection<T> and not an IQueryable<T>, so it gets loaded all at once before you have a chance to apply any filters.
One thing you could possibly do is to create an unmapped property in your Farm entity to hold the filtered fruit list:
public class Farm
{
....
public virtual ICollection<Fruit> Fruits { get; set; }
[NotMapped]
public IList<Fruit> FilteredFruits { get; set; }
}
And then, in your context/repository, add a method to load a Farm entity and populate FilteredFruits with the data you want:
public class MyDbContext : DbContext
{
....
public Farm LoadFarmById(int id)
{
Farm farm = this.Farms.Where(f => f.Id == id).Single(); // or whatever
farm.FilteredFruits = this.Entry(farm)
.Collection(f => f.Fruits)
.Query()
.Where(....)
.ToList();
return farm;
}
}
...
var myFarm = myContext.LoadFarmById(1234);
That should populate myFarm.FilteredFruits with only the filtered collection, so you could use it the way you want within your entity. However, I haven't ever tried this approach myself, so there may be pitfalls I'm not thinking of. One major downside is that it would only work with Farms you load using that method, and not with any general LINQ queries you perform on the MyDbContext.Farms dataset.
All that said, I think the fact that you're trying to do this might be a sign that you're putting too much business logic into your entity class, when really it might belong better in a different layer. A lot of the time, it's better to treat entities basically as just receptacles for the contents of a database record, and leave all the filtering/processing to the repository or wherever your business/display logic lives. I'm not sure what kind of application you're working on, so I can't really offer any specific advice, but it's something to think about.
A very common approach if you decide to move things out the Farm entity is to use projection:
var results = (from farm in myContext.Farms
where ....
select new {
Farm = farm,
FilteredFruits = myContext.Fruits.Where(f => f.FarmId == farm.Id && ...).ToList()
}).ToList();
...and then use the generated anonymous objects for whatever you want to do, rather than trying to add extra data to the Farm entities themselves.
Just figured I'd add another solution to this having spent some time trying to append DDD principles to code first models. After searching around for some time I found a solution like the one below which works for me.
public class FruitFarmContext : DbContext
{
public DbSet<Farm> Farms { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Farm>().HasMany(Farm.FruitsExpression).WithMany();
}
}
public class Farm
{
public int Id { get; set; }
protected virtual ICollection<Fruit> Fruits { get; set; }
public static Expression<Func<Farm, ICollection<Fruit>>> FruitsExpression = x => x.Fruits;
public IEnumerable<Fruit> FilteredFruits
{
get
{
//Apply any filter you want here on the fruits collection
return Fruits.Where(x => true);
}
}
}
public class Fruit
{
public int Id { get; set; }
}
The idea is that the farms fruit collection is not directly accessible but is instead exposed through a property that pre-filters it.
The compromise here is the static expression that is required to be able to address the fruit collection when setting up mapping.
I've started to use this approach on a number of projects where I want to control the access to an objects child collections.
Lazy loading doesn't support filtering; use filtered explicit loading instead:
Farm farm = dbContext.Farms.Where(farm => farm.Owner == someOwner).Single();
dbContext.Entry(farm).Collection(farm => farm.Fruits).Query()
.Where(fruit => fruit.IsRipe).Load();
The explicit loading approach requires two round trips to the database, one for the master and one for the detail. If it is important to stick to a single query, use a projection instead:
Farm farm = (
from farm in dbContext.Farms
where farm.Owner == someOwner
select new {
Farm = farm,
Fruit = dbContext.Fruit.Where(fruit => fruit.IsRipe) // Causes Farm.Fruit to be eager loaded
}).Single().Farm;
EF always binds navigation properties to their loaded entities. This means that farm.Fruit will contain the same filtered collection as the Fruit property in the anonymous type. (Just make sure you haven't loaded into the context any Fruit entities that should be filtered out, as described in Use Projections and a Repository to Fake a Filtered Eager Load.)

Categories