1

I have a Linq expression which I'd like to insert to a ADO.NET datatable. I would like to know the name of the fields from the query in order to set the datatable name. Here is an example of my code:

var result=from item in context.table
           select new{
                field1=... ,
                field2=... ,
                field3=...
           };

What I'd like to do is to set the tables name.

Datatable.Columns.Add("field1"); .. etc

I tried to do it manually but I believe there should be an elegant solution for it.

Thanks

David Rasuli
  • 812
  • 4
  • 15
  • 30

4 Answers4

4

My suggestion:

var result=from item in context.table
           select new{
                field1=... ,
                field2=... ,
                field3=...
           };

static IEnumerable<string> GetPropertyNames<T>(IEnumberable<T> lst) 
{
  foreach (var pi in typeof(T).GetProperties())
  {
    yield return pi.Name;
  }
}

var propnames = GetPropertyNames(result);
leppie
  • 115,091
  • 17
  • 196
  • 297
3

By making use of reflection you can extract the names of the properties of your anonymous type created in the LINQ expression.

var result = from item in context.table
    select new {
         field1 = ... ,
         field2 = ... ,
         field3 = ... };

if (result.Any())
{
    Type t = result.First().GetType();
    foreach (PropertyInfo p in t.GetProperties())
    {
        // Get the name of the prperty
        Console.WriteLine(p.Name);
    }
}
Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
Pranay Rana
  • 175,020
  • 35
  • 237
  • 263
3

This might be bit simpler ;) ...

var fieldNames = (from p in query.GetType().GetProperties() select p.Name).ToArray();
Tony O'Hagan
  • 21,638
  • 3
  • 67
  • 78
2

Because select new creates an anonymous type, there isn't a short elegant solution that I know of, but you can do what you want. The idea here is that you will take the first item returned by the query, and using the type information of the anonymous type, we can reflect its properties and fill your DataTable.

We can do this using the following method that takes a DataTable and the Type information of the anonymous type.

public static void FillColumns(DataTable table, Type anonymousType) {
    PropertyInfo[] properties = anonymousType.GetProperties();

    foreach (PropertyInfo property in properties) {
        table.Columns.Add(property.Name);
    }
}

Then for example you can do something like this

var result = from item in context.Table
            select new {
                field1 = item.f1,
                field2 = item.f2,
                field3 = item.f3
            };

if (result.Count() != 0) {
    DataTable table = new DataTable("Table");
    FillColumns(table, result.First().GetType());
}

Your DataTable in this short example would yield 3 columns field1, field2, and field3.
Edit Already invested the time, so might as well post the full working example.

namespace ConsoleApplication1
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Diagnostics;
    using System.Linq;
    using System.Reflection;

    #region Fake Database

    internal class DatabaseMock
    {
        private DatabaseMock() { 
            // Hides the default public constructor created by the compiler
            // Uses the factory pattern for creation instead
        }

        /// <summary>
        /// Creates a new instance of a database with three default items
        /// </summary>
        public static DatabaseMock Create() {
            DatabaseMock database = new DatabaseMock();

            List<ItemMock> items = new List<ItemMock>();
            items.Add(new ItemMock("item1"));
            items.Add(new ItemMock("item2"));
            items.Add(new ItemMock("item3"));

            database.Table = items;

            return database;
        }

        /// <summary>
        /// Gets the items in the database
        /// </summary>
        public IEnumerable<ItemMock> Table {
            get;
            private set;
        }
    }

    internal struct ItemMock
    {
        /// <summary>
        /// Initializes a new instance of the ItemMock class
        /// </summary>
        public ItemMock(string value) {
            _value = value;
        }

        private string _value;
        /// <summary>
        /// Gets the items value
        /// </summary>
        public string Value {
            get {
                return _value;
            }
        }
    }

    #endregion

    static class Program
    {
        /// <summary>
        /// Takes the specified DataTable and anonymous type information, and populates the table with a new DataColumn per anonymous type property
        /// </summary>
        public static void FillColumns(DataTable table, Type anonymousType) {
            PropertyInfo[] properties = anonymousType.GetProperties();

            foreach (PropertyInfo property in properties) {
                table.Columns.Add(property.Name);
            }
        }

        static void Main() {
            DatabaseMock database = DatabaseMock.Create();

            var query =
                from item in database.Table
                select new {
                    field1 = item.Value,
                    field2 = item.Value,
                    field3 = item.Value
                };

            if (query.Count() != 0) {
                DataTable table = new DataTable("Table");
                FillColumns(table, query.First().GetType());

#if DEBUG
                foreach (DataColumn column in table.Columns) {
                    Debug.WriteLine(column.ColumnName);
                }
#endif
            }
        }
    }
}
David Anderson
  • 13,558
  • 5
  • 50
  • 76
  • David, this doesn't retrives the right names, it retrives the names of the original table (items at your case) and not field1,2,3 – David Rasuli May 03 '12 at 10:37
  • I ran the example multiple times and it returns field1, field2, and field3. It wouldn't output the names of the original table unless you passed in the type information for context.Table, which is not what this example does. Make sure you are passing in the type information for `result.First().GetType()` and not `context.Table`. – David Anderson May 03 '12 at 10:48
  • Could be my bad, I'll check it – David Rasuli May 03 '12 at 10:53
  • This can be made much 'prettier' with proper use of generics. – leppie May 03 '12 at 10:58
  • 1
    I don't see any urgent necessity for the overhead of generics for special-case code here. – David Anderson May 03 '12 at 11:09
  • @DavidAnderson: Your code will fail if the list is empty. Using generics (as in my answer below) allows one to create the datatable without any elements :) – leppie May 04 '12 at 08:56
  • Its just an example leppie. Calm down. – David Anderson May 04 '12 at 09:25
  • I do appreciate and respect your opinion however. Just wanted you to know that. – David Anderson May 04 '12 at 09:37