Select&Distinct into new object with DLinq - c#

Suppose that I have list of following objects
public class OrderInfo
{
public string OrderNo {get; set;}
public DateTime OrderDate {get; set;}
public decimal OrderAmount {get; set;}
public decimal OrderPrice {get; set;}
}
as
List<OrderInfo> data = new List<OrderInfo>();
and this list has necessary number of items in it.
I need to select distinct OrderNo, OrderDate pairs from this List. In Linq I can write a query as:
var q = (from x in data
group x by new { x.OrderNo, x.OrderDate }
into grp
select new
{
grp.Key.OrderNo,
grp.Key.OrderDate
}).Distinct();
The thing is that I am writing a generic method so the object type and properties are unknown. So I have to write this with DLinq.
If I have a list of List<T> and a string[] { "OrderNo", "OrderDate" } how can I apply this example with DLinq at the run-time?

It was my fault. I was Googling for "DLinq" but not "Dynamic LinQ". Actually the answer was quite simple :
var q = data.Select("new (OrderNo, OrderDate)").Distinct();

Related

Linq union with group by perrformnces issue

I have 3 Tables defined as below:
public Class Product
{
public long Key {get; set;}
public CountryKey {get; set;}
}
public Class ProductCountry
{
pullic long Key {get; set;}
public long ProductKey {get; set;}
public LocalProductKey {get; set;}
public long CountryKey {get; set;}
}
public Class Country
{
public long Key {get; set;}
public string Name {get; set;}
}
I'm using EF database first, for each class we have a view to get data from database ( VW_Product to get the products)
I have a method filtering a collection of product depends on a criteria.
IQueryable<VW_Product> query1 = FilterQuery(Object criteria); gets all products matching the criteria;
Now I want concat the filtred collection by adding the folowing collection:
var countriesKey = new List<long>() {45, 36, 6974, 366,....};
var keys = Context.VW_ProductCountries
.GroupBy(pc => pc.ProductKey)
.Where(grp => grp.Any(pc => countriesKey.Contains(pc.CountryKey) && !grp.Any(x => x.LocalProductKey != null)))
.SelectMany(grp => grp.Select(pc => pc.Productkey))
.Distinct();
var query2 = Context.VW_Product.Where(p => keys.Contains(p.ProductKey));
var result = query1.Concat(query2);
Is the another way to improve this query because it takes a lot of time to execute.
Try adding a .ToList() after .Distinct() to transform the list of keys from an ObjectQuery to an in-memory object. In query2, you're running multiple .Contains() against the 'keys' ObjectQuery, possibly executing the query every time.
Also, i presume
var result = query1.Concat(query1);
should be
var result = query1.Concat(query2);

Linq to Entities Join to Show Name in Lookup Table

I'm using EF Code First (hybrid, database generation disabled) and I have two models/tables. I'm try to select and return all values in T1 and one field in a reference/lookup table so I can perform filtering on the list without requerying the database. I need to have the value of ItemName available so I can do comparisons.
If I were using SQL I'd just do something like this:
SELECT s.*, im.ItemName
FROM Specs s
INNER JOIN ItemMake im ON s.ItemMakeID = im.ID
My classes look something like this:
public class Spec {
public int ID {get; set;}
public int ItemMakeID {get; set;}
[ForeignKey("ItemMakeID")]
public ItemMake itemMake {get; set;}
}
public class ItemMake {
public int ID {get; set;}
public string ItemName {get; set;}
}
Currently my Linq to EF query looks like this. It doesn't work. I can't get at the ItemName property like I need to.
var specs = (from s in db.Specs
join im in db.ItemMakes on s.ItemMakeID equals im.ID
orderby s.modelNo select s).ToList();
What am I doing wrong?
That's because you're selecting just s in select clause. Use anonymous type declaration to get ItemName too:
var specs = (from s in db.Specs
join im in db.ItemMakes on s.ItemMakeID equals im.ID
orderby s.modelNo select new { s, im.ItemName }).ToList();

Filter Sub-Collection of Collection

I have the following classes:
class Customer
{
public int IdCustomer {get; set}
public string Name {get; set;}
public virtual ICollection<Expertise> Expertises {get; set;}
}
class Expertise
{
public int IdExpertise {get; set;}
public string Description {get; set;}
public virtual ICollection<SubExpertise> SubExpertises {get; set;}
}
class SubExpertise
{
public int IdSubExpertise { get; set;}
public int IdExpertise {get; set;}
public string Description {get; set;}
public virtual Expertise Expertise {get; set;}
}
How I can filter a SubExpertise using a IQueryable, and keep the filter condition? Basically I want to filter by SubExpertise, but keeping the possibilty to add more filters in the query
Example: I wanna all customers that have a SubExpertise 'x' or 'y'. Keep in mind that SubExpertise is a sub-collection of a collection (Expertise). And that I can have new filters after this.
I think you're after a method to build the expressions dynamically. You could use a predicate builder to do the job. My favorite is this one.
Using this little gem you can do this:
var pred = PredicateBuilder.Create<SubExpertise>(s => s.Description == "x");
pred = pred.Or(s => s.Description == "y");
var customers = db.Customers
.Where(c => c.Expertises
.Any(e => e.SubExpertises.AsQueryable()
.Any(pred)));
e.SubExpertises must be cast to IQueryable because it's compile-time type is ICollection, and the extension method Any that fits ICollection doesn't accept an Expression but a Func, so it doesn't compile.
I use a pattern like this for resuable filters, defining the filters as Expressions
public static class SubExpertiseFilter
{
public static Expression<SubExpertise, bool> XorY =
se => se.Description == "X" || se.Description == "Y";
}
Then if I want to apply it:
public IQueryable<Expertise> ApplyXorYFilter(IQueryable<Expertise> expertises, bool appyXorY)
{
if(applyXorY)
{
expertises = expertises.Any(e => e.Subexpertises.AsQueryable().Any(SubExpertiseFilter.XorY);
}
return expertises;
}
Or just do it inline where you need it.
The "AsQueryable" is needed to use the expression inside another expression so that EF is happy with it, otherwise the compiler expects a function there and that isn't something EF can turn into SQL.
Suppose you have a list customers
var filteredCustomers = from customer in customers
where customer.Expertises.Any(e => e.Subexpertises.Any(s => IsXOrY(s)))
select customer;
You can add any filter conditions in IsXOrY: (and you should rename it)
bool IsXOrY(SubExpertise subExpertise)
{
check if the SubExpertise is 'x' or 'y'
check if the SubExpertise meets other criteria
return true if this SubExpertise meets all your criteria
}

Linq create an array of Dto objects, with an array in it

I have a rich NHibernate dbo object that I need to query to extract a keyword and related array of country codes.
To explain:
I have 3 related DBO objects returned from a database, that have the following structure:
class DboCountry{
public int CountryId {get;set;}
public string CountryCode {get;set;}
}
class DboPage{
public int Id {get;set;}
public string Keyword {get;set;}
}
class DboPageCountry{
public int Id {get;set;}
public DboThemePage DboThemePage {get;set;}
public DboCountry DboCountry {get;set;}
}
As such when querying the database I get a List of DboPageCountries that contain multiple repeating DboPage.Keyword that are associated to various DboCountry.CountryCode
What I need to do is take the List of DboPageCountries and create an array of DtoKeywordCountryCode with the following structure:
class DtoKeywordCountryCode{
public string Keyword {get; set;}
public string[] CountryCodes {get; set;}
}
So far, using Linq, I've either been able to group the keywords, but not get the associated country codes, or get keywords and country codes, but not associated as a unique Keyword against an array of applicable CountryCodes.
Any pointers in the right direction much appreciated.
Try this -
var items = new List<DboPageCountry>(); //list of DboPageCountries
var items2 = from x in items.GroupBy(x => x.DboThemePage)
select new DtoKeywordCountryCode { Keyword = x.First().DboThemePage.Keyword, CountryCodes = x.Select(c => c.DboCountry.CountryCode).ToArray() };
DtoThemePage[] dtoThemePageArrayFull = (from tpc in dboThemePageCountryList group tpc by tpc.DboThemePage.Keyword into k
select new DtoThemePage
{
Keyword = k.Key,
CountryCodes = k.Select(c => c.DboCountry.CountryCode).ToArray<string>()
}).ToArray<DtoThemePage>();

Any of the properties equals any of a list of objects

I have a problem in Entity-Framework, using Code-First, that I couldn't solve.
Having entities of the type
public class Product {
public int ID {get; set; }
public virtual ICollection<Category> Categories { get; set; }
}
public class Category {
public int ID {get; set;}
public virtual ICollection<Product> Products { get; set; }
// rest omitted
}
in my database, i try to get all Products that have at least one Category from a list of given Categories. I need an Expression as this expression is combined with other expressions later.
Ie. i tried:
var searchFor = new List<Category>{...};
var expression = product => product.Categories.Any(cat => searchFor.Contains(cat))
Executing this later against a DbContext
context.Products.Where(expression).ToList();
creates an exception stating mainly that This context supports primitive types only.
Changing it to
var expression = product => product.Categories.Any(
cat => searchFor.Any(d => d.ID == cat.ID));
to get rid of the object comparison didn't help. I'm stuck. How can I manage that?
You should get rid of List<Category>, replacing it with a list of IDs, like this:
// I'm assuming that ID is of type long; please fix as necessary
var searchFor = new List<long>{...};
var expression = product =>
product.Categories.Any(cat => searchFor.Contains(cat.ID))
If you've already got a list of categories, you can build a list of IDs outside the query:
var searchForIds = searchFor.Select(x => x.ID).ToList();
var query = context.Products
.Where(product => product.Categories
.Any(cat => searchForIds.Contains(cat.ID)));
I don't know that that will work, but it might. (Apologies for the indentation... it's just to avoid scrolling.)

Categories