user3757742
user3757742

Reputation: 85

Get fields from anonymous type

I have this class:

public class allFields
{
    public string EAN { get; set; }
    public string title { get; set; }
    public string qty { get; set; }
    public string price { get; set; }
    public DateTime date { get; set; }
}

And a function that return an anonymous type:

public IEnumerable<object> stockEtatQty()
{   
    List<allFields> afList = new List<allFields>();

    var query = from x in ctx.book
                where x.qty > 0
                select x;
    foreach (var item in query)
    {
        allFields af = new allFields();
        af.EAN = item.EAN;
        af.title = item.Titre;
        af.qty = ""+item.Quantite;
        afList.Add(af);
    }
    var q = from x in afList
            select new { EAN=x.EAN, Title=x.title, Quantity=x.qty };

    return q; //q is a IEnumerable<'a> where a is new {string EAN, string Title, string Quantity}
}

In my WinForm a use this function as below:

private void QuantityToolStripMenuItem_Click(object sender, EventArgs e)
{
    ServiceStock sstock = new ServiceStock();
    var q = sstock.stockEtatQty().ToList();// q is a list<object>
    string str = "";
    foreach (var item in q)
    {
        str += item + Environment.NewLine;
    }
    MessageBox.Show(str);
}

The result is:

{ EAN = 1, Title = CSharp Security, Quantity = 970 }
{ EAN = 2, Title = MISC, Quantity = 100 }
...

What I want?
I want not like the result above, but separate each field apart of the item in the loop foreach, e.g get item.EAN, item.Title and item.Quantity.

If there is no solution for my problem I would like to know an alternative,
Thanks for help.

Upvotes: 1

Views: 2466

Answers (4)

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726639

Since the static type information about the object of anonymous type is lost by the time that you exit stockEtatQty() method, you could cast the object to dynamic and access fields like this:

str = string.Join(Environment.NewLine, q.Cast<dynamic>().Select(item => 
    string.Format("{0} {1} {2}", item.EAN, item.Title, item.Quantity)
));

The cast to dynamic tells the compiler that EAN, Title, and Quantity need to be resolved at runtime.

Note that I also replaced the foreach loop with a call to string.Join to improve performance: repeated string concatenation creates unnecessary partial string objects, which string.Join avoids. Another solution would be to use StringBuider instead of string concatenation +=.

stockEtatQty is in a project (Service) and QuantityToolStripMenuItem_Click is in another project (View)

Unfortunately, this means that you would not be able to use anonymous types: anonymous types are generated with internal visibility, limiting their use to the assembly in which they are produced. You can use a work-around based on ExpandoObject described in this answer:

var q = afList.Select(x => {
    dynamic res = new ExpandoObject();
    res.EAN=x.EAN;
    res.Title=x.title;
    res.Quantity=x.qty;
    return res;
});

Upvotes: 1

sloth
sloth

Reputation: 101072

The obvious solution is to create a custom type (let's call it BookInfo) and return a IEnumerable<BookInfo> instead of a IEnumerable<object> (and maybe override ToString if you want to put the formatting into this class itself).

Then you can easily format the output.

public class BookInfo
{
    public string EAN {get;set;}
    public string Title {get;set;}
    public int Quantity {get;set;}
}

public IEnumerable<BookInfo> stockEtatQty()
{   
    ...

    var q = from x in afList
            select new BookInfo { EAN=x.EAN, Title=x.title, Quantity=x.qty };

    return q;
}

private void QuantityToolStripMenuItem_Click(object sender, EventArgs e)
{
    ServiceStock sstock = new ServiceStock();
    var q = sstock.stockEtatQty();

    var message = string.Join(Environment.NewLine, 
                              q.Select(item => String.Format("{0} {1} {2}", item.EAN, item.Title, item.Quantity)));
    MessageBox.Show(message);
}

Upvotes: 1

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236248

You can use collection of dynamic objects instead of simple objects as return type of your method:

public IEnumerable<dynamic> stockEtatQty()

Then you will not have IntelliSense but at runtime properties will be found:

foreach (var item in sstock.stockEtatQty())    
    str += String.Format("{0}", item.EAN) + Environment.NewLine;

But I suggest you to create custom class with EAN, Title and Quantity properties. Or just use your allFields instead of anonymous objects.


Consider also to use StringBuilder for string creation to avoid creating lot of in-memory strings:

var builder = new StringBuilder();
foreach (var item in sstock.stockEtatQty())
   builder.AppendFormat("{0}{1}", item.EAN, Environment.NewLine);

MessageBox.Show(builder.ToString());

Upvotes: 0

Oliver
Oliver

Reputation: 36423

Create a new class that represents the new object structure and return that.

var q = from x in afList
        select new SmallerType { EAN=x.EAN, Title=x.title, Quantity=x.qty };

WinForm Function

foreach (SmallerType item in q)
{
  //
}

Upvotes: 0

Related Questions