简体   繁体   中英

C# - how to loop through each property & value of an object

I have below DTO class in C#

public class Customer
{
   public int Id { get; set;}

   public string Name { get;set;}

   // & so on (say 25 properties)
}

I am using DynmoDb as Db(NoSql), to add the above item to DynamoDb table. This is how my method looks like

 public async Task<bool> Add(Customer customer)
    {
        try
        {
            var res = await _dynamoClient.PutItemAsync(
            tableName: _tableName, item: SetObject(customer)).ConfigureAwait(false);
            return res.HttpStatusCode == System.Net.HttpStatusCode.OK
        }
        catch (Exception ex)
        {
            Logger.Log(ex, eventObj);
            return false;
        }
    }

Private SetObject() method:-

private Dictionary<string, AttributeValue> SetObject(Customer obj)
    {
        //DynamoDb - Using Low Level API
        var attributes = new Dictionary<string,
        AttributeValue> {
            //Id
            {
                nameof(obj.Id),
                new AttributeValue {
                    S = obj.Id
                }
            },
            //Name
            {
                nameof(obj.Name),
                new AttributeValue {
                    S = obj.Name.Trim()
                }
            },
        };
        return attributes;
    }

This private method looks cumbersome & error prone to me. I don't want to look up through each property one by one in this way.

I am thinking if there is some way to loop through all the properties & assign name & value to Dictionary collection

 var propsList = typeof(EventTO).GetProperties().Select(x => x.Name).ToList<string>();

This gives me all the property names but how do I fetch the value & key from the passed object of type customer.

Thanks!

You can use reflection, with something like this, also with Generics for better type safety

using System;
using System.Reflection;

class TestClass
{
    public int publicIntField;
    public string publicStringField;
    public int publicIntProp {get; set;}
    public string publicStringProp {get; set;}
    public static int staticInt;
}

public class Program
{
    public static void Main()
    {
        var src = new TestClass();
        src.publicIntField = 7;
        src.publicIntProp = 12;
        src.publicStringField = "foo";
        src.publicStringProp = "baz";

        var dest = new TestClass();
        DoIt(src, dest);
        TestClass dest1 = DoItWithGenerics(src);
    }

    public static void  DoIt(object src, object dest)
    {
        Console.WriteLine("DoIt");
        Type t = src.GetType();
        // TODO  check the 2 objects have same type

        foreach (PropertyInfo  p in t.GetProperties(BindingFlags.Public|BindingFlags.Instance))
        {
            Console.WriteLine(p.Name);
            p.SetValue(dest, p.GetValue(src));
        }

        foreach (FieldInfo  fi in t.GetFields(BindingFlags.Public|BindingFlags.Instance))
        {
            Console.WriteLine(fi.Name);
            fi.SetValue(dest, fi.GetValue(src));
        }
        Console.WriteLine("*****");
    }

    public static T DoItWithGenerics<T>(T src) where T:new() // only works for types with a default ctor
    {
        Console.WriteLine("DoItWithGenerics");

        Type t = typeof(T);
        T dest = new T();

        foreach (PropertyInfo  p in t.GetProperties(BindingFlags.Public|BindingFlags.Instance))
        {
            Console.WriteLine(p.Name);
            p.SetValue(dest, p.GetValue(src));
        }

        foreach (FieldInfo  fi in t.GetFields(BindingFlags.Public|BindingFlags.Instance))
        {
            Console.WriteLine(fi.Name);
            fi.SetValue(dest, fi.GetValue(src));
        }
        Console.WriteLine("*****");
        return dest;
    }
}

See at work in this fiddle

This is a way to Clone every type of object

It requires some modification to handle the "Trim()" part you are using.

You would be better off using a library like https://github.com/ServiceStack/PocoDynamo

This does all the mapping job, of converting to the required dynamo request for you, so you don't have to worry about it.

For Javascript, AWS has documentClient library which makes the task easier. For .net we use the above library.

//Configuration for client
var awsDb = new AmazonDynamoDBClient("keyId","key",
new AmazonDynamoDBConfig { ServiceURL="http://localhost:8000"});
var db = new PocoDynamo(awsDb);

//Create c# dto
var itemToSave = new Item();
itemToSave.Id = 1;
itemToSave.Name = "Test";

//Single line to save it
db.PutItem(itemToSave);

Check this out: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/reflection . It might help you to simplify your project, reduce redundancy and increase performance, as well as return you the value of the variable. I haven't used it myself yet but my coworker is a big fan of it.

I'm not sure up to which extend it will help you tho and i can't ask my coworker to check on this one since he is on vacation

Using more Reflection!

So you have all the names of every property on a Type. Now you need an instance of that type to get the Values of the properties on. Here is how you can do that:

// Assume that 'event' is an instance of EventTO
foreach (var propertyName in propsList)
{
    object value = event.GetType().GetProperty(propertyName).GetValue(event, null);
    // Do whatever with 'value'
}

As you can see we have some redundant code (GetType and GetProperty), so instead of storing our properties as a string of their names in a list, we can store the PropertyInfo in a and use it like so:

var propsList = typeof(EventTO).GetProperties();
foreach (var property in propsList)
{
    object value = property.GetValue(event, null);
}

Hint: PropertyInfo has a Property called PropertyType which returns the Type of the property

I'd use a library like FastMember to iterate over properties in an object and convert it to an AttributeValue dictionary (See: https://github.com/mgravell/fast-member )

Reflection is in general quite slow compared to an optimized library like FastMember.

 public class Dto
{
    public string Name { get; set; }
    public string Address { get; set; }
}

static void Convert()
{
    var dto = new Dto { Name = "Name", Address = "Address" };

    var ta = TypeAccessor.Create(typeof(Dto));
    var oa = ObjectAccessor.Create(dto);
    var values = new Dictionary<string, AttributeValue>();

    foreach (var m in ta.GetMembers())
    {
        values.Add(m.Name, new AttributeValue { S = oa[m.Name].ToString() });
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM