使用扩展方法和lambda表达式将类转换为数据表

时间:2018-04-16 06:59:14

标签: c# linq lambda

我正在尝试创建一个扩展方法来将类导出为data-table,在这个方法中我想让用户在datatable中导出具有不同名称的属性,假设类中的属性名是" LoginName将"但是用户希望将其导出为"登录"在数据表中,用户也可以指定多个要重命名的属性。

例如以下是类

public class UserInfo
{
   public int UserID { get; set; }
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public string LoginName { get; set; }
   public int CompanyID { get; set; }
}

将此类导出为data-table用户将使用像这样的扩展方法

UserInfo us = UserRep.GetUser("userID","Pass");
DataTable userDetails = null;

//Follwing is a pseudo code it could be different in possible manner
userDetails = us.ExportAsDataTable(u=> new {{u.LoginName,"Login"}, {u.CompanyID ,"Company"}});

//Or

 userDetails = us.ExportAsDataTable(u=> new { Login = u.LoginName, Company = u.CompanyID});

在ExportAsDataTable下我创建了metod以执行该功能但无法提供正确的表达式来获取用户输入。

public static DataTable ExportAsDataTable<TSource, TProperty>(this TSource instance, Expression<Func<TSource, KeyValuePair<TProperty, string>>> renamePropertyMap)
{
   DataTable dataTable = new DataTable();

   //Doing export stuff here

   return dataTable;
 }

//或

public static DataTable ConvertToDataTable<T>(this T instance, Expression<Func<T, object>> renamePropertyMap) where T : EntityBase
{
  //Using this method I am able to get the new name of column from expression like this but not getting the original property name 

   string columnName = (renamePropertyMap.Body as NewExpression).Members[0].Name   

   /*note :- result in columnName is "Login" which is fine,
             but I need to get orignal property name as well, that is
             "LoginName", I am unable to get it from expression.*/


   DataTable dataTable = new DataTable();
   //Doing export stuff here

   return dataTable;
}

4 个答案:

答案 0 :(得分:2)

我会做这样的事情:

public class FluentBuilder<T>
{
    private readonly T _input;
    private readonly Dictionary<string, string> _mappings = new Dictionary<string, string>();

    public FluentBuilder(T input)
    {
        _input = input;
    }
    public FluentBuilder<T> Map(Expression<Func<T, object>> selector, string name)
    {
        MemberExpression member = selector.Body as MemberExpression;
        if (member == null)
            throw new ArgumentException(string.Format("Expression '{0}' refers to a method, not a property.", selector));

        var propInfo = member.Member as PropertyInfo;
        if (propInfo == null)
            throw new ArgumentException(string.Format("Expression '{0}' refers to a field, not a property.", selector));

        _mappings.Add(propInfo.Name, name);
        return this;
    }

    private string GetName(PropertyInfo prop)
    {
        string map;
        if (_mappings.TryGetValue(prop.Name, out map))
            return map;
        return prop.Name;
    }

    public DataTable ToDataTable(string tableName = null)
    {
        var result = new DataTable(tableName);
        foreach (var prop in _input.GetType().GetProperties())
        {
            result.Columns.Add(GetName(prop));
        }

        var values = _input.GetType().GetProperties().Select(x => x.GetMethod.Invoke(_input, new object[0])).ToArray();
        result.Rows.Add(values);
        return result;
    }
}

public static class FluentBuilderExtensions
{
    public static FluentBuilder<T> SetupWith<T>(this T input)
    {
        return new FluentBuilder<T>(input);
    }
}

class Program
{
    public class UserInfo
    {
        public string MailAddress { get; set; }
        public string Username { get; set; }

        public string Password { get; set; }
    }
    static void Main(string[] args)
    {
        var userInfo = new UserInfo()
                       {
                           MailAddress = "foo@bar.com",
                           Username = "foouser",
                           Password = "barpassword"
                       };

        var dt = userInfo.SetupWith()
                         .Map(x => x.MailAddress, "address")
                         .Map(x => x.Username, "user")
                         .ToDataTable();
    }
}

答案 1 :(得分:1)

MoreLINQ已经提供ToDatatable()将IEnumerable结果转换为DataTable。该方法可作为full NuGet package的一部分或作为 source package的一部分提供,您可以将其添加到项目中。

要从UserInfo属性的子集生成DataTable,请在调用Select之前使用ToDataTable(),例如:

var table = myUserInfos.Select(us=>new {Login=us.LoginName, Company=us.CompanyID})
                       .ToDataTable();

如果您只有一个项目,并希望将其转换为单行DataTable:

  • 这是非常奇怪的请求。为什么要这样做而不是将单个对象绑定到UI控件?
  • 您可以将其包装在数组中,例如:

    var table = new[]{theUser}.Select(us=>new {Login=us.LoginName, Company=us.CompanyID})
                              .ToDataTable();
    

答案 2 :(得分:0)

我使用了DataAnnotationsReflection

您需要添加以下缺少的库的引用。我测试了这段代码并且它正在运行。

using System;
using System.Data;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using System.Reflection;

class Program
{

    static void Main(string[] args)
    {
        var user = new UserInfo();

        var name = GetAttributeFrom<DisplayAttribute>(user, "LoginName").Name;

        var dt = objToDataTable(user);

        Console.ReadLine();
    }

    public static DataTable objToDataTable(UserInfo obj)
    {
        DataTable dt = new DataTable();
        UserInfo objU = new UserInfo();

        foreach (PropertyInfo info in typeof(UserInfo).GetProperties())
        {
            dt.Columns.Add(GetAttributeFrom<DisplayAttribute>(objU, info.Name).Name);
        }

        dt.AcceptChanges();

        return dt;
    }

    public static T GetAttributeFrom<T>(object instance, string propertyName) where T : Attribute
    {
        var attrType = typeof(T);
        var property = instance.GetType().GetProperty(propertyName);
        return (T)property.GetCustomAttributes(attrType, false).First();
    }

}


public class UserInfo
{
    [Display(Name = "ID")]
    public int UserID { get; set; }

    [Display(Name = "FName")]
    public string FirstName { get; set; }

    [Display(Name = "LName")]
    public string LastName { get; set; }

    [Display(Name = "Login")]
    public string LoginName { get; set; }

    [Display(Name = "Company")]
    public int CompanyID { get; set; }
}

答案 3 :(得分:0)

这是我构建扩展方法以将类转换为数据表

的方法
public static class EntityExtensions
{
    private static readonly string expressionCannotBeNullMessage = "The expression cannot be null.";
    private static readonly string invalidExpressionMessage = "Invalid expression.";

    public static DataTable ConvertToDataTable<T>(this T instance, Expression<Func<T, object>> proprtiesToSkip = null, Expression<Func<T, object>> proprtiesToRename = null) where T : EntityBase
    {
        string columnName = "";
        string orgPropName;
        int counter = 0;
        Dictionary<string, string> renameProperties = null;
        MemberInfo newName = null;
        NewExpression expression = null;
        List<string> skipProps = null;

    try
    {
        if (proprtiesToSkip != null )
        {
            if (proprtiesToSkip.Body is NewExpression)
            {
                skipProps = new List<string>();
                expression = (proprtiesToSkip.Body as NewExpression);
                foreach (var cExpression in expression.Arguments)
                {
                    skipProps.Add(GetMemberName(cExpression));
                }
            }
            else
            {
                throw new ArgumentException("Invalid expression supplied in proprtiesToSkip while converting class to datatable");
            }
        }

        if (proprtiesToRename != null)
        {
            if (proprtiesToRename.Body is NewExpression)
            {
                renameProperties = new Dictionary<string, string>();
                expression = (proprtiesToRename.Body as NewExpression);
                foreach (var cExpression in expression.Arguments)
                {
                    newName = expression.Members[counter];
                    orgPropName = GetMemberName(cExpression);
                    renameProperties.Add(orgPropName, newName.Name);
                    counter++;
                }
            }
            else
            {
                throw new ArgumentException("Invalid expression supplied in proprtiesToRename while converting class to datatable");
            }
        }

        var properties = instance.GetType().GetProperties().Where(o =>
        {
            return (skipProps != null && !skipProps.Contains(o.Name, StringComparer.OrdinalIgnoreCase) &&
                    (o.PropertyType != typeof(System.Data.DataTable) && o.PropertyType != typeof(System.Data.DataSet)));

        }).ToArray();


        DataTable dataTable = new DataTable();
        foreach (PropertyInfo info in properties)
        {
            columnName = "";
            if (renameProperties != null && renameProperties.ContainsKey(info.Name))
            {
                columnName = renameProperties[info.Name];
            }

            if (string.IsNullOrEmpty(columnName))
            {
                columnName = info.Name;
            }

            dataTable.Columns.Add(new DataColumn(columnName, Nullable.GetUnderlyingType(info.PropertyType) ?? info.PropertyType));
        }

        object[] values = new object[properties.Length];
        for (int i = 0; i < properties.Length; i++)
        {
            values[i] = properties[i].GetValue(instance);
        }

        dataTable.Rows.Add(values);

        return dataTable;
    }
    finally
    {
        renameProperties = null;
        newName = null;
        expression = null;
        skipProps = null;
    }

}        
private static string GetMemberName(Expression expression)
{
    if (expression == null)
    {
        throw new ArgumentException(expressionCannotBeNullMessage);
    }

    if (expression is MemberExpression)
    {
        // Reference type property or field
        var memberExpression = (MemberExpression)expression;
        return memberExpression.Member.Name;
    }

    throw new ArgumentException(invalidExpressionMessage);
}

}

像这样调用

class Program
{
    static void Main(string[] args)
    {

        CancelReason ad = new CancelReason();
        ad.BusinessLineID = 1;
        ad.CancelReasonCodeID = 2;
        ad.CancelRefund = "C";
        ad.CompanyID = 0;
        ad.DBOperation = 1;
        ad.Description = "test";
        ad.IsActive = "Y";
        ad.ReasonCode = "TestCode";
        ad.UpdateStamp = new byte[] { 1, 2, 3, 4 };

        DataTable dt = ad.ConvertToDataTable(s => new { s.DBOperation, s.BusinessLineID, s.LoggedInUser }, r => new { Refund = r.CancelRefund, Company = r.CompanyID });

    }
}