11

I want to know how to map fields of two different objects and assign the values to it.

Eample:

public class employee
{
    public int ID { get; set; }
    public string Name { get; set; }
}

public class manager
{
    public int MgrId { get; set; }
    public string MgrName { get; set; }
}

Now I have a List object. I want to assign the values to "manager" class. Any automatic way to do that. I can do it explicitly and assigning values to it. But my object is very huge thats the problem. I dont want to use any third party tools too.

Note: It can't have any prefix for manager. It can be anything. (Ex: mgrId can be like mgrCode)

Murugavel
  • 269
  • 1
  • 2
  • 9

3 Answers3

17

You could use reflection for it, even by ignoring the property casing (notice the employee.ID vs. manager.MgrId):

class Program
{
    static void Main(string[] args)
    {
        var employee = new Employee() { ID = 1, Name = "John" };
        var manager = new Manager();
        foreach (PropertyInfo propertyInfo in typeof(Employee).GetProperties())
        {
            typeof(Manager)
                .GetProperty("Mgr" + propertyInfo.Name,
                    BindingFlags.IgnoreCase |
                    BindingFlags.Instance |
                    BindingFlags.Public)
                .SetValue(manager,
                    propertyInfo.GetValue(employee));
        }
    }
}

public class Employee
{
    public int ID { get; set; }
    public string Name { get; set; }
}

public class Manager
{
    public int MgrId { get; set; }
    public string MgrName { get; set; }
}

If you don't know the Mgr prefix, you could only match by suffixes:

foreach (PropertyInfo propertyInfo in typeof(Employee).GetProperties())
{
    typeof(Manager).GetMembers()
        .OfType<PropertyInfo>()
        .FirstOrDefault(p => p.Name.EndsWith(propertyInfo.Name, 
            StringComparison.CurrentCultureIgnoreCase))
        .SetValue(manager,
            propertyInfo.GetValue(employee));
}

And a very narrow and impractical assumption: mapping based on the property order (if you are expecting the 2 types to have properties defined in the same sequence and number, the only difference being the property names). I wouldn't recommend anyone using it in real life, but still, here it is (just to make it more fragile :) ):

typeof(Employee)
    .GetProperties()
    .Select((p, index) =>
        new { Index = index, PropertyInfo = p })
    .ToList()
    .ForEach(p =>
        {
            typeof(Manager)
                .GetProperties()
                .Skip(p.Index)
                .FirstOrDefault()
                .SetValue(manager,
                    p.PropertyInfo.GetValue(employee));
        });
Alex Filipovici
  • 31,789
  • 6
  • 54
  • 78
  • 1
    There's an overload of [`PropertyInfo.GetValue`](http://msdn.microsoft.com/en-us/library/hh194385.aspx) that takes the source object. There is also a two argument overload of [`PropertyInfo.SetValue`](http://msdn.microsoft.com/en-us/library/hh194291.aspx). – Dustin Kingen Jul 16 '13 at 11:49
  • Correct, starting with 4.5. I was compiling with 3.5 :) I'll update my answer. – Alex Filipovici Jul 16 '13 at 12:04
  • I guess I've been using .Net 4.5 a lot lately. – Dustin Kingen Jul 16 '13 at 12:05
  • @AlexFilipovici Do the properties have to be the same type, or is there a way to convert them? – Demodave Mar 18 '15 at 16:26
13

Use reflection or AutoMapper. I recommend the latter since writing new code is wasteful if it doesn't have a purpose.

public class Employee
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Manager
{
    public int MgrId { get; set; }
    public string MgrName { get; set; }
}

Mapper.Initialize(cfg =>
{
   cfg.RecognizeDestinationPrefixes("Mgr");
   cfg.CreateMap<Employee, Manager>();
});

var manager = Mapper.Map<Employee, Manager>(new Employee { Id = 1, Name = "Fred" });

Console.WriteLine("Id: {0}", manager.MgrId);
Console.WriteLine("Name: {0}", manager.MgrName);

If the properties don't have an idiomatic source identifier then use AutoMapper's projection.

Mapper.CreateMap<Employee, Manager>()
      .ForMember(dest => dest.MgrCode, opt => opt.MapFrom(src => src.ID))
      .ForMember(dest => dest.MgrName, opt => opt.MapFrom(src => src.Name))
Dustin Kingen
  • 20,677
  • 7
  • 52
  • 92
  • Does it ignore the property casing? – Alex Filipovici Jul 16 '13 at 11:49
  • Yes it will ignore property casing. – Dustin Kingen Jul 16 '13 at 11:50
  • But here i need to use automapper. Is there any way like serialization? – Murugavel Jul 16 '13 at 12:10
  • Serialization is completely different. There is reflection which the answer below demonstrated, but the code is cumbersome and fragile. Using third party code is the norm in the managed world since the code is tested and it works. Just use [NuGet](https://nuget.org/packages/AutoMapper/2.2.1) and install it. It's painless and simple. – Dustin Kingen Jul 16 '13 at 12:13
  • OK I will try to Change the properties in a meaningful way and i will use the reflection. That is the only way to do it. How the typed dataset mapping works? Do you have any idea on that? I am expecting such kind of way only. – Murugavel Jul 16 '13 at 12:16
  • That is probably better suited as a different question. Be sure to accept an answer if one of the solutions works for you. – Dustin Kingen Jul 16 '13 at 12:23
  • sure man thanks a lot. I will experiment it and will accept it. I will raise it as a different question. – Murugavel Jul 16 '13 at 12:24
  • @Romoku, does mapper ignore type? Or have a way to covert the type? – Demodave Mar 18 '15 at 16:25
1

I know this post is kind old now but for other people who are looking for answer is this what you can go for.. just simply use lambda expression

var managers = employees.Select(x => new Manager()
{
    MgrCode = x.ID,
    MgrName = x.Name
}).ToList();
B8ightY
  • 469
  • 5
  • 5
  • supposing `Manager()` has more than 2 properties, your solution would mean that the person has to manually type and assign all the properties, which is usually tedious. – Xander Selorm Jun 08 '21 at 17:16