using one line if in linq statement - c#

I have multiple drop-down lists with default value of 0 which indicates that user not selected any option and other numbers with mean user selected an value and it should include in LINQ query. My problem is I can not check if the user selected any options in drop-down lists or not?
This is my try for first drop-down list:
var ddl1 = Convert.ToInt32(ddlDastgAhasli.SelectedValue);
var project = (from p in context.Projects
where Convert.ToInt32(p.DastgahEjraeiAsli) == ((ddl1 == 0) ? null : ddl1) select p);
This is the error:
type of conditional expression cannot be determined because there is no implicit conversation '' and 'int'

You have two problems here:
You cant compare a value type(in your case int) to null.
You need to select something.
This is the structure for the query:
var project = (from p in context.Projects where p.prop == 1 select p);
And this to parse the int:
Bad
string strInt = "7";
if(Convert.ToInt32(strInt) == null) // compilation error
Good
string strInt = "7";
int intVal;
if(int.TryParse(strInt, out intVal))
{
// intVal is an integer
}
else
{
// intVal isnt an integer
}

If you just want to skip the where condition when ddl1 isn't set, you can just conditionally add the where clause when actually needed, something like (with the somewhat silly example of all ddls comparing to the same field)
var project = (from p in context.Projects select p);
if(ddl1 != 0)
{
project = project.Where(Convert.ToInt32(p.DastgahEjraeiAsli) == ddl1);
}
if(ddl2 != 0)
{
project = project.Where(Convert.ToInt32(p.DastgahEjraeiAsli) == ddl2);
}
...
This will effectively require all where clauses where ddlx != 0 to be true, and the ones where the ddl is 0 are not in any way affecting the query.

Here is a very useful extension method which i use in my projects to solve various checks..Its not about only ASP.NET..You can use it in winforms etc. also
public IEnumerable<Control> GetAll(Control control)
{
var controls = control.Controls.Cast<Control>();
return controls.SelectMany(ctrl => GetAll(ctrl))
.Concat(controls)
.Where(c => c.Visible == true);
//If you don't need to check that the control's visibility then simply ignore the where clause..
}
with this method no matter your control (in your situation dropdownlists) is a member/child of another control under that control which the control you pass as parameter to the method..Method checks recursively started from given parameter and checks all child controls related to given control as Method parameter..
And Usage in code :
//Its Assumed you need to check DropDownList' SelectedIndex property
List<Control> dropDownsOfPageWhichTheirSelectedIndexIsZero
= new List<Control>( GetAll ( YourAspNetPage )
.Where( x => x is DropDownList
&& ( ( DropDownList ) x ) . SelectedIndex == 0 ) ) ;
// Lets check if our page has dropdowns which their SelectedIndex equals to Zero
if ( dropDownsOfPageWhichTheirSelectedIndexIsZero . Count > 0 )
{
// do your work what you need in your project
//suppose you need their names and do something with this name (i.e.warn a message to user)
foreach ( DropDownList ddl in dropDownsOfPageWhichTheirSelectedIndexIsZero )
{
alert ("Please Check "+ ddl.Name +".Should Chost other than \"Select\" option");
}
}
Hope this Helps..

problem solved by putting "Convert.ToInt32(p.DastgahEjraeiAsli)" instead of null, so if the ddl1 == 0 (user hasn't selected any item) where skips.
var project = (from p in context.Projects
where Convert.ToInt32(p.DastgahEjraeiAsli) == ((ddl1 == 0) ? Convert.ToInt32(p.DastgahEjraeiAsli) : ddl1) select p);
this is very useful when for example u have a search form with multiple texboxs and dropdownlists and user may choose each of them and you do not know which one will be selected and which one not.
simplified example :
in the search button click event:
int ddl1 = Convert.ToInt32(dropdownlist1.selectedvalue);
int ddl2 = Convert.ToInt32(dropdownlist2.selectedvalue);
string txt1 = textbox1.text;
var project = (from p in context.mytable
where p.field1 == ((ddl1 == 0) ? p.field1 : ddl1)
&& p.field2 == ((ddl2 == 0) ? p.field2 : ddl2)
&& p.field3 == ((txt1 == "") ? p.field3 : txt1)
select p).ToList();
gridview1.datasource = project;
fridview1.databind();
not: each dropdownlist has a default value with text="select an item" and value="0", so if user didn't select any option default value remains 0.

Related

Linq Query using navigation properties and Where clause

I am trying to compose a linq query using navigation properties. I am selecting properties from 3 entities:
Lockers
SectionColumn
Contracts
I require ALL rows from the Lockers table where all the following conditions are met: the LockerTypeId = "308", .OutOfOrder != true, x.SectionColumn.SectionId == "52").
The query below without the condition x.SectionColumn.SectionId == "52" works and returns exactly what I require except rows with Section id of any value are returned as I would expect.
from l in Lockers.Where(x => x.LockerTypeId == "308" && x.OutOfOrder !=
true).DefaultIfEmpty()
select new
{
ColumnNumber = l.ColumnNumber,
LockerTypeId = l.LockerTypeId,
OutOfOrder = l.OutOfOrder,
Rented = l.Contracts.Select(x => x.Contract_ID < 0 ?
false : true).FirstOrDefault(),
Section = l.SectionColumn.SectionId
}
When I add the condition 'x.SectionColumn.SectionId == "52"' as below I get the error "The cast to value type 'System.Int32' failed because the materialized value is null". Either the result type's generic parameter or the query must use a nullable type" in linqpad. SectionId is a string (varchar in SQL Server).
from l in Lockers.Where(x => x.LockerTypeId == "308" && x.OutOfOrder !=
true).DefaultIfEmpty()
I would be grateful for assistance in correctly writing this query.
First off, your code might be a little more straight forward if you stick to pure LINQ. In that case, your code should look something like the following.
var results = from l in Lockers
where l.LockerTypeId == "308" && l.OutOfOrder != true && l.SectionColumn.SectionId == "52"
select new
{
ColumnNumber = l.ColumnNumber,
LockerTypeId = l.LockerTypeId,
OutOfOrder = l.OutOfOrder,
Rented = l.Contracts.Select(x => x.Contract_ID < 0 ? false : true).FirstOrDefault(),
Section = l.SectionColumn.SectionId
}
If l.SectionColumn.SectionId represents valid navigational properties and is of type string, then this should work correctly.
You really haven't done a thorough job of describing the issue (and it looks like you didn't stick around to field questions), but if l.SectionColumn is nullable, you should be able to update your code to something like this.
var results = from l in Lockers
let sectionId = (l.SectionColumn != null) ? l.SectionColumn.SectionId : null
where l.LockerTypeId == "308" && l.OutOfOrder != true && sectionId == "52"
select new
{
ColumnNumber = l.ColumnNumber,
LockerTypeId = l.LockerTypeId,
OutOfOrder = l.OutOfOrder,
Rented = l.Contracts.Select(x => x.Contract_ID < 0 ? false : true).FirstOrDefault(),
Section = l.SectionColumn.SectionId
}

Use of Contains with one Except in LINQ

I am trying to get a list of Menus which does not contain "license".
var menus= MenuRepo.GetAll().Where(x => x.Name.Contains("license") == false)
But now i want to exclude one of them, ie., "licenseUser"
I need "licenseUser" in the list.
How can i achieve this?
var menus = MenuRepo.GetAll().Where
(
x => !x.Name.Contains("license") || x.Name == "licenseUser"
);
(note that a == false when a is boolean is the same as !a, and it's shorter to write)
Just add an OR condition which checks whether the menu name is equal to "licenseUser":
var menus = MenuRepo.GetAll().Where(menu => !menu.Name.Contains("license") || menu.Name == "licenseUser");

Filtering data with multiple filter values

Hello fellow stackoverflowers,
I'm currently working on a project which gives me a bit of trouble concerning filtering data from a database by using multiple filter values. The filter happens after selecting the filters and by clicking a button.
I have 5 filters: Region, Company, Price, and 2 boolean values
Note that Region and Company are special dropdownlist with checkboxes which means the user can select one or more regions and company names.
I already made a few tests and came up with a incomplete code which works a bit but not to my liking.
Problems arise when one of my filters is NULL or empty. I don't really know how to process this. The only way i thought of was using a bunch of IF ELSE statements, but i'm starting to think that this will never end since there are so much possibilities...
I'm sure there is a far more easier way of doing this without using a bunch of IF ELSE statements, but i don't really know how to do it. If anyone could steer me in the right direction that would be appreciated. Thanks
Here is what i have right now (I haven't added the Price to the query for now):
protected void filterRepeater(List<int> regionIDs, string[] companyArray,
string blocFiltValue, bool bMutFunds, bool bFinancing)
{
DatabaseEntities db = new DatabaseEntities();
PagedDataSource pagedDsource = new PagedDataSource();
IQueryable<Blocs> query = (from q in db.Blocs
where q.isActive == true
orderby q.date descending
select q);
IQueryable<Blocs> queryResult = null;
//if some filters are NULL or Empty, it create a null queryResult
queryResult = query.Where(p => companyArray.Contains(p.company) &&
regionIDs.Contains((int)p.fkRegionID) &&
(bool)p.mutual_funds == bMutFunds &&
(bool)p.financing == bFinancing);
if (queryResult.Count() > 0)
{
//Bind new data to repeater
pagedDsource.DataSource = queryResult.ToArray();
blocRepeater.DataSource = pagedDsource;
blocRepeater.DataBind();
}
}
Only add the relevant filters to query:
IQueryable<Blocs> query =
from q in db.Blocs
where q.isActive == true
orderby q.date descending
select q;
if (companyArray != null)
{
query = query.Where(p => companyArray.Contains(p.company));
}
if (regionIDs != null)
{
query = query.Where(p => regionIDs.Contains((int)p.fkRegionID));
}
// ...
// etc
// ...
if (query.Any()) // Any() is more efficient than Count()
{
//Bind new data to repeater
pagedDsource.DataSource = query.ToArray();
blocRepeater.DataSource = pagedDsource;
blocRepeater.DataBind();
}
If you want to filter only by the filter values that are not null or empty then you can construct the query by appending the where clauses one by one:
if(companyArray != null && companyArray.Length > 0) {
query = query.Where(p => companyArray.Contains(p.company));
}
if(regionIDs!= null && regionIDs.Length > 0) {
query = query.Where(p => regionIDs.Contains((int)p.fkRegionID));
}
if (!String.IsNullOrEmpty(blocFiltValue)) {
query = query.Where(p => p.Block == blocFiltValue);
}
Also you can use nullable values for value types, if you need to filter them optionally
bool? bMutFunds = ...; // Possible values: null, false, true.
...
if(bMutFunds.HasValue) {
query = query.Where(p => (bool)p.mutual_funds == bMutFunds.Value);
}
Maybe you can create a string for the SQL sentence, and dynamically add parts to this sentence like if something was selected or checked you add something to this string when thh selection was completed by the user you can execute this SQL sentence.

Check if value is null, and when it is, execute extra (sub)query

Is it possible to check for a null value in a LINQ query and when the value is null, that it executes an extra (sub)query, all at once?
Explanation
I have default buttons declared in my database, with default descriptions. A user can customize these buttons, and these settings are stored in the ButtonLocations table. Now, every button has a standard description and the user can edit this description. When the user edits the description, it is stored in the Descriptions table in my database.
When I retrieve all buttons, I first check if a button has a specific description (in buttonlocations, with a left join). If this is not true (so null), I retrieve the default description.
Currently I get all my entities with their description and after that I loop through all of them to check if the value is null. This results in multiple queries to the database.
var temp = (from bl in context.buttonLocations
join b in context.Buttons
on bl.ButtonID equals b.ButtonID into buttons
from button in buttons.DefaultIfEmpty()
join d in context.Descriptions
on new
{
ID = bl.ButtonLocationID,
langID = languageID,
originID = descriptionOriginID
}
equals new
{
ID = d.ValueID,
langID = d.LanguageID,
originID = d.DescriptionOriginID
}
into other
where bl.ButtonGroupID == buttonGroupId
from x in other.DefaultIfEmpty()
select new
{
Button = button,
ButtonLocation = bl,
Description = x
}).ToList();
// Retrieve default descriptions if no specific one is set
foreach (var item in temp)
{
if (item.Description == null)
{
item.Description = context.Descriptions
.FirstOrDefault(x => x.ValueID == item.Button.ButtonID && x.LanguageID == languageID && x.DescriptionOriginID == (short)DescriptionOriginEnum.Button);
}
}
I think Colin's answer using the coalesce operator ought to work, but if it can't be made to you could try doing a subselect which gets both options, then ordering by preference for custom source, and taking the top record. (I'm assuming here that any given button only really has one description, and multiple descriptions shouldn't result in multiple buttons.)
var temp = (from bl in context.buttonLocations
join b in context.Buttons
on bl.ButtonID equals b.ButtonID into buttons
from button in buttons.DefaultIfEmpty()
let description = (
from d in context.Descriptions
where
d.LanguageID == languageID
&& (
(
d.ValueID == bl.ButtonLocationID
&& d.DescriptionOriginID == descriptionOriginID
)
||
(
d.ValueID == b.ButtonID
d.DescriptionOriginID == (short)DescriptionOriginEnum.Button
)
)
// this line puts custom descriptions first
orderby d.DescriptionOriginID == (short)DescriptionOriginEnum.Button
? 1
: 0
select d
)
// this will get a custom description if there was one, otherwise
// the first one will be the default description
.FirstOrDefault()
where bl.ButtonGroupID == buttonGroupId
select new
{
Button = button,
ButtonLocation = bl,
Description = description
})
.ToList();
This is obviously a bit awkward, and probably not the most efficient query. I would try moving the coalesce operator to a let description = d ?? /*subselect*/ line first.
The null coalescing operator should work for you here. Something like this:
.....
select new
{
Button = button,
ButtonLocation = bl,
Description ?? context.Descriptions
.FirstOrDefault(
x => x.ValueID == button.ButtonID
&& x.LanguageID == languageID
&& x.DescriptionOriginID == (short)DescriptionOriginEnum.Button)
})

Casting String as DateTime in LINQ

I have a table with a following format.
PID ID Label Value
------------------------------------------
1 1 First Name Jenna
1 2 DOB 10/12/1980
I need to retrieve all PIDs where First name starting with J and Month of DOB is 10.
in my code, I retrieve these in DataTable in C# and then tried to use LINQ to retrieve the results I want. This is just an example. These Labels could be anything user defines.
using LINQ I am able to retrieve all PIDs where First Name start with J, but every time I tried to Cast Value for DOB I get cast not valid error. I cannot change the column type in the database since Value could contain any type of information.
Here's a piece of my code. I am new to LINQ, and still trying to figure out around it.
var resultQuery = from r in query.AsEnumerable()
where (r.Field<string>("Label") == Label &&
r.Field<DateTime>("Value").Month == 10)
select r.Field<int>("PID");
Since not all items in the Value column of the table are convertible to DateTime, what you have will fail on invalid conversions. You can add in a clause that first checks that the value is a DateTime and only if it is, converts it and checks the .Month property.
DateTime d;
var resultQuery = from r in query.AsEnumerable()
where (r.Field<string>("Label") == Label &&
DateTime.TryParse(r.Field<string>("Value"), out d) &&
d.Month == 10)
select r.Field<int>("PID");
To potentially improve readability, you could also extract this out into a separate method:
var resultQuery = from r in query.AsEnumerable()
let d = TryGetDate(r.Field<string>("Value"))
where (r.Field<string>("Label") == Label &&
d != null &&
d.Month == 10)
select r.Field<int>("PID");
private DateTime? TryGetDate(string value)
{
DateTime d;
return DateTime.TryParse(value, out d) ? d : default(DateTime?);
}
You are going to end up filtering in memory which isn't very efficient.
So first select your data
var data= from r in query.AsEnumerable();
Then filter on the data
var filtered = from item in data
where item.Label == "Label"
&& Convert.ToDateTime(item.DOB).Month == 10
select item.PID;

Categories