binding to a linq query, silverlight 4 - linq

I would like to know if binding a control in Silverlight, to linq expression is same as binding to an observable collection ? I have a treeview which as of now I'm binding to an observable collection. Then I learned about linq expressions and it seemed by using the linq query i can totally get rid of this collection and generate it using the query but i'm not sure if this generated result from the linq query will get updated on the fly and pass the updates to the UI?
for example i have a list of employees, i created an observable collection of "employees who were managers" and used that as the itemsource for the tree. Whenever new employees were added and if they were managers they would get added to the manager collection and the tree would update. Now using linq i can generate a list of employees who are managers using a query but i don't have means to test if this will also get updated when new employees who are managers are added to the employees collection.
class EmployeeCollection : IEnumerable<Employee>, INotifyCollectionChanged
{
private List<Employee> empColl;
// private ObservableCollection<Employee> managerCollection; Not needed now
}
managerTreeView.ItemsSource = from Employee in EmployeeCollection where Employee.isManager select Employee;
Thanks

LINQ results aren't live collections, so updates to the source (in the linq query) won't trigger the query results to reevaluate. Hence, you won't have your desired behavior.
Hope this helps :)

Related

Database.SqlQuery with ViewModel and Include results in blank FK values

I am trying to display a grid of Orders from the Northwind sample database, which has FK references to the Customers and Employees tables. I want to use raw SQL (variable "query") and project to a ViewModel "Order_VM". The ViewModel includes the references to the Customers and Employees entities.
I have tried the following statement, which works fine, but only with the original model "Order" and the DbSet version of SqlQuery:
IQueryable<Order> orders = db.Orders.SqlQuery(query).AsQueryable().Include(o => o.Customer).Include(o => o.Employee);
However, I want it to work with the ViewModel "Order_VM" instead (code below) using the Database version of SqlQuery (I assume this is the only way), but the CompanyName (from Customers table) and LastName (from Employees table) display blank in the View (grid).
IQueryable<Order_VM> orders = db.Database.SqlQuery<Order_VM>(query).AsQueryable().Include(o => o.Customer).Include(o => o.Employee);
I have tried with/without the "virtual" keyword for the Customer and Employee entity references in the ViewModel to see if eager vs lazy loading was a factor. However still get blanks. Thanks for any help as I am still learning.
I have tried the following statement, which works fine
No, it doesn't, because the Include doesn't do anything. It compiles and runs, but the returned type, DbSqlQuery<Order> doesn't have an implementation of Include, so the method is ignored. You tried to fix that with the AsQueryable call, but that only "re-activates" the extension method Include, but it doesn't magically inject an Include implementation.
DbSqlQuery (and DbRawSqlQuery, the return type of db.Database.SqlQuery) don't implement IQueryable<T>, so original EF query provider is out of scope and won't return.
To execute the Include statement it should be possible to modify the executed SQL statement and put some JOINs in there. But EF can only do that when Include is called on an object that contains an expression tree, so it can add expressions to this tree. A raw SQL query will never be parsed into an expression tree, but simply be executed as is.
So, these raw SQL queries are only capable of populating flat objects from the column list in the SELECT statement. Nested objects (references of collections) will never be materialized. If your view model has a CompanyName property, you should have a select statement like SELECT ..., Company.CompanyName, ..., so the raw SQL should contain the JOIN.

LINQ to CRM - OR in Where clause

I am trying to bring some data from Dynamics CRM 2011 using LINQ. The goal is to bring all Contact records that have changes since certain date OR have a child entity (PERC files) changed since that same date. The query looks like that:
// Bring all students who have changes (at Contact entity) after specific date
// momentInTime or the status of any of their perc files has been changed since
// that date
var students = (from c in ContactSet
join pl in cga_portallogonSet on c.Id equals pl.cga_ContactId.Id
join ef in cga_percfileSet on c.Id equals ef.cga_StudentId.Id
where
(pl.cga_PortalLogonRole.Value == 284970000) // student
where
(c.ModifiedOn >= momentInTime || c.CreatedOn > momentInTime)
||
(ef.cga_statuschangedate >= momentInTime)
select c.cga_StudentNumber).Distinct().ToList();
This produces the following error message:
'Contact' entity doesn't contain attribute with Name = 'cga_statuschangedate'.
I cannot figure out how to do OR on two different entities. The MSDN says you need WHERE clause for each entity:
where Clause
In order to filter the result set, where clauses can be added against one or more of the >entities. Each where clause may only contain conditions against an individual entity type. >A composite condition involving multiple entities is not valid. Instead, each entity >should be filtered in separate where clauses.
http://msdn.microsoft.com/en-us/library/ff681565.aspx
Is there another way of achieving what I need?
Unfortunately you cannot achieve what you want in a single linq statement beacuse the liunq provider they use boils down to fetchXML, and fetchXML does not support the scenario you are using.
More detail... Fetch gives you condition's inside of entity's or link-entity's. These condition elements cannot have attributes in them from other linked entities, only the direct parent entity or link-entity. Here is one of many microsoft forum posts referencing this limitation of fetchXML
Probably not the answer you were looking for, eh? As an ugly alternative you you can run two separate queries and filter in memory (as damaging as that may be to performance). Or better yet if you are an on-premise deployment you can write some sql against the filtered views. Good luck.

Performace issue using Foreach in LINQ

I am using an IList<Employee> where i get the records more then 5000 by using linq which could be better? empdetailsList has 5000
Example :
foreach(Employee emp in empdetailsList)
{
Employee employee=new Employee();
employee=Details.GetFeeDetails(emp.Emplid);
}
The above example takes a lot of time in order to iterate each empdetails where i need to get corresponding fees list.
suggest me anybody what to do?
Linq to SQL/Linq to Entities use a deferred execution pattern. As soon as you call For Each or anything else that indirectly calls GetEnumerator, that's when your query gets translated into SQL and performed against the database.
The trick is to make sure your query is completely and correctly defined before that happens. Use Where(...), and the other Linq filters to reduce as much as possible the amount of data the query will retrieve. These filters are built into a single query before the database is called.
Linq to SQL/Linq to Entities also both use Lazy Loading. This is where if you have related entities (like Sales Order --> has many Sales Order Lines --> has 1 Product), the query will not return them unless it knows it needs to. If you did something like this:
Dim orders = entities.SalesOrders
For Each o in orders
For Each ol in o.SalesOrderLines
Console.WriteLine(ol.Product.Name)
Next
Next
You will get awful performance, because at the time of calling GetEnumerator (the start of the For Each), the query engine doesn't know you need the related entities, so "saves time" by ignoring them. If you observe the database activity, you'll then see hundreds/thousands of database roundtrips as each related entity is then retrieved 1 at a time.
To avoid this problem, if you know you'll need related entities, use the Include() method in Entity Framework. If you've got it right, when you profile the database activity you should only see a single query being made, and every item being retrieved by that query should be used for something by your application.
If the call to Details.GetFeeDetails(emp.Emplid); involves another round-trip of some sort, then that's the issue. I would suggest altering your query in this case to return fee details with the original IList<Employee> query.

Use LINQ for double-nested OData collection

I've got a custom OData feed that for books. Each book can have multiple authors and an author can be involved in multiple books so I implemented this using a join table (Book - BookAuthorJoin - Author). My proxy object has Book.BookAuthorJoins BookAuthorJoin.Books & BookAuthorJoin.Authors.
What I want todo is have a single query where I get all the books for an author in a single LINQ query, but having trouble applying the filter. Seems I want two Expand() methods, but that isn't working. The following query doesn't work, but shows what I'm trying to do:
var query = from book in ODataContext.Books.Expand("BookAuthorJoins").Expand("Authors")
where book.BookAuthorJoins.Author.AuthorID = authorID
select book;
On the server side, the 1-to-many or many-to-many relationship is usually exposed as just a navigation property, exposing the join table in the middle will make your life much harder. If you use EF you should be able to hide the table and just expose the relationship as a navigation property.
In any case, to get all books for a certain author the query should look like:
/Authors(123)/Books
The result of this query is just a feed of books.
If you do keep the join table exposed then something like this migth work:
/Authors(123)/BookAuthorJoins?$expand=Book
But this time you get all the BookAuthorJoins with the Book for each as well.

Using LINQ with stored procedure that returns multiple instances of the same entity per row

Our development policy dictates that all database accesses are made via stored procedures, and this is creating an issue when using LINQ.
The scenario discussed below has been somewhat simplified, in order to make the explanation easier.
Consider a database that has 2 tables.
Orders (OrderID (PK), InvoiceAddressID (FK), DeliveryAddressID (FK) )
Addresses (AddresID (PK), Street, ZipCode)
The resultset returned by the stored procedure has to rename the address related columns, so that the invoice and delivery addresses are distinct from each other.
OrderID InvAddrID DelAddrID InvStreet DelStreet InvZipCode DelZipCode
1 27 46 Main St Back St abc123 xyz789
This, however, means that LINQ has no idea what to do with these columns in the resultset, as they no longer match the property names in the Address entity.
The frustrating thing about this is that there seems to be no way to define which resultset columns map to which Entity properties, even though it is possible (to a certain extent) to map entity properties to stored procedure parameters for the insert/update operations.
Has anybody else had the same issue?
I'd imagine that this would be a relatively common scenarios, from a schema point of view, but the stored procedure seems to be the key factor here.
Have you considered creating a view like the below for the stored procedure to select from? It would add complexity, but allow LINQ to see the Entity the way you wanted.
Create view OrderAddress as
Select o.OrderID
,i.AddressID as InvID
,d.AddressID as DelID
...
from Orders o
left join Addresses i
on o.InvAddressID= i.AddressID
left join Addresses d
on o.DelAddressID = i.AddressID
LINQ is a bit fussy about querying data; it wants the schema to match. I suspect you're going to have to bring that back into an automatically generated type, and do the mapping to you entity type afterwards in LINQ to objects (i.e. after AsEnumerable() or similar) - as it doesn't like you creating instances of the mapped entities manually inside a query.
Actually, I would recommend challenging the requirement in one respect: rather than SPs, consider using UDFs to query data; they work similarly in terms of being owned by the database, but they are composable at the server (paging, sorting, joinable, etc).
(this bit a bit random - take with a pinch of salt)
UDFs can be associated with entity types if the schema matches, so another option (I haven't tried it) would be to have a GetAddress(id) udf, and a "main" udf, and join them:
var qry = from row in ctx.MainUdf(id)
select new {
Order = ctx.GetOrder(row.OrderId),
InvoiceAddress = ctx.GetAddress(row.InvoiceAddressId),
DeliveryAddress = ctx.GetAddress(row.DeliveryAddressId)) };
(where the udf just returns the ids - actually, you might have the join to the other udfs, making it even worse).
or something - might be too messy for serious consideration, though.
If you know exactly what columns your result set will include, you should be able to create a new entity type that has properties for each column in the result set. Rather than trying to pack the data into an Order, for example, you can pack it into an OrderWithAddresses, which has exactly the structure your stored procedure would expect. If you're using LINQ to Entities, you should even be able to indicate in your .edmx file that an OrderWithAddresses is an Order with two additional properties. In LINQ to SQL you will have to specify all of the columns as if it were an entirely unrelated data type.
If your columns get generated dynamically by the stored procedure, you will need to try a different approach: Create a new stored procedure that only pulls data from the Orders table, and one that only pulls data from the addresses table. Set up your LINQ mapping to use these stored procedures instead. (Of course, the only reason you're using stored procs is to comply with your company policy). Then, use LINQ to join these data. It should be only slightly less efficient, but it will more appropriately reflect the actual structure of your data, which I think is better programming practice.
I think I understand what you're after, but I could wildy off...
If you mock up classes in a DBML (right-click -> new -> class) that are the same structure as your source tables, you could simply create new objects based on what is read from the stored procedure. Using LINQ to objects, you could still query your selection. It's more code, but it's not that hard to do. For example, mock up your DBML like this:
Pay attention to the associations http://geeksharp.com/screens/orders-dbml.png
Make sure you pay attention to the associations I added. You can expand "Parent Property" and change the name of those associations to "InvoiceAddress" and "DeliveryAddress." I also changed the child property names to "InvoiceOrders" and "DeliveryOrders" respectively. Notice the stored procedure up top called "usp_GetOrders." Now, with a bit of code, you can map the columns manually. I know it's not ideal, especially if the stored proc doesn't expose every member of each table, but it can get you close:
public List<Order> GetOrders()
{
// our DBML classes
List<Order> dbOrders = new List<Order>();
using (OrderSystemDataContext db = new OrderSystemDataContext())
{
// call stored proc
var spOrders = db.usp_GetOrders();
foreach (var spOrder in spOrders)
{
Order ord = new Order();
Address invAddr = new Address();
Address delAddr = new Address();
// set all the properties
ord.OrderID = spOrder.OrderID;
// add the invoice address
invAddr.AddressID = spOrder.InvAddrID;
invAddr.Street = spOrder.InvStreet;
invAddr.ZipCode = spOrder.InvZipCode;
ord.InvoiceAddress = invAddr;
// add the delivery address
delAddr.AddressID = spOrder.DelAddrID;
delAddr.Street = spOrder.DelStreet;
delAddr.ZipCode = spOrder.DelZipCode;
ord.DeliveryAddress = delAddr;
// add to the collection
dbOrders.Add(ord);
}
}
// at this point I have a List of orders I can query...
return dbOrders;
}
Again, I realize this seems cumbersome, but I think the end result is worth a few extra lines of code.
this it isn't very efficient at all, but if all else fails, you could try making two procedure calls from the application one to get the invoice address and then another one to get the delivery address.

Resources