Linq to Entities, Take(3) from Joined table - linq

I am trying to populate a ViewModel in an MVC app with data from a parent table joined with a child table. The only data I want from the child table is a comma diliminated string from the Nomenclature field of the top three records and put them into a string field in the ViewModel. Here is what I have tried without success:
public IEnumerable<ReqHeaderVM> GetOpenReqs(string siteCode)
{
var openReqs = from h in context.ReqHeaders
join l in context.ReqLineItems on h.ID equals l.ReqID into reqLineItems
select new ReqHeaderVM
{
ReqID = h.ID,
ShopCode = h.ShopCode
Nomenclatures = reqLineItems.Select(x => x.Nomenclature).Take(3) // This doesn't work
};
return (openReqs.ToList());
}
Here is the ViewMdel:
public class ReqHeaderVM
{
[Editable(false)]
public string ReqID { get; set; }
public string ShopCode { get; set; }
public string Nomenclatures {get; set;}
}

Assuming that you have proper relationship (foreign key) between ReqHeaders and ReqLineItems, this should give you what you are looking for...
public IEnumerable<ReqHeaderVM> GetOpenReqs(string siteCode)
{
var openReqs = from h in context.ReqHeaders
select new
{
ReqID = h.ID,
ShopCode = h.ShopCode
Nomenclatures = h.ReqLineItems
.OrderBy(x => x.SomeColumn)
.Select(x => x.Nomenclature)
.Take(3)
};
var openReqsTran = from oreq in openReqs.AsEnumerable()
select new ReqHeaderVM
{
oreq.ReqID,
oreq.ShopCode,
Nomenclatures = string.Join(", ", oreq.Nomenclatures)
};
return (openReqsTran);
}
Note that Nomenclatures is a list of type of Nomenclature.

Yes, the join creates a single Cartesian result set. (think tabular data) what you are attempting to do. To get the results you want you have a few choices.
use lazy loading and iterate over each header querying the line items individually.
pro - simple queries
con - select n+1
query all headers and all line items, but build view model with only the top 3
pro - single query
con - large Cartesian result set query too much data
query all headers and all associated lines individuals
pro - 2 smaller, simpler queries
con - query too many line details.
query all headers and top 3 lines per header in 2 queries
pro - get only the information you require
con - complex query for top 3 lines per header.

Related

get list from a class to fill taxtbox in other class by linq

how to get just special 2 attributes from a table to other table( and placement in related textbox) by LINQ??
public string srcht(ACTIVITIES sn)
{
db db = new db();
var q = (from i in db.costumers.Where(i => i.id== sn.id)select i.name).ToList();
return q.Single();
}
public string srcht(ACTIVITIES sn)
{
db db = new db();
var q = (from i in db.costumers.Where(i => i.id== sn.id)select i.family).ToList();
return q.Single();
}
i did linq twice to fill 2 textboxes by name and family in other table. can i do it by one LINQ?
So you have an Activity in sn, and you want the Name and the Family of all Customers that have an Id equal to sn.Id.
var result = db.Customers.Where(customer => customer.Id == sn.Id)
.Select(customer => new
{
Name = customer.Name,
Family = customer.Family,
});
In words: in the database, from the table of Customers, keep only those Customers that have a value for property Id that equals the value of sn.Id. From the remaining Customers select the values of Name and Family.
If you expect that there will only be at utmost one such customer (so if Id is the primary key), just add:
.FirstOrDefault();
If you think there will be several of these customers, add:
.ToList();
The result will be an object, or a list of objects of some anonymous type with two properties: Name and Family.
If you want to mix the Name and the Family in one sequence of strings, which I doubt, consider to put the Name and the Family in an array. Result is sequence of arrays of strings. To make it one array of Strings, use SelectMany:
var result = db.Customers
.Where(customer => customer.Id == sn.Id)
.Select(customer => new string[]
{
customer.Name,
customer.Family,
})
.SelectMany(customer => customer);

Performance issue in IEnumerable type when querying large amount of data with LINQ

I'm using LINQ to execute a query on a List type variable with a large amount of data (over a million). For performance purposes I'm using IEnumerable to store the results but when I try to access it there is a slight delay.
Specifically I want to see if the query produced any results, but when I use the .Count() or .Any() functions the performance drops.
I read that for IEnumerable types the execution of the query happens at the time of need, hence the delay. Is there a way to see if the IEnumerable has elements inside it without having that much delay?
This is what I'm trying to run.
IEnumerable<Entity> matchingEntities = entities.Where(e => e.Names.Any(n => myEntity.Names.Any(entityName => entityName.CompareNameObjects(n))));
and here are my classes
public class Entity
{
public string EntityIdentifier { get; set; }
public List<Name> Names { get; set; }
}
public class Name
{
public string FullName { get; set; }
public string NameType { get; set; }
public bool CompareNameObjects(Name name2)
{
return FullName == name2.FullName &&
NameType == name2.NameType;
}
}
entities is a list of all my objects and I want to check if myEntity has any Names identical with another entity in the set.
EDITED:
The data structure is similar to the 2 classes (Entity and Name). The entities are created by selecting all the entities, along with their names, from the database in XML format and then I convert the XML to a List as such:
List<Entity> entities = new List<Entity>();
using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["myCS"].ConnectionString))
{
conn.Open();
SqlCommand cmd = new SqlCommand("GetAllEntities", conn);
cmd.CommandType = CommandType.StoredProcedure;
string entitiesXml = "";
using (SqlDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
{
entitiesXml += rdr["XmlString"].ToString();
}
}
using (TextReader reader = new StringReader(entitiesXml))
entities = (Entity)xmlSerializer.Deserialize(reader);
conn.Close();
}
GetAllEntities (Stored Procedure):
declare #xmlString nvarchar(max) =(
select e.EntityIdentifier,
(
select n.[Full Name] as 'FullName',
n.[Name Type] as 'NameType'
from tblNames n
where e.EntityID=n.[Entity_ID]
for xml path('Name'), type
)
from tblEntities e
order by e.EntityID
for xml path('Entity')
)
select #xmlString as XmlString
Basically, you should avoid getting all data from your database then filter it with C# code. It consumes a lot of effort.
However, for quick solution, you can improve performance by preparing your conditions in a Dictionary form firstly.
// Let's say you have myEntity here
var myEntity = new Entity();
var entities = new List<Entity>();
// You should prepare the list of name that you wanna to find before you do it so that you don't have to make it repeatedly for every iteration
var names = myEntity.Names.Select(p=> p.FullName + p.NameType ).ToDictionary(p=>p, p=>p);
IEnumerable<Entity> matchingEntities = entities.Where(e => e.Names.Any(n => names.ContainsKey(n.FullName + n.NameType)));
This is just an example that may give you more idea. You can improve much more. I hope it can help you

Linq to Objects - Left Outer Join Distinct Object Property Values to an Aggregate Count

Lets say I have a generic list of the the following objects:
public class Supermarket
{
public string Brand { get; set; }
public string Suburb { get; set; }
public string State { get; set; }
public string Country { get; set; }
}
So using a List<Supermarket> which is populated with many of these objects with different values I am trying to:
Select the distinct Suburb properties from a
superset of Supermarket objects contained in a List<Supermarket> (say this superset contains 20
distinct Suburbs).
Join the Distinct List of Suburbs above to another set of aggregated and counted Suburbs obtained by a LINQ query to a different, smaller list of List<Supermarket>
The distinct items in my superset are:
"Blackheath"
"Ramsgate"
"Penrith"
"Vaucluse"
"Newtown"
And the results of my aggregate query are:
"Blackheath", 50
"Ramsgate", 30
"Penrith", 10
I want to join them to get
"Blackheath", 50
"Ramsgate", 30
"Penrith", 10
"Vaucluse", 0
"Newtown", 0
Here is what I have tried so far:
var results = from distinctSuburb in AllSupermarkets.Select(x => x.Suburb).Distinct()
select new
{
Suburb = distinctSuburb,
Count = (from item in SomeSupermarkets
group item by item.Suburb into aggr
select new
{
Suburb = aggr.Key,
Count = aggr.Count()
} into merge
where distinctSuburb == merge.Suburb
select merge.Count).DefaultIfEmpty(0)
} into final
select final;
This is the first time I have had to post on Stack Overflow as its such a great resource, but I can't seem to cobble together a solution for this.
Thanks for your time
EDIT: OK So I solved this a short while after the initial post. The only thing I was missing was chaining a call to .ElementAtOrDefault(0) after the call to .DefaultIfEmpty(0). I also verifed that using .First() instead of .DefaultIfEmpty(0) as Ani pointed out worked, The correct query is as follows:
var results = from distinctSuburb in AllSupermarkets.Select(x => x.Suburb).Distinct()
select new
{
Suburb = distinctSuburb,
Count = (from item in SomeSupermarkets
group item by item.Suburb into aggr
select new
{
Suburb = aggr.Key,
Count = aggr.Count()
} into merge
where distinctSuburb == merge.Suburb
select merge.Count).DefaultIfEmpty(0).ElementAtOrDefault(0)
} into final
select final;
LASTLY: I ran Ani's code snippet and confirmed that it ran successfully, so both approaches work and solve the original question.
I don't really understand the assumed equivalence between State and Suburb (where distinctSuburb == merge.State), but you can fix your query adding a .First() after the DefaultIfEmpty(0) call.
But here's how I would write your query: using a GroupJoin:
var results = from distinctSuburb in AllSupermarkets.Select(x => x.Suburb).Distinct()
join item in SomeSupermarkets
on distinctSuburb equals item.Suburb
into suburbGroup
select new
{
Suburb = distinctSuburb,
Count = suburbGroup.Count()
};

LINQ (Dynamic): OrderBy within a GroupBy using dynamic linq?

I had the following query using normal linq and it was working great (using anonymous type),
var result = from s in Items
group s by s.StartTime into groupedItems
select new {groupedItems.Key, Items= groupedItems.OrderBy(x => x.Name) };
But using Dynamic Linq I cannot get it to order by within the groupby.
result = Items.GroupBy("StartTime", "it").OrderBy("Name");
It states the Name isn't available. It is worth noting that if I take my OrderBy off, everything works great but items inside each "Key" are not ordered.
This is a good question!
I simulated your situation by creating a class called Item.
public class Item
{
public DateTime StartTime { get; set; }
public string Name { get; set; }
}
and then created a basic list of items to do the groupby.
List<Item> Items = new List<Item>()
{
new Item() { StartTime = DateTime.Today, Name = "item2"},
new Item() { StartTime = DateTime.Today, Name = "item1"},
new Item() { StartTime = DateTime.Today.AddDays(-1), Name = "item3"},
};
Now the big difference in the 2 queries is where the order by is being performed. In the first query, when you perform groupedItems.OrderBy(x => x.Name) its being performed on a IGrouping<DateTime,Item> or a single entry as it iterates through all the groupings.
In the second query, the orderby is being performed after the fact. This means you're doing an orderby on a IEnumerable<IGrouping<DateTime,Item>> because the iterations have already happened.
Since Microsoft was nice they added something to help deal with this for expressions. This overload allows you to specify the item returned as it iterates through the collection. Here's an example of the code:
var expressionResult = Items.GroupBy(x => x.StartTime,
(key, grpItems) => new { key, Items = grpItems.OrderBy(y => y.Name) });
The second part of the GroupBy you can specify a lambda expression that takes a key and a grouping of items under that key and return an entry that you specify, which is the same as you're doing in the original query.
Hope this helps!

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