Dynamic linq query with join - c#

Here is my try of dynamic Linq query with join. I try to get list of unique categories, brands and other criteria which were present in last reading in database. What is passed to the query (brand, category etc.) would only be defined at runtime.
I read about best way of doing this with func's, predicates etc., I think this is beyond my capacity at this stage. I'm trying the easier way with query string, which I got working for some simpler case, but I'm doing something wrong here with join. If I do just plain select product.Category with intellisense of course this works, but not in string in select clause.
public IEnumerable<string> getFilterItems(string dbColumn)
{
var filterItems = new List<string>();
return (from reading in Helper.LatestReadings
where reading.Distributor != Helper.Client
join product in Helper.Context.Products
on reading.ProductId equals product.SkuCode
select ("product." + dbColumn)).Distinct();
}

you can use reflection to achieve this
public IEnumerable<string> getFilterItems(string dbColumn)
{
var filterItems = new List<string>();
var productList = from reading in Helper.LatestReadings
where reading.Distributor != Helper.Client
join product in Helper.Context.Products
on reading.ProductId equals product.SkuCode
select product;
return productList.Select(x=>x.GetType().GetProperty(dbColumn).GetValue(x)).Cast<string>().Distinct();
}

"product." + dbColumn evaluates to a String, therefore select ("product." + dbColumn) will return this string n times, one time per item in the result of your query.
This SO question has an answer if you want to have a dynamic select. It proposes a method so that you could write something like
var result = list.Select( CreateNewStatement( "Field1, Field2" ) );
EDIT:
Oh, sorry, I just read about "dynamic LINQ" here, therefore my above answer may not be that helpful. As I can see on the linked page, they use the following format in Select.
.Select("new(<ColumnName> as <DataMemberName>, ... )");
In your case you should try
.Select("new(product" + dbColumn + " as Value)");

Related

SQL Selection to Array in C#

I am querying in C# for the first time, so please forgive my ignorance. I want to query a table, then place the results in an array/dict/dataframe to then be accessed later. I am unable to run the final code on my end, so this is more of an exercise in setting up the queries for when the final code (a chatbot) works.
Here is the code that should work to get boiling points and melting points seperately. Assume that casnumber is declared in advance (let's just call it str '753')
boiling_point = (from cdls in ADVISORCHEMICALS
where cdls.casnumber == casnumber
select cdls.boiling_point).FirstOrDefault();
melting_point = (from cdls in ADVISORCHEMICALS
where cdls.casnumber == casnumber
select cdls.metling_point).FirstOrDefault();
How would I get the results of the query to an array/dict/dataframe instead?
dict = (from cdls in ADVISORCHEMICALS
where cdls.casnumber == casnumber
select cdls.boiling_point,
cdls.melting_point).FirstOrDefault();
Ideally, I would want {(boiling_point : 200F), (melting_point : 100F)} as output, or something similar in a table/df/array. There are 30+ attributes in the table, so a way to assign key-value pairs or create a dataframe from the query for each attribute queried would be ideal.
Get a list of Tuples like this
var tuples = (from cdls in ADVISORCHEMICALS
where cdls.casnumber == casnumber
select (cdls.boiling_point, cdls.melting_point))
.ToList();
tuples will be a list of tuples (ex. List<(string boiling_point, string melting_point)>)
for (var tuple in tuples)
{
var boiling_point = tuple.boiling_point;
var melting_point= tuple.melting_point;
}

LINQ subquery returns brackets and fieldnames, how to get just the value?

My LINQ query is the following, I query two tables, Settlements and Bills which have a one to many relationship, in particular there can be one settlement with one or more bills.
var TheSettlements = from settlement in context_.Settlements
select new
{
asettlementid = settlement.SettlementId,
SqBills = string.Join(",",
(
from b in context_.Bills
.Where(b => b.SettlementId == settlement.SettlementId)
select new { b.BillMunicipalityId }
))
};
Now the trivial part (for me), is that I would like the bills concatenated, so after many hours of trial and error, I got my results but the Bills(BillMunicipalityId) are presented inside brackets including the fieldname, like this.
The way I export the data, to a txt to be more precise, is this.
foreach (var settlement in TheSettlements)
{
SettlementsText
.Append(settlement.asettlementid).Append(Delimiter)
.Append(settlement.SqBills.ToString()).Append(Delimiter)
.Append(Newline);
}
And the results I get in the txt.
3,{ BillMunicipalityId = f9e47f81-fc97-4008-b93d-d384230c53aa },
6,,
7,{ BillMunicipalityId = 8b66610a-20c1-4f47-9f37-489d1a8ce31a },{ BillMunicipalityId = 003d59d4-7bcb-4603-b42c-dc389dd8fb06 },{ BillMunicipalityId = 0070bb29-e3a1-4317-b5e2-3d1ef08dd20b },
How should I handle this to get only the values?
Just the GUID of every BillMunicipalityId, without the { BillMunicipalityId = } part.
I think rather than selecting as a new object you could just select the value like this:
var TheSettlements = from settlement in context_.Settlements
select new
{
asettlementid = settlement.SettlementId,
SqBills = string.Join(",",
(
from b in context_.Bills
.Where(b => b.SettlementId == settlement.SettlementId)
select b.BillMunicipalityId
))
};
Your Linq statement looks really strange to me. As it shows in the question you're mixing Linq with extensions methods.
If context is a DbContext which is going to the database, concating the results with string.Join won't work as this statement can't be translated to SQL code. If context however contains in memory data this may work. I advise however to not use string.Join within Linq unless you add a clear comment to the code, this Linq should never hit the database.
When this code will hit the database, you'll get an NotSupportedException with the message:
LINQ to Entities does not recognize the method 'System.String Join[Int32]'
The second thing I notice in your query, normally the one-to-many relation is known by the datamodel and you shouldn't need to join the results yourself.
The easiest way to solve this, is to use an intermediate query, which gets the results from the database and after running the query and getting the data into memory, perform the conversion with string.Join()
This would look like:
var TheSettlements =
from settlement in context_.Settlements
select new
{
asettlementid = settlement.SettlementId,
SqBills = (
from b in settlement.Bills
select b.BillMunicipalityId
).ToList(),
};
// Get the results in memory:
var results = TheSettlements.ToArray();
// Format the results:
var printResults = results.Select(s =>
s.asettlementid.ToString() + ", " + string.Join(", ",s.SqBills));

How do I traverse three tables and get the maximum of a value

In my EntityFramework I have three associated tables like so:
I'm trying to get the last time a file was used on a job. I have a value for File.Name and I need to get the maximum value for Job.ExecutedOn. I can obtain the File.FileId with a where clause, then in a separate expression get a list of FileHistory.JobId, then finally in a third expression get the maximum of Job.ExecutedOn. It feels very clumsy.
I was hoping for a single expression that accepts a value for File.Name and traverses across the tables to return the maximum value for Job.ExecutedOn. Is this possible? How?
This should do the trick
DateTime GetLastExecutionTime(ObjectContext context, string fileName)
{
var query = from file in context.Files
join history in context.FileHistories
on file.FileID equals history.FileID
join job in context.Jobs
on history.JobID equals job.JobID
where file.FileName == fileName
select job.ExecutedOn;
var result = query.Max();
}
I believe this will work for you:
var query =
from file in dc.Files
where file.Name == fileName
from history in file.FileHistories
orderby history.Job.ExecutedOn descending
select history.Job.ExecutedOn;
var lastExecutedDate = query.First();
I wrote it in a way so you could easily return the associated job instead. To do that just change the final projection to select history.Job.
Otherwise you could just remove the sort and take the Max() of the query (instead of First()).

Linq to SQL - Query

I am trying to mimic below statement in Linq to SQL.
WHERE (rtrim(posid) like '%101' or rtrim(posid) like '%532')
I statement basically determine if posid ends with 101 or 532. In the above example I am only making 2 comparisons but their could be 1 to N comparisons all joined with OR. I store the comparison values (101,532,...) in a generic list that I send to my Linq to SQL method.
I have tried to mimic above SQL using a where clause unsuccessfully (example below):
var PosNum = new List<string>();
PosNum.Add("101");
PosNum.Add("532");
var q = (from a in context.tbl_sspos select a);
q = q.Where(p => PosNum.Contains(p.posid.Trim()));
The issue with the above where clause is that it tries to do an exact match rather I want an ends with comparison.
How would I mimic the SQL statement in Linq to SQL.
Thank You in advance for any help / advice you can provide.
I would use String.EndsWith();
This will check the end of the string rather than entire contents of it.
var q = (from a in context.tbl_sspos select a);
q = q.Where(p => p.posid.EndsWith("102") || p.posid.EndsWith("532"));
In EF 4 you can use the StartsWith / EndsWith methods by now. Might also work in LINQ to SQL.
UPDATE
Just realized that you are trying todo this against multiple values (PosNum), I don't think that this is directly supported currently. You can however concatenate multiple Where()clauses to get the result.
UPDATE 2
As AdamKing pointed out concatenating the where clauses was filtering against all PosNum values, here is the corrected version:
var baseQuery = (from a in context.tbl_sspos select a);
IEnumerable<YourType> q = null;
foreach(var pos in PosNum)
{
if(q == null)
q = baseQuery.Where(a => a.posid.EndsWith(pos));
else
q = q.Union(baseQuery.Where(a => a.posid.EndsWith(pos)));
}
This is not as pretty anymore, but works nonetheless.

How do I implement a dynamic 'where' clause in LINQ?

I want to have a dynamic where condition.
In the following example:
var opportunites = from opp in oppDC.Opportunities
join org in oppDC.Organizations
on opp.OrganizationID equals org.OrgnizationID
where opp.Title.StartsWith(title)
select new
{
opp.OpportunityID,
opp.Title,
opp.PostedBy,
opp.Address1,
opp.CreatedDate,
org.OrganizationName
};
Some times I have Title and sometimes I don't. And also I want to add date in where clause dynamically.
For example, like this SQL:
string whereClause;
string SQL = whereClause == string.Empty ?
"Select * from someTable" : "Select * from someTable" + whereclause
You can rewrite it like this:
var opportunites = from opp in oppDC.Opportunities
join org in oppDC.Organizations on opp.OrganizationID equals org.OrgnizationID
select new
{
opp.OpportunityID,
opp.Title,
opp.PostedBy,
opp.Address1,
opp.CreatedDate,
org.OrganizationName
};
if(condition)
{
opportunites = opportunites.Where(opp => opp.Title.StartsWith(title));
}
EDIT: To answer your question in the comments, yes, you can keep appending to the original Queryable. Remember, this is all lazily executed, so at this point all it's doing it building up the IQueryable so you can keep chaining them together as needed:
if(!String.IsNullOrEmpty(title))
{
opportunites = opportunites.Where(.....);
}
if(!String.IsNullOrEmpty(name))
{
opportunites = opportunites.Where(.....);
}
You can dynamically add a where clause to your IQueryable expression like this:
var finalQuery = opportunities.Where( x => x.Title == title );
and for the date similarly.
However, you will have to wait to create your anonymous type until after you've finished dynamically added your where clauses if your anonymous type doesn't contain the fields you want to query for in your where clause.
So you might have something that looks like this:
var opportunities = from opp in oppDC.Opportunities
join org in oppDC.Organizations on
opp.OrganizationID equals org.OrgnizationID
select opp
if(!String.IsNullOrEmpty(title))
{
opportunities = opportunities.Where(opp => opp.Title == title);
}
//do the same thing for the date
opportunities = from opp in opportunities
select new
{
opp.OpportunityID,
opp.Title,
opp.PostedBy,
opp.Address1,
opp.CreatedDate,
org.OrganizationName
};
The WHERE clause could be done something like
//...
where string.IsNullOrEmpty(title) ? true : opp.Title.StartsWith(title)
//...
Dynamically returning records I don't think is possible in LINQ since it needs to be able to create a consistent AnonymousType (in the background)
Because queries are composable, you can just build the query in steps.
var query = table.Selec(row => row.Foo);
if (someCondition)
{
query = query.Where(item => anotherCondition(item));
}
If you know in advance all possible where queries like in the SQL example you have given you can write the query like this
from item in Items
where param == null ? true : ni.Prop == param
select item;
if you don't know all possible where clauses in advance you can add where dymically for example like this:
query = query.Where(item => item.ID != param);
The following questions and answers address this quite well:
Dynamic where clause in LINQ - with column names available at runtime
Is there a pattern using Linq to dynamically create a filter?
I was searching for creating a dynamic where clause in LINQ and came across a very beautifull solution on the web which uses ExpressionBuilder in C#.
I am posting it here since none of the above solution uses this approach. It helped me. Hope it helps you too
http://www.codeproject.com/Tips/582450/Build-Where-Clause-Dynamically-in-Linq

Categories