2

I am using LinQ inside an if statement. Also defining as var since there are a few columns of different types. The problem is that after the if statement, I want to do a foreach but since the var result is defined inside the if statement, it is not accessible outside.

After researching the problem I found online someone used "cast by example" to solve this. But I can't seem to understand how it works, nor how to adjust it to my actual example.

Here is the cast by example code:

static IEnumerable<T> SequenceByExample<T>(T t) { return null; }

Below is the main if statement which I will have different LinQ query's inside:

if ((currentObject.CurrentAccount == "") && (currentObject.CurrentSector == "All"))
{
    var result = from row in datatableMasterA.AsEnumerable()
                 group row by new
                 {
                     symbol = row.Field<string>("BloombergSymbol"),
                     desc  = row.Field<string>("Description")
                 }
                 into grp
                 select new
                 {
                     symbol = (string)grp.Key.symbol,
                     desc = (string)grp.Key.desc,
                     delta = grp.Where(x => x.Field<string>("TD_Indicator") == "P").Select(r => r.Field<decimal>("Delta")).FirstOrDefault(),
                     prevQty = grp.Where(x => x.Field<string>("TD_Indicator") == "P").Sum(r => r.Field<Int64>("Qty_Net")),
                     prevPl = grp.Where(x => x.Field<string>("TD_Indicator") == "P").Sum(r => r.Field<double>("PL_USD")),
                     topQty = grp.Where(x => x.Field<string>("TD_Indicator") == "T").Sum(r => r.Field<Int64>("Qty_Net")),
                     topPl = grp.Where(x => x.Field<string>("TD_Indicator") == "T").Sum(r => r.Field<double>("PL_USD"))
                };

    CreateDatagridBreakdownPartB(result.ToArray());
}

//portfolio-single sector
else if ((currentObject.CurrentAccount == "") && (currentObject.CurrentSector != "All"))
{
}

//single account-all sectors
else if ((currentObject.CurrentAccount != "") && (currentObject.CurrentSector == "All"))
{
}

//single account-single sector
else if ((currentObject.CurrentAccount != "") && (currentObject.CurrentSector != "All"))
{
}
else
{
    MessageBox.Show("Error in CreateDataGridBreakdown");
}

 #endregion

//foreach (var x in result)
//{
//}

Can someone please assist me in this issue? Much appreciated!

Abbas
  • 14,186
  • 6
  • 41
  • 72
solarissf
  • 1,199
  • 2
  • 23
  • 58
  • 2
    first of all, you got the *cast by example* code wrong, yours does nothing - http://blogs.msdn.com/b/alexj/archive/2007/11/22/t-castbyexample-t-object-o-t-example.aspx – decPL Nov 05 '14 at 16:14
  • 3
    Does each if statement produce different `select` structures (properties)? – musefan Nov 05 '14 at 16:17
  • yes, each if selects different properties – solarissf Nov 05 '14 at 16:18
  • Dynamic type is your friend then - http://msdn.microsoft.com/en-us/library/dd264736.aspx. The *cast by example* trick would only work if your results have a fixed signature. – decPL Nov 05 '14 at 16:20
  • 1
    @solarissf: So how do you expect your foreach to know the object structure when it gets there? `var` isn't magic, it will always evaluate to a specific type and thus the `foreach` can only expect 1 type, not 4 different ones. If you want to stick with `var` then you need to have 4 `foreach` loops, 1 in each of your `if` statements – musefan Nov 05 '14 at 16:21
  • I must have explained it wrong. the results of the linq will always have the same structure. for example... string string decimal int64 .... the only different is adding or changing a where clause to the linq query – solarissf Nov 05 '14 at 16:22
  • @solarissf: You need more than the same data types, you need the **exact** same object definition. Why don't you just create a class with the properties you want, then you can do: `select new MyClass() { symbol = (string)grp.Key.symbol, \*etc.*\ };` – musefan Nov 05 '14 at 16:25
  • 1
    If only the where clause is different, then define your base query outside the if/else blocks, then just tack on the relevent where clause e.g. `result = result.Where(...` – Ben Robinson Nov 05 '14 at 16:32
  • thanks for all the info... based on everyone's comments I realize creating a class was the better option. thanks for the idea! – solarissf Nov 05 '14 at 16:42
  • possible duplicate of [Cast to Anonymous Type](http://stackoverflow.com/questions/1409734/cast-to-anonymous-type) – Agat Nov 05 '14 at 16:58

3 Answers3

0

Basically, you should follow this example.

The example is here:

    void YourMethod()
    {
        var arr = new int[5];

        var anonim = arr.Select(x => new {value1 = "", value2 = "", value3 = "", value4 = 5});

        var anonimAsTemplate = new {value1 = "", value2 = "", value3 = "", value4 = 5};
        var anonimCasted = CastTo(anonim, anonimAsTemplate);
    }

    private static IEnumerable<T> CastTo<T>(Object value, T targetType)
    {
        // targetType above is just for compiler magic
        // to infer the type to cast x to
        return (IEnumerable<T>)value;
    }

But this is really, really bad idea to use in life-time applications. Unless, for studying only. Instead, you should use real classes to return from your LINQ statements.

Community
  • 1
  • 1
Agat
  • 4,577
  • 2
  • 34
  • 62
0

Assuming your anonymous type is the same in all if clauses and you want to use the cast by example trick, described here, you need to:

Create the extension method like so:

public static T SequenceByExample<T>(this object o, T example)
{
    return (IEnumerable<T>) o;
} 

Change your code to something like so:

Object result;
if ((currentObject.CurrentAccount == "")
 && (currentObject.CurrentSector == "All"))
{
    result = from row in datatableMasterA.AsEnumerable()
             group row by new
/* [...] */

And then the foreach loop could be done like this:

foreach (var item in result.SequenceByExample(new
                                              {
                                                symbol = "",
                                                desc = "",
                                                delta = 0M,
                                                /*...*/
                                              }))

Having said that all, seriously consider simply creating your entity type:

class MyEntity
{
   public String symbol { get; set; }
   public String desc { get; set; }
   public decimal delta { get; set; }
   /* ... */
}
decPL
  • 5,384
  • 1
  • 26
  • 36
  • thank you for pointing that out, I went with the better idea of creating a new class. always appreciate finding out the most efficient way! – solarissf Nov 05 '14 at 16:44
-1

I would recommend you create a class to hold your data, something like this:

public class MyClass{
    public string Symbol { get; set; }
    public string Description { get; set; }
}

Then before your if statements, define the list of results:

List<MyClass> results;

Then in each if statement you can select like this:

results = (from row in datatableMasterA.AsEnumerable()
             group row by new
             {
                 symbol = row.Field<string>("BloombergSymbol"),
                 desc  = row.Field<string>("Description")
             }
             into grp
             select new MyClass()
             {
                 Symbol = (string)grp.Key.symbol,
                 Description = (string)grp.Key.desc,
                 //etc...
            }).ToList();

The your foreach should be ok...

foreach (MyClass x in results)
{
    string symbol = x.Symbol;
}
musefan
  • 47,875
  • 21
  • 135
  • 185
  • Not sure what the down votes are for, if there is something wrong with my answer then please let me know so it can be amended. It might just be that they are from a couple of people I annoyed on a different question the other day... – musefan Nov 10 '14 at 09:00