Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating an anonymous type depending on different conditions and casting it to a class

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.

like image 277
Francisco Avatar asked May 22 '15 11:05

Francisco


People also ask

When can anonymous types be created?

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.

What is the difference between an anonymous type and a regular data type?

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.

How do I make an anonymous 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 .

Which of the following selects an anonymous type?

In LINQ, select clause generates anonymous type so that in a query you can include properties that are not defined in the class.


2 Answers

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.

like image 175
xanatos Avatar answered Oct 17 '22 08:10

xanatos


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
}
like image 20
adricadar Avatar answered Oct 17 '22 08:10

adricadar