using(DataContext db = new DataContext ())
{
var result = db.SomeTable.ToList();
return result;
}
Problem is after i have returned the result, the connection is closed and because its closed, it crashes when i am trying to access any of the child elements. That happens because with lazy loading set to True ( default ) it never loads the child relations before they are used and i start using them AFTER the connection is closed. So how is the best way to solve this?
I have tried to turn off the lazy loading but then it didnt load any of the child relation tables.
You can always explicitly load your child collections:
var result = db.SomeTable.Include("SomeChildCollectionName")
.Include("AnotherChildCollectionName")
.ToList();
You can use the .include() method.
var result = db.SomeTable.Include("ChildEntitySet").ToList();
You can also add a result.ChildEntitySet.Load() call before returning. This is less efficient as it will result in two trips to the server. Using the .Include() method will generate a SQL Statement with a JOIN allowing only one trip to the server.
Related
The property is defined as virtual. But before I access the order property, the order entity's data has been loaded, why?
Full source code:
A couple of things:
When you state: doesn't work, you are getting an Order back, but the $ figure is 0.0 when you expect a different value? You appear have 2x order records, but based on what is coming back you're expecting a non-zero figure, are both records non-zero? In your debug view, expand the "Orders" in the pop-up context menu, this will reveal what order details EF has loaded.
Firstly you should be careful around the use of "OrDefault" renditions of the methods. Your code is assuming that a value is returned. In these cases you're better off using Single() or First() as applicable.
Additionally, when using First you should be specifying an OrderBy clause to ensure that there is a reliable ordering.
SaveChanges Should only be called if you modify data.
Lastly, lazy loading is an enabler for loading infrequently used data on demand. You should largely work to avoid relying on lazy-load calls. If you need the entire entities and you know you are going to use orders, then eager-load them.
I.e.
using (var context = new EfContext())
{
var customer = context.Customers
.Include(c => c.Orders)
.Single(c => c.CustomerId = customerId);
// Do stuff...
}
If you want just 1 applicable order for a given employee then consider using Select to retrieve it:
I.e.
using (var context = new EfContext())
{
var data = context.Customers.Where(c => c.CustomerId = customerId)
.Select(c => new { Customer = c, FirstOrder = c.Orders.OrderBy(o => o.OrderDate).FirstOrDefault()})
.Single();
// Do Stuff...
}
This will give you an anonymous type containing the Customer (without eager loading the orders) and the matching first order.
Better is just to use Select to retrieve the specific fields you need from customer and order(s). This reduces the amount of data (rows and columns) pulled back from the database.
sorry,It looks like the problem of visual studio,When the call statement is commented,the order table's sql querie no longer being executed,but when the call statement is included,although the breakpoint is set,the order table's sql querie still be executed.
visual studio debug execute the Expression automatically
comment the staement
sql profiler
include the statement
sql profiler
Considering a table [Person] which has two foreign keys ([Phone_Numbers], [Business_Information]). When using EF Core, we can simply find a Person using the dbContext.Find method like var person = await db.Context.FindAsync<Person>(1); however, the Find method only looks for the entity inside the tracking cache and it does not handle relational properties. In order to solve this, we can call the Entry method to attach those properties like dbContext.Entry<Person>(person).Reference(x=> x.Business_Information). Considering the provided example, we have to call the Entry method twice in this case:
dbContext.Entry<Person>(person).Reference(x=> x.Business_Information).Load();
dbContext.Entry<Person>(person).Collection(x=> x.Phone_Numbers).Load();
An alternative solution is to use the Include method:
var person = await dbContext.Set<Person>().Include("Business_Information").Include("Phone_Numbers").FirstOrDefaultAsync(x=> x.id == id);
The first solution sends two request to the Db (I think the Find method does not send a request if the entity is being tracked); however, I'm not sure how the second one works and accordingly I'm also unsure if it has any performance advantages. I've been thinking the first solution could be faster and more efficient. I'd appreciate if someone clarifies this for me.
It really depends on number of related properties, their type (reference or collection) and in the first case - if they are already loaded or not.
Let say your entity has N reference navigation properties and M collection navigation properties that you want to load.
The approach with Include will always execute 1 + M db queries - one for the entity + reference properties which data is retrieved with JOINs to the corresponding tables and returned as columns in the query result) and one for each collection - regardless of whether the entity and any of the related entities/collections is already loaded.
The approach with explicit loading is more dynamic.
It will execute 1 db query for the entity if it's not loaded in the context, 0 otherwise.
For each reference navigation property it will execute 1 db query if the referenced entity is not already loaded in the context, 0 otherwise.
For each collection navigation property, it will execute 1 db query if the collection is not marked as loaded (db.Entry(entity).Collection(e => e.Collection).IsLoaded == false), 0 otherwise.
At the end, the explicit loading approach could execute between 0 and 1 + N + M db queries.
With all that being said, it's not clear which one is better. If you are using relatively short lived DbContext instances, hence the chances of not execution related queries is low, I would go with Include approach because it is deterministic.
Why should I put .Include(...) while the view has visibility on all navigation properties?
action1
public ActionResult Included(){
var models = db.Money.Include(m=>m.Rate);
return View(models);
}
action2
public ActionResult UnIncluded(){
var models = db.Money;
return View(models);
}
In both views I may get the amount as follows
#Html.DisplayFor(model=>model.Rate.Amount)
So, what is the difference?
Is this what we know as lazy load mode?
So, what is the difference? Is this what we know as lazy load mode?
Yes.
Assume your Money table has 1000 rows. Also assuming that Rate is a navigation property, the following will execute 1001 separate SQL queries:
public ActionResult UnIncluded(){
var models = db.Money;
return View(models);
}
...
#Html.DisplayFor(model=>model.Rate.Amount)
Why? The above action will return only the Money data to the view. Then the view, while iterating over each Money instance, will lazily load each and every .Rate instance. This means 1000 extra SQL queries, one for each Money item in the collection.
However, the following will only execute 1 SQL query:
public ActionResult Included(){
var models = db.Money.Include(m=>m.Rate);
return View(models);
}
...
#Html.DisplayFor(model=>model.Rate.Amount)
Why? The above action will execute a single SQL query that performs a JOIN between the Money and Rate tables to load all of the Rate data for each of the 1000 Money rows in only one round trip to the database. This is known as eager-loading.
As #IronMan84 says, this is assuming that you have lazy loading enabled, and that Rate is a virtual navigation property on Money.
.Include forces the specified property to be loaded eagerly, as opposed to lazily, assuming that lazy loading is turned on in your application. So in your first action Rate will have been brought back from the database right away, as opposed to your second action in which it will only be brought over (again, assuming lazy loading is on) when it's called for (i.e. when you call for model.Rate.Amount on the page).
One thing that you should be aware of: If you're using lazy loading, it would actually behoove you to use .Include or to use .ToList() in your controller action, because you could possibly run into situations in which you get an exception because your controller has disposed the DbContext object before the View calls for a navigational property that requires EF to go back to the database. So Action1 above might be the better way to go just to avoid that situation.
MSDN post on Include can be found here.
I would like to be able to iterate through every row in an entity table without holding every row in memory. This is a read only operation and every row can be discarded after being processed.
If there is a way to discard the row after processing that would be fine. I know that this can be achieved using a DataReader (which is outside the scope of EF), but can it be achieved within EF?
Or is there a way to obtain a DataReader from within EF without directly using SQL?
More detailed example:
Using EF I can code:
foreach (Quote in context.Quotes)
sw.WriteLine(sw.QuoteId.ToString()+","+sw.Quotation);
but to achieve the same result with a DataReader I need to code:
// get the connection to the database
SqlConnection connection = context.Database.Connection as SqlConnection;
// open a new connection to the database
connection.Open();
// get a DataReader for our table
SqlCommand command = new SqlCommand(context.Quotes.ToString(), connection);
SqlDataReader dr = command.ExecuteReader();
// get a recipient for our database fields
object[] L = new object[dr.FieldCount];
while (dr.Read())
{
dr.GetValues(L);
sw.WriteLine(((int)L[0]).ToString() + "," + (string)L[1]);
}
The difference is that the former runs out of memory (because it is pulling in the entire table in the client memory) and the later runs to completion (and is much faster) because it only retains a single row in memory at any one time.
But equally importantly the latter example loses the Strong Typing of EF and should the database change, errors can be introduced.
Hence, my question: can we get a similar result with strongly typed rows coming back in EF?
Based on your last comment, I'm still confused. Take a look at both of below code.
EF
using (var ctx = new AppContext())
{
foreach (var order in ctx.Orders)
{
Console.WriteLine(order.Date);
}
}
Data Reader
var constr = ConfigurationManager.ConnectionStrings["AppContext"].ConnectionString;
using (var con = new SqlConnection(constr))
{
con.Open();
var cmd = new SqlCommand("select * from dbo.Orders", con);
var reader = cmd.ExecuteReader();
while (reader.Read())
{
Console.WriteLine(reader["Date"]);
}
}
Even though EF has few initial query, both of them execute similar query that can be seen from profiler..
I haven't tested it, but try foreach (Quote L in context.Quotes.AsNoTracking()) {...}. .AsNoTracking() should not put entities in cache so I assume they will be consumed by GC when they out of the scope.
Another option is to use context.Entry(quote).State = EntityState.Detached; in the foreach loop. Should have the similar effect as the option 1.
Third option (should definitely work, but require more coding) would be to implement batch processing (select top N entities, process, select next top N). In this case make sure that you dispose and create new context every iteration (so GC can eat it:)) and use proper OrderBy() in the query.
You need to use an EntityDataReader, which behaves in a way similar to a traditional ADO.NET DataReader.
The problem is that, to do so, you need to use ObjectContext instead of DbContext, which makes things harder.
See this SO answer, not the acepted one: How can I return a datareader when using Entity Framework 4?
Even if this referes to EF4, in EF6 things work in the same way. Usually an ORM is not intended for streaming data, that's why this functionality is so hidden.
You can also look at this project: Entity Framework (Linq to Entities) to IDataReader Adapter
I have done this by pages.
And cleaning the Context after each page load.
Sample:
Load first 50 rows
Iterate over them
Clean the Context or create a new one.
Load second 50 rows
...
Clean the Context = Set all its Entries as Detached.
In a repository, I do this:
public AgenciesDonor FindPrimary(Guid donorId) {
return db.AgenciesDonorSet.Include("DonorPanels").Include("PriceAdjustments").Include("Donors").First(x => x.Donors.DonorId == donorId && x.IsPrimary);
}
then down in another method in the same repository, this:
AgenciesDonor oldPrimary = this.FindPrimary(donorId);
In the debugger, the resultsview shows all records in that table, but:
oldPrimary.Count();
is 1 (which it should be).
Why am I seeing all table entries retrieved, and not just 1? I thought row filtering was done in the DB.
If db.EntitySet really does fetch everything to the client, what's the right way to keep the client data-lite using EF? Fetching all rows won't scale for what I'm doing.
You will see everything if you hover over the AgenciesDonorSet because LINQ to Entities (or SQL) uses delayed execution. When the query is actually executed, it is just retrieving the count.
If you want to view the SQL being generated for any query, you can add this bit of code:
var query = queryObj as ObjectQuery; //assign your query to queryObj rather than returning it immediately
if (query != null)
{
System.Diagnostics.Trace.WriteLine(context);
System.Diagnostics.Trace.WriteLine(query.ToTraceString());
}
Entity Set does not implement IQueryable, so the extension methods that you're using are IEnumerable extension methods. See here:
http://social.msdn.microsoft.com/forums/en-US/linqprojectgeneral/thread/121ec4e8-ce40-49e0-b715-75a5bd0063dc/
I agree that this is stupid, and I'm surprised that more people haven't complained about it. The official reason:
The design reason for not making
EntitySet IQueryable is because
there's not a clean way to reconcile
Add\Remove on EntitySet with
IQueryable's filtering and
transformation ability.