I'm trying to use Laravel Eager Loading in my project and I have read the documentation about it. Every example in documentation is about getting all instances of a model with Eager Loading. But is it just about getting all instance not just a single model?
Consider this :
public function single(Coin $coin)
{
$coin = $coin->with('exchanges')->get();
return view('public.coin',compact('coin'));
}
It is a controller method which loads a single Coin using route binding and injects the instance and I'm trying to eager load relations of that coin. but when I access $coin in my blade view file I get a list of all coins. So I can't eager load an injected model instance?!
You are looking for Lazy Eager Loading.
You can achieve what you want using:
public function single(Coin $coin)
{
$coin->load('exchanges');
return view('public.coin',compact('coin'));
}
Also, you can eager load relations when retrieving a single model like:
Coin::with('exchanges')->find($id);
Related
When using the examples for Single Page Application, I've the following TodoItem controller:
public partial class MVC4TestController : DbDataController<MVC4TestContext>
{
public IQueryable<TodoItem> GetTodoItems()
{
return DbContext.TodoItems.OrderBy(t => t.TodoItemId);
}
}
Question 1:
It seems that only EntityModels are supported ?
When using a real ViewModel (model only used for the Views, not not used as 1:1 mapping to database entity), the DbDataController does not support this.
Also using Linq.Translations or PropertyTranslator does not seem to work, see this code extract:
private static readonly CompiledExpressionMap<TodoItem, string> fullExpression =
DefaultTranslationOf<TodoItem>.Property(t => t.Full).Is(t => t.Title + "_" + t.IsDone);
public string Full
{
get
{
return fullExpression.Evaluate(this);
}
}
Question 2:
What is the recommended design when using SPA, DBContext and ViewModels ?
As far as I know so far is - it instists on the usage of "real" model classes bound to DbContext.
I have the same problem as you - I need to use my own DTO objects which are "flat".
The Json serialisation is currently not able to serialize data which has parent references in child objects (cyclic references). Usually I do not need the entity tree anyways so I created smaller classes which fits perfectly to the view.
I tried to use a normal Controller with JsonResult and parsed the returned model into ko.mapping.fromJS after retrieved the data. Thats working fine. But - you have to take care of all the nice stuff the MVC4 generated viewmodels are already dealing with (like creating navigation, etc.).
Maybe someone finds a workaround to "fake" a DbContext with DTO data.
So I'm using Entity Framework Code First (so no .edmx)
I have a base entity class with a bool IsEnabled to do soft delete's
I am using repository pattern so all queries against the repository can be filtered out with IsEnabled.
However any time I use the repository to get an MyType which is IsEnabled, Lazy Loading MyType.Items may mean that Items could be not enabled.
Is there a way, perhaps with EF Fluent to describe how to do filtering on tables?
Update:
If I have a Dbset
public class UnitOfWork : DbContext
{
private IDbSet<MyObj> _MyObj;
public IDbSet<MyObj> MyObjs
{
get { return _MyObj ?? (_MyObj = base.Set<MyObj>()); }
}
}
Is there any way I can tell the DbContext to filter the DbSet?
No, there is no way to define a filter for lazy loading (also not for eager loading using Include). If you want that your navigation collections only get populated with items where IsEnabled is true you can only shape your queries accordingly, for example with explicit loading:
context.Entry(parent).Collection(p => p.Items).Query()
.Where(i => i.IsEnabled)
.Load();
This will populate the Items collection of parent only with the enabled items.
Edit
I feel a bit like the messenger of the bad news about a lost battle who gets his head knocked off. Maybe it's too hard to believe that Entity Framework sometimes does not have the capabilities you want. To improve my chance to convince you I add a quote from an authority, Julie Lerman:
Neither eager loading nor deferred/lazy loading in the Entity
Framework allows you to filter or sort the related data being
returned.
It looks like it is still possible. If you are intrested you can take a look at the Wiktor Zychla blogpost, where he gives a solution to the soft delete problem.
This http://blogs.claritycon.com/blog/2012/01/25/a-smarter-infrastructure-automatically-filtering-an-ef-4-1-dbset/ basically defines how I can achieve what I was looking for.
Basically you create a FilteredDbSet and make all your DbContext IDbSet's return it.
I created a generic repository that handles querying my entities.
When I call this:
public IQueryable<TEntity> GetQuery()
{
return _context.Set<TEntity>().AsQueryable();
}
I get back the entire entity including all child entities.
When I call this:
public TEntity GetById(Guid id)
{
return GetQuery().Where(e => e.Id == id).FirstOrDefault();
}
I have to specify what child entities to include.
Is there a way to get back ALL child entities without having to write includes for each entity?
Lazy loading is enabled by default. This means that the collections will be loaded when you access them, not when you retrieve the parent object e.g.
foreach (var parent in repo.GetQuery()) {
foreach (var child in parent.Children) {
// do something
}
}
If you wish to eagerly load your entities you could subclass your generic repository and override the methods where you wish to use the Include lambda. Alternatively there is an Include method that accepts a string list of associations to include, which you could expose on your generic repository.
Update:
Not quite sure why you gave my answer -1 but as further clarification.
You stated regarding your GetQuery() method:
I get back the entire entity including
all child entities.
The child entities are lazily loaded, whether you access the collections in debug or output them on your page.
The single query should work, with lazy loading enabled.
And AFAIK, with lazy loading disabled, this doesn't mean that all the collections are loaded automatically, quite the opposite, you have to explicitly load them by calling Include.
I'm using Entity Framework 4.1 code first approach.
I want to make eager loading as my the dafault configuration, and by that avoid using the Include extension method in each fetching query.
I did as recomended in MSDN, changing the simple lazy property at the DbContext constructor:
public class EMarketContext : DbContext
{
public EMarketContext()
{
// Change the default lazy loading to eager loading
this.Configuration.LazyLoadingEnabled = false;
}
}
unfortunately, this approach is not working. I have to use the Include method to perform eager loading in each query. Any ideas why?
Thanks in advance.
There is no default configuration for eager loading. You must always define Include or create some reusable method which will wrap adding include. For example you can place similar method to your context:
public IQueryable<MyEntity> GetMyEntities()
{
return this.MyEntities.Include(e => e.SomeOtherEntities);
}
I have two entities in 1:n relationship: Link, Category. At the first I get all categories and Links and put them to list. Next I fill the links of every category manually but category.Links.Add(link) connect to db and get the link again and it cause double data in the result. I know this action is because of lazy loading. the lazy loading is true and I do not want to disable it. How can I fill links of every category manually without connecting to db again?
Please do not offer Eager loading or disabeling lazy load.
var categories = categoryRepository.GetCategories().ToList();
var allLinks = linkRepository.GetLinks().ToList();
foreach (var category in categories)
{
var links = allLinks.Where(l => l.CategoryID == category.CategoryID);
foreach (var link in links)
{
category.Links.Add(link);
}
}
Even you mentioned that you don't want to turn off the lazy loading I insists you to do that because lazy loading will be triggered always when you access the navigation property first time. The only way to avoid this is:
Turning lazy loading off. The disadvantage is that you must turn lazy loading off for whole processing of categories. Because once you turn it on it will immediately reload Links during next access to the property. The reason is that EntityCollection used for handling Links has property IsLoaded which is false (and cannot be modified except by calling Load).
Removing virtual keyword from Links collection. This will disallow wrapping only Category by dynamic proxy and because of that it will not allow lazy loading. It will be even more interesting because the only needed code in your loading should be first two lines - links will be populated to categories automatically during second query execution. The problem here is that removing virtual keyword from auto generated entities is hard (it must be hard coded to T4 template) or you must use own entity.
Somehow cheating EF by replacing EntityCollection in Links property with another collection. This is not easy because you can't just assign new collection to Links - dynamic proxy will throw InvalidOperationException. You must create second partial part of your entity and add some code which will modify directly private field holding the collection (bypassing the property). Even such approach can have some bad consequences. Default code uses some fixups etc.
Turning lazy loading off will not break your functionality - actually EF is doing this quite often internally. You just need to do:
context.ContextOptions.LazyLoadingEnabled = false;