Condition in Linq Expression - c#

I have a LINQ expression that joins two tables. I want to conditionally check another boolean value: (notice text between ********* below)
bool status = testfunc();
var List =
from t in Houses
join k in Tbl_HouseOwner on t.HouseCode equals k.HouseCode
where k.ReqCode== t.ReqCode
*********** if (status) { where k.Name.Contains(Name) } **********
select new
{
...
name = k.Name,
...
};

You can use status to mask the condition, like this:
where k.ReqCode == t.ReqCode && (!status || k.Name.Contains(Name))
If the status is false, the OR || will succeed immediately, and the AND && will be true (assuming that we've got to evaluating the OR ||, the left-hand side of the AND && must have been true). If the status is true, on the other hand, the k.Name.Contains(Name) would need to be evaluated in order to finish evaluating the condition.

An alternative option to dasblinkenlight's answer (which should work fine) is to build up the query programmatically. In this case you're effectively changing the right hand side of the join, so you could use:
IQueryable<Owner> owners = Tbl_HouseOwner;
if (status)
{
owners = owners.Where(k => k.Name.Contains(Name));
}
Then:
var query = from t in Houses
join k in owners on t.HouseCode equals k.HouseCode
where k.ReqCode == t.ReqCode
select new { ... };
Which approach is the most suitable depends on your scenario. If you want to add a variety of different query filters, building it up programmatically can be cleaner - and make the final SQL easier to understand for any given query. For a one-off, dasblinkenlight's approach is simpler.
Also note that in LINQ to Objects at least, it would be more efficient to join on both columns:
var query = from t in Houses
join k in owners
on new { t.HouseCode, t.ReqCode } equals new { k.HouseCode, k.ReqCode }
select new { ... };
In any flavour of LINQ which translates to SQL, I'd expect this to be optimized by the database or query translation anyway though.

I do it this way:
IQueryable<X> r = from x in Xs
where (x.Status == "Active")
select x;
if(withFlagA) {
r = r.Where(o => o.FlagA == true);
}
To fit this to your example, firstly you could do this:
IQueryable<YourOwnerType> filteredOwners = Tbl_HouseOwner;
if( status ) {
filteredOwners = filteredOwners.Where( o => o.Name.Contains(Name) );
}
Then substitute Tbl_HouseOwner with filteredOwners.
var List =
from t in Houses
join k in filteredOwners on t.HouseCode equals k.HouseCode
where k.ReqCode== t.ReqCode
//Nothing here
select new
{
...
name = k.Name,
...
};
Now, you may know this, but the point here is that the initial .Where does not 'reach out' to the database. Your query doesn't get executed either until you start enumerating it (e.g. foreach) or call a method like ToList(), First(), FirstOrDefault(). This means you can call .Wheres after your selects if you prefer and the final query will still be efficient.

Related

passing linq anonymous result to iqueryable object for another query

I have two tables (tbPerson and tbDataLog) where I need to return Id from one table (tbPerson) after checking certain conditions on both. After this, this result should be passed to another query. My first query returns the Id (primary key of a table) successfully and I need to pass these ids to another query so that it return me data based upon these Id. I also has an IQueryable type base object to check certain conditions to fetch data.
IQueryable<tbPerson> dataset
and I cannot changes this from Iqueryable to other as it will break other part of the code)
My first linq statement:
public static IQueryable<LogResults> GetResultsForYes()
{
Databasename ents = new Databasename();
var ids = (from f in ents.tbPerson
join g in ents.tbDataLog
on f.InfoID equals g.RefId
where g.Tag == "subscribed" && g.OldValue == "No" && g.Action == "Modified"
select new LogResults { _LogID = f.Id }).OrderBy(x => x._LogID);
return ids;
}
public class LogResults
{
public int _LogID { get; set; }
}
I access my result something like this where I can see in debugger all the Ids.
IQueryable<LogResults> log = GetResultsForYes();
Problem comes, when I tried to get records from tbPerson based upon these returned Id.
dataset=log.where(x=>x._LogID != 0);
I get this error:
Cannot implicitly convert type 'System.Linq.IQueryable' to 'System.Linq.IQueryable'. An explicit conversion exists(are you missing a cast)?
Any suggestions or some other good approach is welcome.
I love this thing about stackoverflow. when we write questions we force our brain to think more deeply and after 30 mins of posting this question, I solved it in a simple way. Sometimes we overcomplicated things!
var ids = (from f in ents.tbPerson
join g in ents.tbDataLog
on f.InfoID equals g.RefId
where g.Tag == "subscribed" && g.OldValue == "No" && g.Action == "Modified"
select new { f.Id }).ToArray();
var allId = ids.Select(x => x.Id).ToArray();
dataset = dataset.Where(x => allId.Contains(x.Id));
#ankit_sharma : I have not tested yours but will give a try and come back to you. Thanks for giving time and effort.
IQueryable<tbPerson> dataset=log.where(x=>x._LogID != 0);
The result of log.where(x=>x._LogID != 0) is an IQueryable<LogResults>, and you are trying to assign this result to dataset of type IQueryable<tbPerson>, two diferent types.
EDIT:
I see you make a join to get the tbPerson ids, and then you do a second query to get the persons. You could get the persons in the first join.
I just modify your code:
IQueryable<tbPerson> persons = from person in ents.tbPerson
join g in ents.tbDataLog
on person.InfoID equals g.RefId
where g.Tag == "subscribed" && g.OldValue == "No" && g.Action == "Modified"
select person;

How do I join more than one files, in a Linq (C#) where one of the joins is a left join

My Sql below works great, but now I would like to join another file that has a common key. The file I would like to join has could have one to many records, any suggestions
here is my code:
var regPoints = (from x in db.CertPoints
join y in db.CommunityPts on x.PointId equals y.PointId into z
from t in
(from r in z
where r.CommunityId == id select r).DefaultIfEmpty()
where x.TypeId == 1
select new Points
{
pointId = x.PointId,
pointsDescription = x.PointDescription,
points = x.Points,
dateApplied = t.DateApplied,
pointsEarned = t.PointsEarned,
pointsPending = t.Pending ? true : false,
pointsApproved = t.Approved ? true : false,
notes = t.Notes
}).AsEnumerable();
the new join would be a one to many records, where the key in CommunityPts is Id, and the file I would like to join is a list of file links "CommunityPtsDocs" with a foreign key CommnunityPtId. How do i add it to the above sql statement above?
Sometimes I feel I'm a navigation properties evangelist (fortunately, I'm not the only one).
The answer you accepted is OK, it does the job. But using any ORM like Entity Framework or LINQ-to-SQL you should avoid the join statement as much as possible. It's verbose and error-prone. It causes repetitive code and it's too easy to join the wrong properties erroneously.
You class CertPoint could have a 0..1-n navigation property CommunityPts (a list) and CommunityPt could have a 1-n navigation property CommunityPtsDocs (also a list). If you're using LINQ-to-SQL, chances are that they're already there but you're not aware of them. If you use Entity Framework code-first, you should add them yourself.
Having these navigation properties, your code is going to look like this:
from cert in CertPoints
from comm in cert.CommunityPts.DefaultIfEmpty()
from doc in comm.CommunityPtsDocs
where comm.CommunityId == id && cert.TypeId == 1
select new Points
{
pointId = cert.PointId,
pointsDescription = cert.PointDescription,
points = cert.Points,
dateApplied = comm.DateApplied,
pointsEarned = comm.PointsEarned,
pointsPending = comm.Pending ? true : false,
pointsApproved = comm.Approved ? true : false,
notes = comm.Notes,
something = doc.Something
})
Now the ORM will translate this into SQL with the correct joins and your code looks much cleaner (note that I also prefer more meaningful range variable names).
Following modification shall help in achieving the Task, though I prefer Fluent syntax, as that's much cleaner in my view to achieve the same, though I have not selected any column from CommunityPtsDocs in the Select statement
var regPoints = (from x in CertPoints
join y in CommunityPts on x.PointId equals y.PointId
join s in CommunityPtsDocs on y.Id equals s.CommnunityPtId into k
from t in (from r in k where r.CommunityId == id select r).DefaultIfEmpty()
where x.TypeId == 1
select new Points
{
pointId = x.PointId,
pointsDescription = x.PointDescription,
points = x.Points,
dateApplied = t.DateApplied,
pointsEarned = t.PointsEarned,
pointsPending = t.Pending ? true : false,
pointsApproved = t.Approved ? true : false,
notes = t.Notes
}).AsEnumerable();

Make a LINQ query dynamic to bring back all rows or only rows with a link to a lookup table?

I have a query that returns a list of currencies and joins to a lookup table. The result is then put into a class object (which works fine):
var queryforobject = from x in db.CurrencyExchangeRates.AsNoTracking()
join c in db.CurrencyTypes.AsNoTracking() on x.CurrencyTypeID equals c.ID
orderby x.ID
select new CurrencyExchangeRateObject
{
ID = x.ID,
CurrencyID = c.ID,
Currency = c.Description,
ExchangeRate = x.ExchangeRate,
LastEditedDate = x.LastEditedDate,
LastEditedBy = x.LastEditedBy,
Active = x.Active
};
I want to make this more dynamic, so if no CurrencyTypeID is supplied then it will return the full list (as it does already) - otherwise if a CurrencyTypeID is supplied it will only show where X.CurrencyTypeID = ID.
Something along the lines of an inline if?
There are a few options for filtering the query based on CurrencyTypeID if a search value (named currencyTypeID in this answer) is supplied, but return all data if no currencyTypeID is supplied.
First option: You could add a where clause to your existing query expression. The WHERE clause below will return every record in the data set if null is passed in for the currencyTypeID variable, otherwise it will filter the results.
from x in db.CurrencyExchangeRates.AsNoTracking()
join c in db.CurrencyTypes.AsNoTracking() on x.CurrencyTypeID equals c.ID
where (currencyTypeID == null || x.CurrencyTypeID == currencyTypeID)
orderby x.ID
select new CurrencyExchangeRateObject {
ID = x.ID,
CurrencyID = c.ID,
Currency = c.Description,
ExchangeRate = x.ExchangeRate,
LastEditedDate = x.LastEditedDate,
LastEditedBy = x.LastEditedBy,
Active = x.Active
};
Alternatively: Since queryforobject is of type IQueryable<T>, you can use LINQ's fluent API to append a WHERE clause to the query inside an if statement. You need to be more careful about timing on this one though as it needs to be done before you force evaluation of the IQueryable with a foreach loop, .ToList(), .Select() or other LINQ methods that force evaluation.
if(currencyTypeID != null)
queryforobject = queryforobject.Where(cerObj => cerObj.CurrencyID == currencyTypeID);

Linq Left Join with Where clause

I have this query that I am performing using entity framework 5 with MySql.
var employeeDetails = (from em in entities.employeemasters.AsEnumerable()
join sf in entities.salaryfitments.AsEnumerable()
on em.empID equals sf.empID into emsf
from x in emsf
where (x.edCode.ToString().Trim().Equals(txtEDCode.Text)
&& x.edCode != "SYS001")
select new { em, x });
The where (x.edCode.ToString().Trim().Equals(txtEDCode.Text) checks to see if there are any earnings/deductions stored for that employee and if so I can be able to get the amount figure.
I would like the query to return all employees and if they do not have a particular earnings/deductions matching txtEDCode.Text, then return a default value.
I cannot place .DefaultIfEmpty() after where (x.edCode.ToString().Trim().Equals(txtEDCode.Text)
What should I do to get the appropriate results?
Instead of returning the whole entities I'd create a new object with only the fields I was interested in and use a ternary if to provide the default value in the select statement, for example.
select new {
name = x.Name,
salary = x.Salary,
code = string.IsNullOrEmpty(x) ? "Blah" : x
}

LINQ query should be based on criteria

This might be bit tricky here for me, as I am new to LINQ.
I have following LINQ code returns me data properly.
But, the conditions given are selective.
Means, all the conditions may or may not be given at a time.Conditions are user input based. So, how to write such incremental LINQ based on criteria selected by user.
var dRows = (from TblPatientInformation in this.dsPrimaPlus1.tblPatientInformation
join TblDoctorMaster in this.dsPrimaPlus1.tblDoctorMaster on new { PtI_dcMId = Convert.ToInt32(TblPatientInformation.ptI_dcMId) } equals new { PtI_dcMId = TblDoctorMaster.dcM_Id }
join TblDepartmentMaster in this.dsPrimaPlus1.tblDepartmentMaster on new { DcM_deptMId = TblDoctorMaster.dcM_deptMId } equals new { DcM_deptMId = TblDepartmentMaster.ID }
join TblPatientDiagnosis in this.dsPrimaPlus1.tblPatientDiagnosis on new { PtI_Id = TblPatientInformation.ptI_Id } equals new { PtI_Id = Convert.ToInt32(TblPatientDiagnosis.ptD_ptIId) }
join TblDiagnosisInformation in this.dsPrimaPlus1.tblDiagnosisInformation on new { PtD_tgIId = Convert.ToInt32(TblPatientDiagnosis.ptD_tgIId) } equals new { PtD_tgIId = TblDiagnosisInformation.tgI_Id }
where
TblPatientInformation.ptI_Id > 0 ||
TblPatientInformation.ptI_PatientName.Contains(txtName.Text) ||
TblPatientInformation.ptI_Code == int.Parse( txtCaseNo.Text) ||
TblDepartmentMaster.ID ==int.Parse( cmbDepartment.SelectedValue.ToString()) ||
TblDoctorMaster.dcM_Id == int.Parse(cmbDoctor.SelectedValue.ToString()) ||
TblDiagnosisInformation.tgI_Id == int.Parse(cmbDiagnosis.SelectedValue.ToString())
select new
{
TblPatientInformation.ptI_Id,
TblPatientInformation.ptI_Code,
TblPatientInformation.ptI_PatientName,
TblPatientInformation.ptI_dcMId,
TblPatientInformation.ptI_Age,
TblPatientInformation.ptI_Address,
TblPatientInformation.ptI_eMail,
TblPatientInformation.ptI_Phone1,
TblPatientInformation.ptI_Phone2,
TblPatientInformation.ptI_Phone3,
TblPatientInformation.ptI_Date,
TblPatientInformation.ptI_Gender,
TblDiagnosisInformation.tgI_Name,
TblDiagnosisInformation.tgI_Description,
TblDoctorMaster.dcM_FullName,
TblDepartmentMaster.Department
});
I recommend trying out Predicate Builder http://www.albahari.com/nutshell/predicatebuilder.aspx for this purpose.
That post recommends the following:
The easiest way to experiment with PredicateBuilder is with LINQPad.
LINQPad lets you instantly test LINQ queries against a database or
local collection and has direct support for PredicateBuilder (press F4
and check 'Include PredicateBuilder').
which is an easy way to get going with this approach.
Hope that helps.
One solution would be to use Dynamic LINQ, where you can specify string expressions, instead of code expressions. e.g.
// Dynamic Linq string expression
var result = context.People.Where("Age >= 3 And StreetNumber < 3").ToList();
as opposed to:
// Linq code expression
var result = context.People.Where(q=>q.Age>=3 && q.StreetNumber < 3).ToList();
With this, you can parse your expression based on the user input, e.g.
StringBuilder sb = new StringBuilder();
...
if(criteria1)
{
sb.Append(" And Criteria>1");
}
...
if(criteria2)
{
sb.Append(" And Criteria2<15");
}
...
var result = context.People.Where(sb.ToString()).ToList();
Check out this Scott Gu article for a complete example:
http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

Categories