IF statements inside linq query clause - linq

I have written a LINQ query clause,But a error raise up.Can anyone help me out.Thanks in advance:
list = (from cr in dt.AsEnumerable()
select new Cards
{
MemberNumber = cr.Field<string>("MemberNumber"),
if( cr.Field<int>("CardState")==0)
{
CardState ="aaa";
}
if( cr.Field<int>("CardState")==1)
{
CardState ="bbb";
}
if( cr.Field<int>("CardState")==2)
{
CardState ="ccc";
}
if( cr.Field<int>("CardState")==3)
{
CardState ="ddd";
}
if( cr.Field<int>("CardState")==4)
{
CardState ="eee";
}
}).ToList<Cards>();

You can't have branching logic like that inside an initializer. You could write it as a series of ternary operators, however:
list = (from cr in dt.AsEnumerable()
select new Cards
{
MemberNumber = cr.Field<string>("MemberNumber"),
CardState = ( cr.Field<int>("CardState")==0) ? "aaa" :
( cr.Field<int>("CardState")==1) ? "bbb" :
( cr.Field<int>("CardState")==2) ? "ccc" :
( cr.Field<int>("CardState")==3) ? "ddd" :
( cr.Field<int>("CardState")==4) ? "eee" :
( cr.Field<int>("CardState")==5) ? "bbb" : null
}).ToList<Cards>();
Although a better solution may be to separate out that code block into a separate function and call it from your initializer:
list = (from cr in dt.AsEnumerable()
select new Cards
{
MemberNumber = cr.Field<string>("MemberNumber"),
CardState = GetCardState( cr.Field<int>("CardState"))
}).ToList<Cards>();

Related

Linq to Entities Where Or clause

Have read other responses to similar issuebut I can not use PredicateBuilder, or copy its source. I'm trying what I've read here:
<https://blogs.msdn.microsoft.com/meek/2008/05/02/linq-to-entities-combining-predicates/
but as I'm newb, am having trouble with translating what I'm reading to what I'm applying. I have created a L2E query, and trying to append a series of OR clauses onto the WHERE:
So as simplified snippet (this one will be AND'd with the previously already defined WHERE clause):
if (firstParm == "realtor")
query = query.Where(x=> x.A == "realtor");
Now trying to OR:
if (secondParm == "clown")
// how to add this one as an OR to the above query:
query = query.OR(x=> x.fool == "clown");
I understand this can be done also with Union, but not clear on the syntax:
query = query.Union(x=> x.fool == "clown"); // ??
I've also referenced:
Combining two expressions (Expression<Func<T, bool>>)
Unable to create a compound Expression<Func<string, bool>> from a set of expressions
but again, I am new to LINQ and especially Expression Trees, so need more fillin.
There are two ways to generate expressions.
Use the compiler to do it.
Expression<Func<Person, bool>> = p => p.LastName.Contains("A");
Limitations: The only expressions that can be generated this way are instances of LambdaExpression. Also, it is rather complicated to extract parts of the expression and combine with other parts.
Use the static methods at System.Linq.Expressions.Expression.
In order to generate dynamic expressions, you can either choose between different compiler-generated expressions:
// using Record and Records as a placeholder for the actual record type and DbSet property
Expression<Func<Record,bool>> expr;
if (firstParam == "realtor") {
if (secondParam == "clown") {
expr = x => x.A == "realtor" || x.fool == "clown";
} else {
expr = x => x.A == "realtor";
}
} else {
if (secondParam == "clown") {
expr = x => x.fool="clown";
} else {
expr = x => false;
}
}
var ctx = new MyDbContext();
var qry = ctx.Records.Where(expr).Select(x => new {x.A, x.fool});
Or, you can dynamically create the expression using the static methods:
(Add using System.Linq.Expressions; and using static System.Linq.Expressions.Expression; to the top of the file.)
Expression expr;
var parameter = Parameter(typeof(Record));
if (firstParam == "realtor") {
expr = Equals(
MakeMemberAccess(parameter, typeof(Record).GetProperty("A")),
Constant("realtor")
);
}
if (secondParam == "clown") {
var exprClown = Equals(
MakeMemberAccess(parameter, typeof(Record).GetProperty("fool")),
Constant("clown")
);
if (expr == null) {
expr = exprClown;
} else {
expr = Or(expr, exprClown);
}
}
var lambda = Lambda<Func<Record,bool>>(expr, new [] {parameter});
var ctx = new MyDbContext();
var qry = ctx.Records.Where(lambda).Select(x => new {x.A, x.fool});
Given a query with a type unknown at compile time, so any variable referring to it must be IQueryable only, and not IQueryable<T>:
IQueryable qry = ctx.GetQuery(); //dynamically built query here
var parameter = Parameter(qry.ElementType);
if (firstParam == "realtor") {
expr = Equals(
MakeMemberAccess(parameter, qry.ElementType.GetProperty("A")),
Constant("realtor")
);
}
if (secondParam == "clown") {
var exprClown = Equals(
MakeMemberAccess(parameter, qry.ElementType.GetProperty("fool")),
Constant("clown")
);
if (expr == null) {
expr = exprClown;
} else {
expr = Or(expr, exprClown);
}
}
var lambda = Lambda(expr, new [] {parameter});
//Since we don't have access to the TSource type to be used by the Where method, we have
//to invoke Where using reflection.
//There are two overloads of Queryable.Where; we need the one where the generic argument
//is Expression<Func<TSource,bool>>, not Expression<Func<TSource,int,bool>>
var miWhere = typeof(Queryable).GetMethods().Single(mi => {
mi.Name == "Where" &&
mi.GetParameters()[1].ParameterType.GetGenericArguments()[0].GetGenericArguments().Length == 2
});
qry = miWhere.Invoke(null, new [] {qry, lambda});
For Or you can try
if (secondParm == "clown")
{
query = query.Where(x=> x.fool == "clown" || x.fool==x.fool);
}
OR
if (secondParm == "clown")
{
query = query.Where(x=> x.fool == "clown" || true );
}

Count nulls in properties with one LINQ query

Paste into LINQPad:
void Main()
{
List<Data> list = new List<Data>();
list.Add(new Data());
list.Add(new Data{a="a", b="b"});
list.Add(new Data{a=null, b="b"});
var queryA = from data in list where data.a == null select data;
var queryB = from data in list where data.b == null select data;
var countNulls = new {a = queryA.Count(),b = queryB.Count()};
countNulls.Dump();
}
class Data
{
public string a {get;set;}
public string b {get;set;}
}
Instead of using queryA and queryB is it possible to do this in one query?
Answer:
All queries below generate exactly same SQL, so it's just a preference of coder what to choose.
var countNulls = new
{
a = queryA.Count(),
b = queryB.Count()
};
var countNulls2 = new
{
a = list.Count(d => d.a == null),
b = list.Count(d => d.b == null)
};
var countNulls3 = list.Aggregate(
new { a = 0, b = 0 },
(acc, data) => new
{
a = acc.a + (data.a == null ? 1 : 0),
b = acc.b + (data.b == null ? 1 : 0),
});
Update: apparently (thanks to Evan Stoev) this task can be done 20x faster on EF's DbSet and it creates one SQL query.
var countNulls4 =
(from data in db.Data
group data by 1 into g
select new
{
a = g.Sum(data => data.a == null ? 1 : 0),
b = g.Sum(data => data.b == null ? 1 : 0)
}).First();
For completeness, you can use Aggregate to get the result in a single pass over the input sequence:
var countNulls = list.Aggregate(
new { a = 0, b = 0 },
(acc, data) => new
{
a = acc.a + (data.a == null ? 1 : 0),
b = acc.b + (data.b == null ? 1 : 0),
});
But I'm not sure it would be more efficient compared to 2 separate Count calls due to the need of anonymous object allocation on each step.
UPDATE: It turns out that you are asking for a single SQL query (so list is not actually a List<Data> but DbSet<Data> I guess). In LINQ to Entities, you can use group by constant technique, which combined with replacing Count(condition) with Sum(condition ? 1 : 0) will produce a nice single SQL query pretty similar to what you would write manually:
var countNulls =
(from data in db.Data
group data by 1 into g
select new
{
a = g.Sum(data => data.a == null ? 1 : 0),
b = g.Sum(data => data.b == null ? 1 : 0)
}).First();
You can simply use the Count() overload that takes a predicate:
var countNulls = new
{
a = list.Count(d => d.a == null),
b = list.Count(d => d.b == null)
};
Output is:
As far as I understand, you want to count nulls for two different object properties regardless if the other one is null. In other words:
a=null, b="b"
a="a", b=null
a=null, b=null
would return count for 2 and 2.
In that case you can do it this way in one query:
var queryAB = from data in list where data.a == null || data.b == null select data;

Linq Query jumps out of foreach

I have a Linq query, I want to return the RegistrationID (GUID) and an email. when the query gets to the foreach it jumps out on the value.
Can you tell me how the query should be wrote.
using( var fbdc = new FBDataContext() )
{
var query = ( from q in fbdc.Registrations
where q.BookingDateTime == DateTime.Now.AddDays( -1 )
select new
{
q.RegistrationID,
q.Email
} );
foreach( var value in query )
{
registrationId = value.RegistrationID;
email = value.Email;
}
}
Plus, I'm not sure if the query would work. Is there a way to test this?
The BookingDateTime is a DateTime so thought this would work as I only want the files from yesterday. Could I use a more exact expression to query the date(30/09/2014)?
You can use it by as below:
var query = ( from q in fbdc.Registrations
where q.BookingDateTime == DateTime.Now.AddDays( -1 )
select new
{
q.RegistrationID,
q.Email
} ).AsEnumerable();

Column in gwt celltable doesn't sort

I want to add sorting for column in celltable with help of ListHandler. But it doesn't sort. I don't understand why it doesn't work. My code is based on GWT tutorial.
Please suggest me something.
Contract.java
public class Contract implements Serializable {
private int contId;
private String contOrgName;
//getters and setters...
}
Main.java
TextColumn<Contract> orgNameColumn = new TextColumn<Contract>() {
#Override
public String getValue(Contract contract) {
return contract.getcontOrgName();
}};
orgNameColumn.setSortable(true);
CellTable<Contract> tableContract = new CellTable<Contract>();
tableContract.addColumn(orgNameColumn, "OrgName");
ListDataProvider<Contract> contractDataProvider = new ListDataProvider<Contract>();
contractDataProvider.addDataDisplay(tableContract);
GetContractsServiceAsync getContractsService = GWT.create(GetContractsService.class);
getContractsService.getContracts(new AsyncCallback<List<Contract>>() {
public void onFailure(Throwable caught) {
// Show the RPC error message to the user
}
public void onSuccess(List<Contract> result) {
contractDataProvider.getList().clear();
contractDataProvider.getList().addAll(result);
ListHandler<Contract> columnSortHandler = new ListHandler<Contract>(result);
columnSortHandler.setComparator(orgNameColumn, new Comparator<Contract>() {
public int compare(Contract o1, Contract o2) {
if (o1 == o2) {
return 0;
}
if (o1 != null) {
return (o2 != null) ? o1.getcontOrgName().compareTo(o2.getcontOrgName()) : 1;
}
return -1;
}
});
tableContract.addColumnSortHandler(columnSortHandler);
table.getColumnSortList().push(orgNameColumn);
}
});
Hey I was also facing the same issue a long back..Then I got to know that the object reference id is different than the one I have added into table. Once i got the reason it was very easy to find the solution.
Also please register your ListHandler before adding column to CellTable.
Note the edit for your comment :
There is nothing wrong with the comparator. The problem is still same. It reflects the wrong the object in comparator compared to in CellTable.
Please try replace the code as below :
contractDataProvider.getList().addAll(result);
ListHandler<Contract> columnSortHandler = new ListHandler<Contract>(contractDataProvider.getList());
Try to move the creation of your columnSortHandler out of the onSuccess Method. Place it right after you created your column.
At the moment you are adding the results before you add the columnSortHandler. I think that is the wrong order.
i my case, i did my sorting like this:
private void sortColumn( List<CModel> list ) {
int count = listOfColumns.size(); //upon adding the columns, i have created a list that will hold all the columns
if ( list.size() <= 0 ) {
for ( int i = 0; i < count; i++ ) {
listOfColumns.get(i).setSortable( false );
}
} else {
for ( int i = 0; i < count; i++ ) {
sort( i );
dg.redrawHeaders();
}
}
}
private void sort(int i) {
String[] listColHead = { /** list of column headers */ };
Column<CModel,?> column = listCheckoutColumns.get( i );
final String valueToCompare = listColHead[i];
final ListDataProvider<CModel> dataProvider = new ListDataProvider<CModel>();
// Connect the table to the data provider
dataProvider.addDataDisplay( dg );
// Add the data to the data provider, which automatically pushes it to the widget
List<CModel> list = dataProvider.getList();
for ( CModel product : listCheckout ) {
list.add( product );
}
column.setSortable( true );
// Add a ColumnSortEvent.ListHandler to connect sorting to the List
ListHandler<CModel> columnSortHandler = new ListHandler<CModel>( list );
columnSortHandler.setComparator( column, new Comparator<CModel>() {
public int compare( CModel p1, CModel p2 ) {
if ( p1 == p2 ) {
return 0;
}
// Compare the columns.
if ( p1 != null ) {
if ( valueToCompare.equals( "code" ) ) {
return ( p2 != null ) ? p1.getFproductid().compareTo( p2.getFproductid() ) : 1;
}
else if ( valueToCompare.equals( "desc" ) ) {
return ( p2 != null ) ? p1.getFproduct_name().compareTo( p2.getFproduct_name() ) : 1;
}
else if ( valueToCompare.equals( "type" ) ) {
return ( p2 != null ) ?
uec.computeTypeLabel( p1 ).compareTo( uec.computeTypeLabel( p2 ) ) : 1;
}
else if ( valueToCompare.equals( "maxex" ) ) {
return ( p2 != null ) ? uec.exQtyLabel( p1 ).compareTo( uec.exQtyLabel( p2 ) ) : 1;
}
else if ( valueToCompare.equals( "unit" ) ) {
return ( p2 != null ) ? p1.getFuom().compareTo( p2.getFuom() ) : 1;
}
else if ( valueToCompare.equals( "status" ) ) {
return ( p2 != null ) ? uec.qtyLabel( p1 ).compareTo( uec.qtyLabel( p2 ) ) : 1;
}
else if ( valueToCompare.equals( "qty" ) ) {
return ( p2 != null ) ? Double.compare( p1.getFqty(), p2.getFqty() ) : 1;
}
else if ( valueToCompare.equals( "price" ) ) {
return ( p2 != null ) ? uec.extPriceLabel( p1 ).compareTo( uec.extPriceLabel( p2 ) ) : 1;
}
else if ( valueToCompare.equals( "total" ) ) {
return ( p2 != null ) ? Double.compare( p1.getFtotal_line(), p2.getFtotal_line() ) : 1;
}
}
return -1;
}
});
dg.addColumnSortHandler( columnSortHandler );
dg.getColumnSortList().push( column );
}
everytime, i'll set the data for my DataGrid list,
dgCheckout.redraw();
dgCheckout.setRowCount( lengthOfList, true );
dgCheckout.setRowData( 0, newList );
sortColumn( newList );

Good practice for using "NOT IN" in a LINQ query

I have converted the following SQL query to LINQ with the exception of the "NOT IN" subquery.
What would be the most effective way to implement this using LINQ? Should I use a join instead?
If anybody is able to provide an example or some guidance, I'd appreciate it.
New LINQ Query:
return from objOpenCalls in db.OpenItemss
from objTasks in db.Tasks
.Where(t => (t.Task_ID == objOpenCalls.Parent_Task_ID))
where ((objTasks.Item_ID > 0) && (objTasks.Type_ID > 0) && (objTasks.OwnerTypeItem_ID == user) && (objOpenCalls.CallEnd < DateTime.Now))
orderby objOpenCalls.CallStart descending
select new CallMiniViewModel
{
ID = objOpenCalls.ID,
CallStart = objOpenCalls.CallStart,
Name = objTasks.Task_Title
};
Old SQL Query:
SELECT TOP (100) ta.ID, t.Task_Title, ta.CallStart
FROM OpenItems AS ta INNER JOIN
Tasks AS t ON ta.Parent_Task_ID = t.Task_ID
WHERE
(t.Item_ID > 0) AND (t.[Type_ID] > 0) AND (ta.CallStart > DATEADD(m, -6, GETDATE()))
AND (ta.ID NOT IN (SELECT CallId FROM CallFeedback)) AND (t.OwnerTypeItem_ID = #Username) AND (ta.CallEnd < GETDATE())
ORDER BY ta.CallStart DESC
There are a couple of ways of doing the not in. Below is just a quick sample put in LinqPad as a test.
class MyClass {
public int Id {get;set;}
}
void Main()
{
int[] myItems = new[] { 1, 2, 3, 4, 5, 6 };
IEnumerable<MyClass> classes = new []{
new MyClass { Id = 3 },
new MyClass { Id = 6 },
new MyClass { Id = 8 }
};
var results = from cl in classes
where !myItems.Contains( cl.Id )
select cl;
foreach(var result in results) {
Console.WriteLine( "Class {0}", result.Id);
}
var results2 = from cl in classes
where (
from i in myItems
where i == cl.Id
select i ).Count( ) == 0
select cl;
foreach(var result in results2) {
Console.WriteLine( "Class {0}", result.Id);
}
}
I normally play with code first in LinqPad as it helps me understand any problems, and it will (if you're working with SQL) then show you what SQL the query will generate and you can fine tune a little. Sometimes it takes a little time to get your stuff able to be run in there, but it's worth it with the more complex queries.

Categories

Resources