Paging and sorting Entity Framework on a field from Partial Class - entity-framework

I have a GridView which needs to page and sort data which comes from a collection of Customer objects.
Unfortunately my customer information is stored separately...the customer information is stored as a Customer ID in my database, and the Customer Name in a separate DLL.
I retrieve the ID from the database using Entity Framework, and the name from the external DLL through a partial class.
I am getting the ID from my database as follows:
public class DAL
{
public IEnumberable<Customer> GetCustomers()
{
Entities entities = new Entities();
var customers = (from c in entities.Customers
select c);
//CustomerID is a field in the Customer table
return customers;
}
}
I have then created a partial class, which retrieves the data from the DLL:
public partial class Customer
{
private string name;
public string Name
{
if (name==null)
{
DLLManager manager = new DLLManager();
name= manager.GetName(CustomerID);
}
return name;
}
}
In my business layer I can then call something like:
public class BLL
{
public List<Customer> GetCustomers()
{
DAL customersDAL = new DAL();
var customers = customersDAL.GetCustomers();
return customers.ToList();
}
}
...and this gives me a collection of Customers with ID and Name.
My problem is that I wish to page and sort by Customer Name, which as we have seen, is populated from a DLL. This means I cannot page and sort in the database, which is my preferred solution. I am therefore assuming I am going to have to call of the database records into memory, and perform paging and sorting at this level.
My question is - what is the best way to page and sort an in-memory collection. Can I do this with my List in the BLL above? I assume the List would then need to be stored in Session.
I am interested in people's thoughts on the best way to page and sort a field that does not come from the database in an Entity Framework scenario.
Very grateful for any help!
Mart
p.s. This question is a development of this post here:
GridView sorting and paging Entity Framework with calculated field
The only difference here is that I am now using a partial class, and hopefully this post is a little clearer.

Yes, you can page and sort within you list in the BLL. As long as its fast enough I wouldn't care to much about caching something in the session. An other way would be to extend your database with the data from you DLL.

I posted this question slightly differently on a different forum, and got the following solution.
Basically I return the data as an IQueryable from the DAL which has already been forced to execute using ToList(). This means that I am running my sorting and paging against an object which consists of data from the DB and DLL. This also allows Scott's dynamic sorting to take place.
The BLL then performs OrderBy(), Skip() and Take() on the returned IQueryable and then returns this as a List to my GridView.
It works fine, but I am slightly bemused that we are perfoming IQueryable to List to IQueryable to List again.
1) Get the results from the database as an IQueryable:
public class DAL
{
public IQueryable<Customer> GetCustomers()
{
Entities entities = new Entities();
var customers = (from c in entities.Customers
select c);
//CustomerID is a field in the Customer table
return customers.ToList().AsQueryable();
}
}
2) Pull the results into my business layer:
public class BLL
{
public List<Customer> GetCustomers(intint startRowIndex, int maximumRows, string sortParameter)
{
DAL customersDAL = new DAL();
return customersDAL.GetCustomers().OrderBy(sortParameter).Skip(startRowIndex).Take(maximumRows).ToList();
}
}
Here is the link to the other thread.
http://forums.asp.net/p/1976270/5655727.aspx?Paging+and+sorting+Entity+Framework+on+a+field+from+Partial+Class
Hope this helps others!

Related

Inserting and updating disconnected entities in EF code first

I am retrieving data about books from lots of different sources such as XML and web services which I then store in the database using EF Code First 6 via a Generic Repository and and obviously DbContext.
The problem is that performance is very bad.
I have the following (fictional but analogous) POCO in my Model
public class Book
{
public int Id {get; set;}
public string Title {get; set;}
}
also
public class BookDataSource
{
public int Id {get; set;}
public virtual List<Book> Books {get; set;};
}
So I retrieve the book data from some source and construct the above book object.
I then need to check whether the book already exists in the DB and update it if it does or insert it if it does not. I also need to then delete any books that no longer exist on the data source.
//The following method takes the data source (type: IBookDataSource) to update from as the parameter
public string UpdateBooks(BookDatasource dataSource)
{
string successMessage = "";
//Disconnected entities
List<Book> retreivedBooks= dataSource.RetreiveBooks();
foreach (Book retreivedBook in retreivedBooks)
{
//Check if the dataSource already contains a book (based on title)
Book localBook =
dataSource.Books.SingleOrDefault(
b => b.Title== retreivedBook.Title);
if (localBook ==null)
{
//Insert a new one
_unitOfWork.BookRepository.Insert(retreivedBook);
}
else
{
//Update existing
localBook.Title= retreivedPortalMerchant.PortalsMerchantName;
_unitOfWork.PortalMerchantRepository.Update(localPortalMerchant);
}
}
//Soft delete any existing ones that no longer exist in the received data
foreach (Book existingBook in dataSource.Books)
{
if ( !retreivedBooks.Exists(
b => m.Title == existingBook.Title))
{
existingBook.Deleted = true;
_unitOfWork.PortalMerchantRepository.Update(existingBook);
}
}
}
However the performance is very bad. Sometimes there are 25000 books retrieved from the data source and I am having to do two for loops. ForEach retreived book, check if one exists in the db the insert/update accordingly. And another one to loop all existing books and check whether it no longer exists on the datasource and soft delete.
Is there a better way to attach the entities and monitor their state. In the above example I think I am querying the context each time and not the DB so why such bad performance. Should I revert to T-SQL?
For the proper algorithm of inserting-updating-deleting disconnected entities, you can check "Setting the State of Entities in a Graph" section of "Chapter 4. Working with Disconnected Entities Including N-Tier Applications" of "Programming Entity Framework: DbContext by Julia Lerman, Rowan Miller" book.
Also in this SO answer some ways to increase performance of EF is explained. The answer is for bulk inserting however it may work for your scenerio also.
The fastest way would be using bulk insert extension
Here's maxlego's description:
It uses SqlBulkCopy and custom datareader to get max performance. As a result it is over 20 times faster than using regular insert or AddRange EntityFramework.BulkInsert vs EF AddRange
context.BulkInsert(hugeAmountOfEntities);

EF Filtering a Child Table with Lazy Load

I'm using entity framework with POCOs and the repository pattern and am wondering if there is any way to filter a child list lazy load. Example:
class Person
{
public virtual Organisation organisation {set; get;}
}
class Organisation
{
public virtual ICollection<Product> products {set; get;}
}
class Product
{
public bool active {set; get;}
}
Currently I only have a person repository because I'm always starting from that point, so ideally I would like to do the following:
Person person = personRepo.GetById(Id);
var products = person.organisation.products;
And have it only load products where active = true from the database.
Is this possible and if so how?
EDIT My best guess would be either a filter can be added to the configuration of the entity. Or there might be a way to intercept/override the lazy load call and modify it. Obviously if I created an Organisation Repository I could manually load it as I please but I am trying to avoid that.
There's not a direct way to do this via lazy loading, but if you were willing to explicitly load the collection, you could follow whats in this blog, see the Applying filters when explicitly loading related entities section.
context.Entry(person)
.Collection(p => p.organisation.products)
.Query()
.Where(u => u.IsActive)
.Load();
You can do what Mark Oreta and luksan suggest while keeping all the query logic within the repository.
All you have to do is pass a Lazy<ICollection<Product>> into the organization constructor, and use the logic they provided. It will not evaluate until you access the value property of the lazy instance.
UPDATE
/*
First, here are your changes to the Organisation class:
Add a constructor dependency on the delegate to load the products to your
organization class. You will create this object in the repository method
and assign it to the Person.Organization property
*/
public class Organisation
{
private readonly Lazy<ICollection<Product>> lazyProducts;
public Organisation(Func<ICollection<Product>> loadProducts){
this.lazyProducts = new Lazy<ICollection<Product>>(loadProducts);
}
// The underlying lazy field will not invoke the load delegate until this property is accessed
public virtual ICollection<Product> Products { get { return this.lazyProducts.Value; } }
}
Now, in your repository method, when you construct the Person object you will assign the Organisation property with an Organisation object containing the lazy loading field.
So, without seeing your whole model, it will looks something like
public Person GetById(int id){
var person = context.People.Single(p => p.Id == id);
/* Now, I'm not sure about the cardinality of the person-organization or organisation
product relationships, but let's assume you have some way to access the PK of the
organization record from the Person and that the Product has a reference to
its Organisation. I may be misinterpreting your model, but hopefully you
will get the idea
*/
var organisationId = /* insert the aforementioned magic here */
Func<ICollection<Product>> loadProducts = () => context.Products.Where(product => product.IsActive && product.OrganisationId == organisationId).ToList();
person.Organisation = new Organisation( loadProducts );
return person;
}
By using this approach, the query for the products will not be loaded until you access the Products property on the Organisationinstance, and you can keep all your logic in the repository. There's a good chance that I made incorrect assumptions about your model (as the sample code is quite incomplete), but I think there is enough here for you to see how to use the pattern. Let me know if any of this is unclear.
This might be related:
Using CreateSourceQuery in CTP4 Code First
If you were to redefine your properties as ICollection<T> rather than IList<T> and enable change-tracking proxies, then you might be able to cast them to EntityCollection<T> and then call CreateSourceQuery() which would allow you to execute LINQ to Entities queries against them.
Example:
var productsCollection = (EntityCollection<Product>)person.organisation.products;
var productsQuery = productsCollection.CreateSourceQuery();
var activeProducts = products.Where(p => p.Active);
Is your repository using something like:
IQueryable<T> Find(System.Linq.Expressions.Expression<Func<T, bool>> expression)
If so you can do something like this:
var person = personRepo.Find(p => p.organisation.products.Any(e => e.active)).FirstOrDefault();
You could possibly use Query() method to achieve this. Something like:
context.Entry(person)
.Collection(p => p.organisation.products)
.Query()
.Where(pro=> pro.Active==true)
.Load();
Have a look at this page click here

I get the error : “An entity object cannot be referenced by multiple instances of IEntityChangeTracker.” with .net MVC2 and the Entity Framework4

I have a big problem since some days and I’m a very beginner in the Entity Framework.
I have 2 entities : Group and News. A news is visible by one or many groups. I use two repositories (newsRepository and groupsRepository).
This is my Create method for the news :
public ActionResult Create()
{
return View(new CreateNewsViewModel(new News()));
}
[HttpPost]
public ActionResult Create(CreateNewsViewModel model)
{
model.news.CategoryId = Int32.Parse(Request.Form["news.CategoryId"]);
if (ModelState.IsValid)
{
News news = new News();
DateTime date = DateTime.Now;
//AuthorId a recuperer
news.AuthorId = 1;
news.Title = IntranetTools.UppercaseFirst(model.news.Title.Trim());
news.Content = model.news.Content;
news.IsVisible = Request.Form["news.IsVisible"].Contains("true");
news.CreateDate = date;
news.PublicationDate = date;
news.LastChangedDate = date;
news.CategoryId = model.news.CategoryId;
// Collection des groupes concernés
foreach (var c in model.allGroups)
{
if (Request.Form["" + c.GroupId].Contains("true"))
{
News.Groups.Add(c);
}
}
_newsRepository.AddToNewsSet(news);
_newsRepository.SaveChanges();
return Redirect("/NewsAdmin/Index/");
}
return View(model);
}
I say that all my groups are already created. I just want to insert the groups (chosen by the user via checkboxes). In my “CreateNewsViewModel”, I create a list of groups that contains all existing groups in my DB. I get the list in my view, via a “foreach” loop and create a checkbox for each group.
I reuse the same list in my controller to compare if checkboxes have been checked.
For each “true” value, I add groups in the groups collection of my news (just created).
With this, I get this Error Message :
“An entity object cannot be referenced by multiple instances of IEntityChangeTracker.” (at line _newsRepository.AddToNewsSet(news);)
I try some solutions but I still don’t understand how I can solve this problem.
Thanks for all
Edit
Actually, if I use explicitly two contexts and detach/attach my objects to other context it's fine and I have no erros.
ObjectContext context = _newsRepository.Context;
ObjectContext context2 = _groupsRepository.Context;
foreach (var c in groups)
{
if (Request.Form["" + c.GroupId].Contains("true"))
{
context2.Detach(c);
context.Attach(c);
news.Groups.Add(c);
}
}
I would like to use the Ladislav Mrnka's solution and use the dependency injection (I use Ninject framework) to give the same ObjectContext to my repositories (in single request processing).
I understand the concept but I don't know how to code it.
The error message says that News object or any of related Group objects is attached to different ObjectContext instance. How is your repository implemented and how did you get model.allGroups? If you loaded allGroups from GroupsRepository which has its own ObjectContext instance then it is probably source of the problem. The solution would be:
(Preferred) Share single ObjectContext for all repositories in single request processing
Detach objects when you load them from database (ObjectContext has Detach method)
Close ObjectContext when you load objects from database

How to relate entities that are not directly mapped through a navigation property

I have an object that has been populated with the contents of four different related entities. However i have another entity in which i cannot include as part of the query due to it not being related in the navigation properites directly to the IQueryable table i am pulling. The entity i am trying to include is related to one of the four different entities that have been included successfully.
Is there a way to include(during db hit or afterwards) this entity as part of the overall object i am creating?
Here is an example of what my calls look like to build the CARTITEM object:
public List<CARTITEM> ListCartItem(Guid cartId)
{
//Create the Entity object
List<CARTITEM> itemInfo = null;
using (Entities webStoreContext = new Entities())
{
//Invoke the query
itemInfo = WebStoreDelegates.selectCartItems.Invoke(webStoreContext).ByCartID(cartId).ToList();
}
//Return the result set
return itemInfo;
}
here is the selectCartItems filter(Where i would normally do the includes):
public static Func<Entities, IQueryable<CARTITEM>> selectCartItems =
CompiledQuery.Compile<Entities, IQueryable<CARTITEM>>(
(cart) => from c in cart.CARTITEM.Include("ITEM").Include("SHIPPINGOPTION").Include("RELATEDITEM").Include("PROMOTION")
select c);
from this i have my CARTITEM object. Problem is i want to include the PROMOTIONTYPE table in this object, but since the CARTIEM entity doesn't have a navigation property directly to the PROMOTIONTYPE table i get an error.
Let me know if you need any more clarification.
Thanks,
Billy
You can use join and if it is the same database and server it should generate the join in SQL and do it all in one call...
LinqToEnties join example

Entity Framework IQueryable

I'm having problems querying the entity model to get additional information.
My db has a Program table with a one to many relation with an Events table. The Entity model generates the relationships just fine, but I'm unable to figure out how to query the model to get the progam object with its events.
I can do this:
var foo = from program in entities.ProgramSet
where program.StartDate > DateTime.now
orderby program.StartDate
select program;
No problems there. From what I've found on Microsofts Page (Shaping queries with Entity framework): msdn.microsoft.com/en-us/library/bb896272.aspx, if I wanted to get the child objects, I just do the following:
// Define a LINQ query with a path that returns
// orders and items for a contact.
var contacts = (from contact in context.Contact
.Include("SalesOrderHeader.SalesOrderDetail")
select contact).FirstOrDefault();
However, there is no .Include or Include that I can find on the query.
Any suggestion? I know that I can do a foreach across the results, then run a .Events.Load() on it, but doesn't that force the IQueriable result to execute the sql, instead of optomize it to run only when a .ToList() etc is called on it?
Here is some sample code from my project:
public class ProgramRepository : CT.Models.IProgramRepository
{
CTEntities db = new CTEntities();
public IQueryable<Program> FindAllPrograms()
{
return db.ProgramSet;
}
public IQueryable<Program> FindUpcomingPrograms()
{
var programs = from program in FindAllPrograms()
where program.StartDate > DateTime.Now
orderby program.StartDate
select program;
return programs;
}
With the FindUpComingPrograms I would like to have it also include the Events Data. There is a relationship between the Program and Events model. Program has a List<Events> property, that I would like to fill and return with the IQueryable method.
Thanks again!
The Include Function is part of the ObjectQuery object...
I think you are going to need to re-write your query to look something like this:
var contacts = context.Contact.Include("SalesOrderHeader.SalesOrderDetail").FirstOrDefault();
//Not sure on your dot path you might have to debug that a bit
Here is an Article that has some examples...