I have this problem for sometime now but today I reached the tipping point and I'm asking for your help.
Suppose I have this code:
var query = DbContext.Table.Where(x => x.z == true).Select(x => new { x.a, x.b, x.c });
foreach(var item in query)
{
// Do the work
}
I end up with this anonymous type and everything is fine. Now something comes up that makes me condition the Where clause, so I have to make a different query according to the condition and this is where I'm really frustrated:
if(something)
{
var query = DbContext.Table.Where(x => x.z == true && x.zz == false).Select(x => new { x.a, x.b, x.c });
foreach(var item in query)
{
// Do the work
}
}
else
{
var query = DbContext.Table.Where(x => x.z == true).Select(x => new { x.a, x.b, x.c });
foreach(var item in query)
{
// Do the work
}
}
because the work to be done in the foreach
cycle is exactly the same and I shouldn't have to repeat it.
So, instead of working with the anonymous type I declared a new class and tried this:
class MyQuery
{
public int a { get; set; }
public string b { get; set; }
public decimal? c { get; set; }
}
IQueryable<MyQuery> query = null;
if(something)
{
query = (IQueryable<MyQuery>)DbContext.Table.Where(x => x.z == true && x.zz == false).Select(x => new { x.a, x.b, x.c });
}
else
{
query = (IQueryable<MyQuery>)DbContext.Table.Where(x => x.z == true).Select(x => new { x.a, x.b, x.c });
}
foreach(var item in query)
{
// Do the work
}
But now I have an 'System.InvalidCastException'
thrown because I'm still getting an anonymous type and apparently it can't be casted into my class.
I want to avoid the repetition of code for the sake of maintenance but I don't know how to do it. I feel that I'm missing something basic here but can't find what so your help is welcome and appreciated.
Thanks a lot for your help.
In C#, an anonymous type is a type (class) without any name that can contain public read-only properties only. It cannot contain other members, such as fields, methods, events, etc. You create an anonymous type using the new operator with an object initializer syntax.
The compiler gives them a name although your application cannot access it. From the perspective of the common language runtime, an anonymous type is no different from any other reference type, except that it cannot be cast to any type except for object.
You create anonymous types by using the new operator together with an object initializer. For more information about object initializers, see Object and Collection Initializers. The following example shows an anonymous type that is initialized with two properties named Amount and Message .
In LINQ, select clause generates anonymous type so that in a query you can include properties that are not defined in the class.
You are overcomplicating it:
IQueryable<MyQuery> query = DbContext.Table;
if (something)
{
query = query.Where(x => x.z == true && x.zz == false)
}
else
{
query = query.Where(x => x.z == true);
}
var result = query.Select(x => new { x.a, x.b, x.c });
foreach (var item in result)
{
// Do the work
}
The .Where()
method doesn't change the "type" of the query!
Note even that this is legal:
var query = DbContext.Table.Select(x => new { x.a, x.b, x.c });
if (something)
{
query = DbContext.Table.Where(x => x.z == true && x.zz == false).Select(x => new { x.a, x.b, x.c });
}
else
{
query = DbContext.Table.Where(x => x.z == true).Select(x => new { x.a, x.b, x.c });
}
foreach (var item in query)
{
// Do the work
}
I'm using the fact that anonymous types with the same parameters (same name of the parameters, same type of parameters, same number of parameters) inside the same assembly are "unified" by the C# compiler. I use this line var query = DbContext.Table.Select(x => new { x.a, x.b, x.c });
only to "give" the type to the var
variable. Then I overwrite completely the query with other queries, because the various .Select(x => new { x.a, x.b, x.c });
"produce" objects of the same (anonymous) type.
You can modify the Where
condition to match a case or another. This way you no longer need to worry about duplicate code or creating another class that will be used only on this method.
var query = DbContext.Table.Where(x => something && (x.z == true && x.zz == false) || !something && (x.z == true)).Select(x => new { x.a, x.b, x.c });
foreach(var item in query)
{
// Do the work
}
Or you can add a ternary comparison how @Rawling mentioned.
var query = DbContext.Table.Where(x => something ? (x.z == true && x.zz == false) : (x.z == true)).Select(x => new { x.a, x.b, x.c });
foreach(var item in query)
{
// Do the work
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With