C # FOREACH for multiple types LIST <>

Hello everyone and thanks for watching.

I don't think this is possible, but I would like to make an identical FOREACH across multiple LIST <> return types without having to cut and paste the code 4 times. All properties of the returned lists dto2, dto3, dto4, and dto5 are the same except for DataValue, which is a different data type for each (int, varchar, bool, etc.).

var dto2 = rd.EngDetailBitsList(dto.EngId);
var dto3 = rd.EngDetailDateTimesList(dto.EngId);
var dto4 = rd.EngDetailVarCharsList(dto.EngId);
var dto5 = rd.EngDetailVarCharMaxesList(dto.EngId);

foreach (var x in dto2)
{
    var propertyInfo = dto.GetType().GetProperty(x.ShortDescript,
           BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
    if (propertyInfo != null)
    { 
        propertyInfo.SetValue(dto, x.DataValue);
    }
}

foreach (var x in dto3)
{
    var propertyInfo = dto.GetType().GetProperty(x.ShortDescript,
           BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
    if (propertyInfo != null)
    {
        propertyInfo.SetValue(dto, x.DataValue);
    }
}

foreach (var x in dto4)
{
    var propertyInfo = dto.GetType().GetProperty(x.ShortDescript,
           BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
    if (propertyInfo != null)
    {
        propertyInfo.SetValue(dto, x.DataValue);
    }
}

foreach (var x in dto5)
{
    var propertyInfo = dto.GetType().GetProperty(x.ShortDescript,
           BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
    if (propertyInfo != null)
    {
        propertyInfo.SetValue(dto, x.DataValue);
    }
}

      

+3


source share


5 answers


There are two ways to resolve this issue:



  • Assuming that all the dto2

    , dto3

    , dto4

    and dto5

    are collections of some type T

    that implements a common interface with your ad on it properties ShortDescript

    and DataValue

    .

    var dto2 = rd.EngDetailBitsList(dto.EngId);
    var dto3 = rd.EngDetailDateTimesList(dto.EngId);
    var dto4 = rd.EngDetailVarCharsList(dto.EngId);
    var dto5 = rd.EngDetailVarCharMaxesList(dto.EngId);
    
    var source = dto2.Cast<MyInterface>
                     .Concat(dto3.Cast<MyInterface>)
                     .Concat(dto4.Cast<MyInterface>)
                     .Concat(dto4.Cast<MyInterface>);
    
    
    var dtoType = dto.GetType();
    foreach (var x in source)
    {
        var propertyInfo = dtoType.GetProperty(x.ShortDescript,
               BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
        if (propertyInfo != null)
        {
            propertyInfo.SetValue(dto, x.DataValue);
        }
    }
    
          

  • Without a regular interface, you can use dynamic

    :

    var dto2 = rd.EngDetailBitsList(dto.EngId);
    var dto3 = rd.EngDetailDateTimesList(dto.EngId);
    var dto4 = rd.EngDetailVarCharsList(dto.EngId);
    var dto5 = rd.EngDetailVarCharMaxesList(dto.EngId);
    
    var source = dto2.Cast<dynamic>
                     .Concat(dto3.Cast<dynamic>)
                     .Concat(dto4.Cast<dynamic>)
                     .Concat(dto4.Cast<dynamic>);
    
    dto.GetType() 
    foreach (var x in source)
    {
        var propertyInfo = dtoType.GetProperty(x.ShortDescript,
               BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
        if (propertyInfo != null)
        {
            propertyInfo.SetValue(dto, x.DataValue);
        }
    }
    
          

    This will cause the properties ShortDescript

    and to DataValue

    be resolved at runtime, and you will get an exception if there is no such type in the current type.

+2


source


If you want to fully reflect the solution, you can do a method like this:

static void SetDtoFields<T>(object targetDto, IEnumerable<T> fields)
{
    Type fieldType = typeof(T);

    var fieldNameProp = fieldType.GetProperty("ShortDescript");
    if (fieldNameProp == null || !fieldNameProp.CanRead) 
        return;

    var dataValProp = fieldType.GetProperty("DataValue");
    if (dataValProp == null || !dataValProp.CanRead) 
        return;

    Type targetType = targetDto.GetType();

    foreach (T field in fields)
    {
        var propToSet = targetType.GetProperty((string)fieldNameProp.GetValue(field),
                BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase );

        if (propToSet != null && propToSet.CanWrite &&
            propToSet.PropertyType.IsAssignableFrom(dataValProp.PropertyType))
        {
            propToSet.SetValue(targetDto, dataValProp.GetValue(field));
        }
    }
}

      

Then, in your main code, you can simply:



SetDtoFields(dto, rd.EngDetailBitsList(dto.EngId));
SetDtoFields(dto, rd.EngDetailDateTimesList(dto.EngId));
SetDtoFields(dto, rd.EngDetailVarCharsList(dto.EngId));
SetDtoFields(dto, rd.EngDetailVarCharMaxesList(dto.EngId));

      

Here is a working demo: https://dotnetfiddle.net/GhrJ0f

0


source


I would try something like this. For your dto2, dto3, dto4, dto5 classes, make them share this interface:

public interface IDto
{
    string ShortDescript {get;set;}
    object ObjectValue {get;}
}

      

Implement ObjectValue in your objects (showing one example):

public partial class DetailBits // dto2 class maybe?
{
    public object ObjectValue
    {
        get
        {
            return DataValue;
        }
    }
}

      

Then create this function:

public static void SetValues(DTO dto, IEnumerable<IDto> items)
{
    foreach (var x in items)
    {
        var propertyInfo = dto.GetType().GetProperty(x.ShortDescript,
               BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
        if (propertyInfo != null)
        { 
            propertyInfo.SetValue(dto, x.ObjectValue);
        }
    }
}

      

Finally, you can do this in your main function:

var dto2 = rd.EngDetailBitsList(dto.EngId).Cast<IDto>();
var dto3 = rd.EngDetailDateTimesList(dto.EngId).Cast<IDto>();
var dto4 = rd.EngDetailVarCharsList(dto.EngId).Cast<IDto>();
var dto5 = rd.EngDetailVarCharMaxesList(dto.EngId).Cast<IDto>();

SetValues(dto, dto2);
SetValues(dto, dto3);
SetValues(dto, dto4);
SetValues(dto, dto5);

      

0


source


I used Martin # 2 dynamic solution with a few editing changes. Works amazing!

        var dto2 = rd.EngDetailBitsList(dto.EngId);
        var dto3 = rd.EngDetailDateTimesList(dto.EngId);
        var dto4 = rd.EngDetailVarCharsList(dto.EngId);
        var dto5 = rd.EngDetailVarCharMaxesList(dto.EngId);

        var source = dto2.Concat(dto3.Concat(dto4.Concat(dto5.Cast<dynamic>())));

        var dtoType = dto.GetType();
        foreach (var x in source)
        {
            var propertyInfo = dtoType.GetProperty(x.ShortDescript,
                   BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
            if (propertyInfo != null)
            {
                propertyInfo.SetValue(dto, x.DataValue);
            }
        }

      

0


source


You can try this, which it does, just take your DTO object, collection and name of the properties to get and set.

    var dto2 = rd.EngDetailBitsList(dto.EngId);
    var dto3 = rd.EngDetailDateTimesList(dto.EngId);
    var dto4 = rd.EngDetailVarCharsList(dto.EngId);
    var dto5 = rd.EngDetailVarCharMaxesList(dto.EngId);

    ObjectSetter(new object() /* test only */, dto2, "DataValue");

    private void ObjectSetter(object dto, string dtoProp, 
        IEnumerable items, string itemProperty)
    {
        foreach (var item in items)
        {
            var propertyInfo = item.GetType().GetProperty(dtoProp,
                BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);
            var itemValue = dto.GetType().GetProperty(itemProperty,
                BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);

            if (propertyInfo != null)
            {
                propertyInfo.SetValue(item, itemValue.GetValue(dto));
            }
        }
    }

      

you can improve performance by doing asynchronous processing. call it like this

        Task.Factory.StartNew(delegate()
        {
            ObjectSetter(new object() /* test only */, dto2, "DataValue");
        });

        Task.Factory.StartNew(delegate()
        {
            ObjectSetter(new object() /* test only */, dto3, "DataValue");
        });

        Task.Factory.StartNew(delegate()
        {
            ObjectSetter(new object() /* test only */, dto4, "DataValue");
        });

        Task.Factory.StartNew(delegate()
        {
            ObjectSetter(new object() /* test only */, dto5, "DataValue");
        });

      

-1


source







All Articles