LINQ/Projection - How to sort TableA by non FK column id/name field per TableB? - linq

How can I use LINQ/Projection to sort a list of A objects that contain an id field that references table/object B (B contains id and name).
I want to sort list of A objects that contain B by B.name?
Model (pseudo)
public class A
{
public int AId {get; set;}
public Nullable<int> BId {get; set;}
}
public class B
{
public int BId {get;set;}
public string Name {get;set;}
}
Code in some controller passing in a list of A's that contain B's but sort them by B.Name?
var list = db.As.OrderBy(x => x.BId->References.Name); // Way wrong but using something similar
return(list.ToList()
Basically, looking for the equivalent of this (using projection join or OrderBy from above):
var q1 =
from a in db.As
join b in db.Bs on a.BId equals b.BId
orderby b.Name // <- Need this to sort by B's name
select c;

Am I right that you are looking for the equivalent to the LINQ query you have already written above, only that it is based on extension methods instead?
In this case the following should work:
var list = db.As.Where(a => a.BId.HasValue)
.Join(db.Bs, a => a.BId.Value, b => b.BId, (a, b) => new { a, b.Name })
.OrderBy(r => r.Name)
.Select(r => r.a);
I've also added a check to make sure A.BId is not null before getting its value.
Just curious: why can't you use your LINQ query (with the only difference of selecting a instead of c)?

Related

count based on lookup in LINQ

I have a table (or entity) named Cases. There is another table CaseStatus_Lookup and the primary key of this table is a foreign key in the Cases table.
What I want to do is: For every status type I want the number of count of cases. For e.g. if status = in progress , I want to know how many cases are in that status.
one other thing: I also want to filter the Cases based on UserID.
I tried several ways in LINQ but could not get vary far. I was wondering if someone could help.
try Linq .GroupBy
am assuming your entity structure
suppose your Case Entity is like
public class Case
{
public int Id{get;set;}
public int CaseStatusId{get;set;}
public int UserId{get;set;}
//navigational fields
public virtual CaseStatus CaseStatus {get;set;}
}
and suppose your CaseStatus entity is like:
public class CaseStatus
{
public int Id{get;set;}
public string Name{get;set;}
//navigational fields..
public virtual ICollection<Case> Cases{get;set;}
}
then you can do this:
using (myDbContext db = new myDbContext())
{
var query = db.Cases.GroupBy(case => case.CaseStatus.Name)
.Select(group =>
new {
Name = group.Key,
Cases= group.OrderBy(x => x.Id),
Count= group.Count()
}
).ToList();
//query will give you count of cases grouped by CaseStatus.
}
similarly you can further filter your result based on userId.
Start to explore about Linq .GroupBy
You need a function that returns the sum and takes the status as parameter :- something like below.
MyCaseStatusEnum caseStatus; //Pass your required status
int caseCount = myCases
.Where(r => r.Status == caseStatus)
.GroupBy(p => p.Status)
.Select(q => q.Count()).FirstOrDefault<int>();

MVC3/LINQ/EF4.1 selecting distinct col values from a result set?

How can I select a list of column values from a result set (as distinct) and put into a list?
class T {int id; string name;}
-- Controller...
var query = #"exec someStoredProc";
IEnumerable<T> bb =
db2.Database.SqlQuery<T>(query);
// Something like???:
List<string> Names = bb.SelectDistinct("name"); // returns distinct list of names from result set
Since you just need the distinct list of names, you can project to the name property and the just use Distinct() :
List<string> Names = bb.Select( x=> x.name)
.Distinct()
.ToList();
This requires that you make the name property public, also I would rethink your class name T, how about CustomerName (or whatever else is expressive enough so you know what it means) ?
public class CustomerName
{
public int id{get;set;}
public string name {get;set;}
}

Projections over sub collections - EF4

Lets assume the following model with EF4:
class Order
{
....
public int Id {get;private set;}
//ICollection is the root of all evil here
public ICollection<OrderDetail> Details {get;private set;}
}
I can then project over this structure with Linq:
var IdAndCount = context
.Orders
.Select ( o => new {
Id = o.Id,
Count = o.Details.Where(d => d.Foo > 0).Count()});
So far so good, this will be fully translated to sql.
Now to the problem, what if I want to extract the where clause predicate in this query:
Func<OrderDetail,bool> detailPredicate = d => d.Foo > 0;
var IdAndCount = context
.Orders
.Select ( o => new {
Id = o.Id,
Count = o.Details
.Where(detailPredicate)
.Count()});
This compiles, but fails at runtime because there is no way for EF4 to translate the predicate to SQL since it is a Func and not an Expression.
Changing the predicate to an Expression<Func<OrderDetail,bool>> will not work since ".Where" on the OrderDetails links to the IEnumerable "Where" since the details are ICollection.
So, is it possible to extract parts of a bigger Linq query if the properties beeing traversed are IEnumerable or similair?
Try
Expression<Func<OrderDetail,bool>> detailPredicate = d => d.Foo > 0;

Linq to NHibernate - select count problem

Given the classes A and B where
class A
{
string Name;
Ilist<B> BList;
}
class B
{
string Name;
}
With FluentNH mapping, relationship is many-to-many which is HasManyToMany(x => x.B) for A. B has no reference to A. NH version is 2.1.2.4000.
What should be the linq query to select the collection where each row contains B.Name and count of A's containing that B? Result must be the List of anonymous type who has 2 fields: Name and Count. Result also should include all B's, hence it should be outer join.
My intend is to get the result with minimum round-trips to database, possibly in one go.
If you want to do it in Linq in one hit in code, you could do this...
var result = Session.Linq<A>()
.SelectMany(a => a.BList, (a, b) => new { b.Name, A = a.Id })
.ToList()
.GroupBy(x => x.Name)
.Select(x => new { Name = x.Key, Count = x.Count() })
.ToList();
NHibernate.Linq (2.1.2.4000) can't handle a GroupBy after a SelectMany it seems, so the first ToList pulls all the data into memory. This is inefficient -- a SQL count would be better.
Alternatively, you could add a lazy loaded collection to your B class that goes back to A. If you're using a many-to-many table in the middle, that should be easy.
public class B
{
public virtual string Name { get; set; }
public virtual IList<A> AList { get; private set; }
}
Your query simply becomes...
var result = Session.Linq<B>()
.Where(b => b.AList.Count > 0)
.Select(b => new { b.Name, b.AList.Count }
.ToList();
Which produces very efficient SQL from Linq (using a count) and gives the same result.

Subsonic 3 Linq Projection Issue

OK I'm banging my head against a wall with this one ;-)
Given tables in my database called Address, Customer and CustomerType, I want to display combined summary information about the customer so I create a query to join these two tables and retrieve a specified result.
var customers = (from c in tblCustomer.All()
join address in tblAddress.All() on c.Address equals address.AddressId
join type in tblCustomerType.All() on c.CustomerType equals type.CustomerTypeId
select new CustomerSummaryView
{
CustomerName = c.CustomerName,
CustomerType = type.Description,
Postcode = address.Postcode
});
return View(customers);
CustomerSummaryView is a simple POCO
public class CustomerSummaryView
{
public string Postcode { get; set; }
public string CustomerType { get; set; }
public string CustomerName { get; set; }
}
Now for some reason, this doesn't work, I get an IEnumerable list of CustomerSummaryView results, each record has a customer name and a postcode but the customer type field is always null.
I've recreated this problem several times with different database tables, and projected classes.
Anyone any ideas?
I can't repro this issue - here's a test I just tried:
[Fact]
public void Joined_Projection_Should_Return_All_Values() {
var qry = (from c in _db.Customers
join order in _db.Orders on c.CustomerID equals order.CustomerID
join details in _db.OrderDetails on order.OrderID equals details.OrderID
join products in _db.Products on details.ProductID equals products.ProductID
select new CustomerSummaryView
{
CustomerID = c.CustomerID,
OrderID = order.OrderID,
ProductName = products.ProductName
});
Assert.True(qry.Count() > 0);
foreach (var view in qry) {
Assert.False(String.IsNullOrEmpty(view.ProductName));
Assert.True(view.OrderID > 0);
Assert.False(String.IsNullOrEmpty(view.CustomerID));
}
}
This passed perfectly. I'm wondering if you're using a reserved word in there?
This post seems to be referring to a similar issue...
http://groups.google.com/group/subsonicproject/browse_thread/thread/2b569539b7f67a34?hl=en&pli=1
Yes, the reason Rob's example works is because his projection's property names match exactly, whereas John's original example has a difference between CustomerType and type.Description.
This shouldn't have been a problem, but it was - the Projection Mapper was looking for properties of the same name and wasn't mapping a value if it didn't find a match. Therefore, your projection objects' properties would be default values for its type if there wasn't an exact name match.
The good news is, I got the latest source today and built a new Subsonic.Core.dll and the behavior is now fixed.
So John's code above should work as expected.
I just downloaded the latest build from 3/21/2010, which is about 2 months after the last poster on this thread, and the problem still exists in the packaged binary. Bummer.
Here what I have to do:
var data =
(from m in Metric.All()
where m.ParentMetricId == parentId
select new
{
m.MetricName,
m.MetricId,
})
.ToList();
var treeData =
from d in data
select new TreeViewItem
{
Text = d.MetricName,
Value = d.MetricId.ToString(),
LoadOnDemand = true,
Enabled = true,
};
return new JsonResult { Data = treeData };
If I try to do the projection directly from the Subsonic query, the Text property ends up with the ID, and the Value property ends up with the Name. Very strange.

Resources