Strange behavior of INCLUDE in Entity Framework - c#

This works:
using (var dbContext = new SmartDataContext())
{
dbContext.Configuration.ProxyCreationEnabled = false;
var query = dbContext.EntityMasters.OfType<Person>();
if (includeAddress)
query.Include(p => p.Addresses);
if (includeFiles)
query.Include(p => p.FileMasters);
output.Entity = query.Include(s=>s.Addresses).FirstOrDefault<Person>(e => e.EntityId == id);
}
while this doesn't:
using (var dbContext = new SmartDataContext())
{
dbContext.Configuration.ProxyCreationEnabled = false;
var query = dbContext.EntityMasters.OfType<Person>();
if (includeAddress)
query.Include(p => p.Addresses);
if (includeFiles)
query.Include(p => p.FileMasters);
output.Entity = query.FirstOrDefault<Person>(e => e.EntityId == id);
}
I am trying to include Addresses, Files based on boolean flags coming from function. However it seems, EF not including them when using IF condition.
This is related to my previous question which actually worked using Include.

You need to assign the result of Include back to query
query = query.Include(p => p.Addresses);

Entity framework's 'Include' function only works when it is connected to the entire linq query that was looking up the entity. This is because the linq query is actually a form of Expression that can be inspected as a whole before it is executed.
In the second example there the Person object is already detached from the database so EF has no information on which table Person came from and how it should join Person with the address table to get the results you want.
If you turn on dynamic proxy generation EF is able to keep track of the relation between the entity and the database. However, I'm not sure if this will make the include statement work.

Related

Entity Framework SQL Query Generation

I am having difficulty understanding how Entity Framework generates a single SQL Query from a couple of Linq methods. Here is a code which selects Username from a table.
var result1 = context.UserMasters
.Where(t => t.UserRole == "LOCAL")
.Skip(100)
.Take(100)
.Select(t => new { t.UserName });
Typically the above code is identical to the below code.
var test = context.UserMasters.Where(t=> t.UserRole == "LOCAL");
test = test.Skip(100);
test = test.Take(100);
var result2 = test.Select(t => new { t.UserName });
I hope we can get results from the table after any Linq Method. So there is a list readily available always. Is it selecting all results from the table initially and doing operation on the list ?
If the type is IQueryable, then the filtering of data happens at the database side. But if the type of query is IEnumerable or other than IQueryable, all the data from the table is fetched and filtering is done at .Net side. It is always advised to use IQueryable in these cases. Please read about IQueryable and IEnumerable difference.

EF Core query from Include but no need to return in result

Okay, so this is probably from not knowing how to use EF core correctly as this is my 2nd day using but I appear to have to run .Include() in order to query against the "inner join" this method creates.
I've got this bit working and filtering down my results, but I don't want to return the include in the model as it's making a 256kb JSON response turn into a 2.8Mb JSON response.
public IEnumerable<Property> GetFeaturedProperties()
{
var properties = _db.Properties
.Include(p => p.GeoInfo)
.Include(p => p.FeaturedUntil)
.ToArray();
var featuredProperties = new List<Property>();
var DateTimeNow = DateTime.Now;
var midnightToday = DateTimeNow.AddHours(-DateTimeNow.Hour)
.AddMinutes(-DateTimeNow.Minute)
.AddSeconds(-DateTimeNow.Second-1);
for (var i = 0; i < properties.Count(); i++)
{
var property = properties[i];
if(property.FeaturedUntil.Any(p => p.FeaturedDate >= midnightToday))
featuredProperties.Add(property);
}
return featuredProperties;
}
So the offending line is .Include(p => p.FeaturedUntil). As this is an Array of dates that can be up anything from 10-1000 rows per joined row. It includes ALL data, even historical so this is really racking up data.
Can I run my query and then run something to .RemoveInclude(p => p.FeaturedUntil)?
You don't need to load the navigation properties in order to apply filtering. When you access a navigation property inside LINQ to Entities query, it's translated to the corresponding SQL construct, including JOINs. No real object/collections are involved. The whole query (with some exceptions) executes at server (database) side.
In your case, the following simple query will do the job:
public IEnumerable<Property> GetFeaturedProperties()
{
var DateTimeNow = DateTime.Now;
var midnightToday = DateTimeNow.AddHours(-DateTimeNow.Hour)
.AddMinutes(-DateTimeNow.Minute)
.AddSeconds(-DateTimeNow.Second-1);
return _db.Properties
.Include(p => p.GeoInfo) // assuming you want to return this data
.Where(p => p.FeaturedUntil.Any(p => p.FeaturedDate >= midnightToday))
.ToList();
}
For more info, see How Queries Work documentation topic.

Entity Framework code-first IQueryable navigation property

Most of the examples I see on the internet show the navigation properties as either ICollection or straight List implementation. They are usually virtual, to enable lazy-loading.
However, when you access such property, it will load the entire collection in memory and if you have a subquery after it (i.e. object.MyListProperty.Where(...)) I have noticed that an SQL query will be issued for each item in the MyListProperty.
How do I avoid this? I want the where clause after the list property to execute on the SQL server, if possible. Can I use an IQueryable navigation property? Is there any best-practice for such case?
My advice for best practise is to disable Lazy loading altogether. Instead force the caller to eagerly load navigation properties through include statements or by using projections.
There are 3rd party products that support include with filters, as described in this post: How to filter include entities in entity framework, but in my experience this further complicates down-stream processing of the objects that are retrieved. If the entity object is loaded outside of method X, because method X can't know for sure if the navigation properties have been loaded with the correct filters, method X starts off by re-querying for the precise rows that it knows it needs.
using (var context = new MyDbContext())
{
context.Configuration.LazyLoadingEnabled = false;
// TODO: load your data
...
}
In this way the records will only be loaded when they are explicitly requested.
When you want access to an IQueryable so you can defer the loading of the data, then make those queries against the DbContext instance and not from the object.
In this example assume that a Customer has many thousands of transactions, so we don't want them to be eagerly or lazy loaded at all.
using (var context = new MyDbContext())
{
context.Configuration.LazyLoadingEnabled = false;
var customer = context.Customers.First(x => x.Id == 123);
...
// count the transactions in the last 30 days for this customer
int customerId = customer.Id;
DateTime dateFrom = DateTime.Today.AddDays(-30)
// different variations on the same query
int transactionCount1 = context.Customers.Where(x => x.Id == customerId)
.SelectMany(x => x.Transactions.Where(x => x.TransactionDate >= dateFrom))
.Count();
int transactionCount2 = context.Customers.Where(x => x.Id == customerId)
.SelectMany(x => x.Transactions)
.Where(x => x.TransactionDate >= dateFrom)
.Count();
int transactionCount3 = context.Transactions.Where(x => x.CustomerId == customerId)
.Where(x => x.TransactionDate >= dateFrom)
.Count();
}
It is good that you have identified that you want to use an IQueryable<T> we access them from the DbContext directly, not from the instances that were previously retrieved.

Entity Framework eager loading based on a specific attribute

The current setup of EF in my application is lazy loading, which is great for the most part. However, I am lost trying work out how to load a list of related entities based on their IsEnabled bit attribute.
In this example I am just returning a list of entities.
return Context.Entities.ToList()
Let's say the Entities object contains a list of ChildEntities like so:
public class Entities
{
private string EntityName;
private List<ChildEntities> ChildEntities;
}
public class ChildEntites
{
private string ChildEntityName;
private bool IsEnabled;
}
I want to only want to get out the ChildEntities based on their IsEnabled flag when loading the list of Entities.
You can use the Include() method to load all child entities and then select only those which are enabled like
Context.Entities.Include("ChildEntites").Select(c => e.IsEnabled == true)
Another way would be to get the filter entities and then run the query as shown in this post
var data = from e in Context.Entities
select new
{
Entities = e,
Childs = e.ChildEntites.Where(c => c.IsEnabled == true)
};
var Results = data.ToArray().Select(x => x.Entities);
I think there is no way to filter when you load related entities in case that you use lazy loading or eager loading unless you project your query to an anonymous type or a DTO, but if you have an entity instance,you can load related entities based on a condition using explicit loading:
var entity=context.Entities.FirstOrDefault();
context.Entry(entity)
.Collection(b => b.ChildEntities)
.Query()
.Where(ce => ce.IsEnabled == true)
.Load();
If that doesn't satisfy what you are trying to achieve because you need to load the entire entity collection, then, as I said before, you should project your query to a custom class or an anonymous type:
var query= from e in Context.Entities.Include(c=>c.ChildEntities)
select new EntityDTO
{
EntityName= e.EntityName,
ChildEntites= e.ChildEntites.Where(c => c.IsEnabled == true)
};
Using a projection
var entities = context.Entities
.Select(x => new {x, x.ChildEntities.Where(y => y.IsEnabled))
.ToList() // resolve from database before selecting the main entity
.Select(x => x.x);
Using a third party library
EF+ Query IncludeFilter allow you to easily filter related entities
var entities = context.Entities.IncludeFilter(x => x.ChildEntities.Where(y => y.IsEnabled))
.ToList();
You can find the documentation here
Disclaimer: I'm the owner of the project EF+.
A couple ways I would recommend this approach. I would either lazy load where IsEnabled = false and eager load in a separate call where IsEnabled = true OR once you have the lazy loaded collection, in a separate call, get the children where IsEnabled = true. I don't believe you will be able to do this in one call. The other option would be a stored procedure. I hope this helps.
I had a similar problem. I solved it in the following manner.
Create a new method in your model Entities. Lets call it ChildEntitiesEnabled
public ICollection<ChildEntity> ChildEntitiesEnabled()
{
//First I get the full list using the lazy loading...
var allChildEntities=ChildEntities.ToList();
//do further processing if there is data
if(allChildEntities!=null && allChildEntities.Count()>0)
{
var childEntitiesEnabled = ChildEntities.Where(x=>x.Enabled==true).ToList();
return childEntitiesEnabled;
}
return null; //or you can return an empty list...
}
I like this method because you can use it anywhere the model is available without complicated code strewn all over the place. Also, you dont lose all the ChildEntities data... that is available too from the original call.

Use linq to generate direct update without select

G'day everyone.
I'm still learning LINQ so forgive me if this is naive. When you're dealing with SQL directly, you can generate update commands with conditionals, without running a select statement.
When I work with linq I seem to follow the pattern of:
Select entities
Modify entities
Submit changes
What I want to do is a direct update using linq and deferred execution. Is it possible that the actual execution occurs directly at the SQL without any data being transmitted up to the client?
DataContext dc = new DataContext
var q = from product in dc.Products
where product.Type = 1
set product.Count = 0
dc.SubmitChanges
So in essence LINQ has all the information it needs WITHOUT using a select to generate an update command. It would run the SQL:
Update Products Set Count = 0 Where Type = 1
Does a keyword like "set" exist in LINQ?
You can actually let LINQ-to-SQL generate update statements:
Foo foo=new Foo { FooId=fooId }; // create obj and set keys
context.Foos.Attach(foo);
foo.Name="test";
context.SubmitChanges();
In your Dbml set UpdateCheck="Never" for all properties.
This will generate a single update statement without having to do a select first.
One caveat: if you want to be able to set Name to null you would have to initialize your foo object to a different value so Linq can detect the change:
Foo foo=new Foo { FooId=fooId, Name="###" };
...
foo.Name=null;
If you want to check for a timestamp while updating you can do this as well:
Foo foo=new Foo { FooId=fooId, Modified=... };
// Modified needs to be set to UpdateCheck="Always" in the dbml
No, neither LINQ nor LINQ to SQL has set-based update capabilities.
In LINQ to SQL, you must query for the object you wish to update, update the fields/properties as necessary, then call SubmitChanges(). For example:
var qry = from product in dc.Products where Product.Name=='Foobar' select product;
var item = qry.Single();
item.Count = 0;
dc.SubmitChanges();
If you wish to do batching:
var qry = from product in dc.Products where Product.Type==1 select product;
foreach(var item in qry)
{
item.Count = 0;
}
dc.SubmitChanges();
Alternatively, you could write the query yourself:
dc.ExecuteCommand("update Product set Count=0 where Type=1", null);
The PLINQO (http://plinqo.com) framework is using the LINQ batch update to perform updates
context.Task.Update(t => t.Id == 1, t2 => new Task {StatusId = 2});
This will perform a Update Task Set StatusId = 2 Where Id = 1
Linq 2 SQL doesn't have direct insert/update/delete equivalents of SQL. In V1 the only updates you can do using linq is thought SubmmitChanges on the context or if you fallback to sql.
However some people have tried to overcome this limitation of linq using custom implementations.
Linq batch update.
Use this extension method: EntityExtensionMethods.cs
public static void UpdateOnSubmit<TEntity>(this Table<TEntity> table, TEntity entity, TEntity original = null)
where TEntity : class, new()
{
if (original == null)
{
// Create original object with only primary keys set
original = new TEntity();
var entityType = typeof(TEntity);
var dataMembers = table.Context.Mapping.GetMetaType(entityType).DataMembers;
foreach (var member in dataMembers.Where(m => m.IsPrimaryKey))
{
var propValue = entityType.GetProperty(member.Name).GetValue(entity, null);
entityType.InvokeMember(member.Name, BindingFlags.SetProperty, Type.DefaultBinder,
original, new[] { propValue });
}
}
// This will update all columns that are not set in 'original' object. For
// this to work, entity has to have UpdateCheck=Never for all properties except
// for primary keys. This will update the record without querying it first.
table.Attach(entity, original);
}
To use it, make sure the entity object that you pass to UpdateOnSubmit method has all the primary key properties set for the record you want to update. This method will then update the record with the remaining properties from the entity object without pulling the record first.
After calling UpdateOnSubmit, make sure to call SubmitChanges() for changes to apply.
You can use Entity Framework Extensions library, it supports batch update and batch merge, however the library is not free:
PM > Install-Package Z.EntityFramework.Extensions
using Z.EntityFramework.Plus;
...
dc.Products
.Where(q => q.Type == 1)
.Update(q => new Product { Count = 0 });
Try this :
dbEntities.tblSearchItems
.Where(t => t.SearchItemId == SearchItemId)
.ToList()
.ForEach(t => t.isNew = false);
dbEntities.SaveChanges();

Categories

Resources