4

I'm pretty sure this is impossible, but here goes..

I have a custom class in C# called Person which has a few properties such as Age, Height etc.

I then make a new class called Employee which inherits from Person but I don't yet add any other properties to Employee. So its basically just a Person still, except its called an Employee.

Now say I have an instance of Person called SomePerson. How can I create a new Employee instance that has all of the values it inherited from Person, set to those inside SomePerson. Like casting from a Person into an Employee.. But without me having to manually specify each and every property that needs to be set..

Something like..

Employee NewEmployee = (Employee)SomePerson;

But of course you get the error saying "Can't convert a Person into an Employee" etc.

Is AutoMapper the only practical solution to do things like this if you say had 300 properties in the involved objects??

UPDATE:

Auto-Mapper doesn't seem to handle my objects..

Employee SomeEmployee = EmployeeRepository.GetEmployee(SomeEmployeeID);

// Populate the ViewModel with the Person fetched from the db, ready for editing..
VMEmployee EmployeeToEdit = Mapper.Map<Employee, VMEmployee>(SomeEmployee);


// ViewModel based on Employee with Validation applied..
[MetadataType(typeof(Employee_Validation))]
public class VMEmployee : Employee
{
    // Absolutely nothing here
}

where "Employee" is auto-generated by LINQ to SQL..

6
  • Soulution you are looking for is called multiple inheritance in C# which doesn't exist. Commented Jan 8, 2011 at 11:18
  • I would remove the inheritance of VMEmployee : Employee. You need to define your view model with a subset of properties from your Employee domain model. Commented Jan 8, 2011 at 12:10
  • Why am I using DataAnnotations again? If I'm manually re-specifying every property 4 times this becomes ridiculous. Once in each of the two ViewModels and once in each of the two validation classes. That is totally ridiculous. Just to have validation on two different situations. I might as well write the validation by hand the old school way. There has to be a smarter way.. Otherwise DataAnnotations is useless. Commented Jan 8, 2011 at 12:13
  • You don't need to use separate buddy classes with your view model. Just apply your DataAnnotations to your view model itself. Buddy classes are intended for when you are using your domain objects directly in your view and you don't have control over the properties generated for you, hence implementing a buddy class to handle the validation. Your view model is a separate class completely with its own properties. Redefine your view model. Commented Jan 8, 2011 at 12:37
  • Fantastic, thanks Matthew. That has taught me something valuable and now seems obvious! That reduces duplication nicely, but one further question/issue.. This still leaves me with needing AutoMapper to set the ViewModel values after retrieving the object from the database before editing?? Commented Jan 8, 2011 at 21:45

6 Answers 6

4

AutoMapper is a good solution in this case. If you're not going to use a property mapping framework, and you're not willing to create a copy constructor public Employee(Person person), or an implicit/explicit conversion, how else do you expect to copy the properties across. Realistically you could

1.Reflection

public void Map<TSource, TDestination>(TSource source, TDestination destination)
{
  var props = typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance);
  var type = typeof(TDestination);

  foreach (var prop in props)
  {
    object value = prop.GetValue(source, null);

    var prop2 = type.GetProperty(prop.Name);
    if (prop2 == null)
      continue;

    if (prop.PropertyType != prop2.PropertyType)
      continue;

    prop2.SetValue(destination, value, null);
  }
}

2.Copy Constructor

public Employee(Person person)
{
  // Copy properties
}

3.Implicit/Explicit Conversion

public static implicit operator Employee(Person person)
{
  // Build instance and return
}

4.AutoMapper

Mapper.Map<Person, Employee>(person);

5.Combination of 3/4:

public static implicit operator Employee(Person person)
{
  return Mapper.Map<Person, Employee>(person);
}

A note on implicit/explicit conversion operators: I believe in using these you won't be generating CLS-compliant code.

As @Mitch Wheat has already said, if you have an object with over 300 properties, I would reconsider what that object actually represents. Refactor refactor refactor.

Sign up to request clarification or add additional context in comments.

6 Comments

Ok AutoMapper it is. Does it work with child complex objects? Many thanks.
Yes, AutoMapper supports projection, so when you are wiring up your map, Mapper.CreateMap<Person, Employee>().ForMember(e => e.ComplexProperty, o => o.MapFrom(p => new ComplexProperty(p.Name))); You should have a look at the examples (automapper.codeplex.com)
Trying to map MyProject.Data.Employee to MyProject.ViewModels.VMEmployee. Exception of type 'AutoMapper.AutoMapperMappingException' was thrown.
Thanks I will check the examples. But it looks like I will manually have to specify each complex property that needs mapping?
"Missing type map configuration or unsupported mapping". Is this because my complex object is auto-generated by LINQ to SQL!??!?!?!?!??!
|
1

You can use automaper for this purpose, without any configuration.

here's an example:

Employee employee = Mapper.Map<Person, Employee>(person);

4 Comments

So far I am liking this the most as it (a) doesn't involve hardcore hacks such as Reflection, (b) involves a single line of code and (c) involves no configuration.. My next question is why isn't something like this built into .Net in some way. The need is growing particularly with ASP.Net MVC.
How many DLLs do you have to add to your Bin folder to use AutoMapper? I'm no fan of unnecessary bloat.
Exactly my own school of thought. As a general rule, the more add-ons, the less reliability and higher the future maintenance. I am very anti add-ons in general. You come back in 3 years time when ASP.Net MVC10 is out and suddenly your solution doesn't build because you're running AutoMapper 1.01. That is the problem with add-ons and I have learnt this from experience with working on some mammoth solutions with way too many add-ons. Anyway, I'm desperate and investigating AutoMapper. The author seems smart, its just a single DLL and very low bloat.
This exact code doesn't work :( Trying to map MyProject.Data.Employee to MyProject.ViewModels.VMEmployee. Exception of type 'AutoMapper.AutoMapperMappingException' was thrown. Missing type map configuration or unsupported mapping
0

Create a constructor on Employee that takes a Person instance. Of course you'll have to fill in all the properties (maybe Resharper can help?)

8 Comments

Yeah ok, but I want to avoid manually setting the values of each and every property one by one as I have hundreds of them.
@Aaron: 1. Reflection (ugh!), 2. ReSharper (maybe), 3. A class with 300 properties? Drawing Board....
Ok there aren't 300 of them, but they are complex LINQ to SQL Model classes. And they contain child complex classes as well. E.g. Address (which in itself might have StreetName, StreetNumber and so on etc..)
Thanks Mitch. I think I know the answer already. IMPOSSIBLE. In that case, ASP.Net MVC's Model Validation needs some much needed further improvements.
@Aaron: are you placing your custom validation attributes on Person's properties?
|
0

If you have relatively simple properties, reflection will probably be the quickest and easiest solution to map property values.

Contrary to popular belief, reflection really isn't that bad; Rick Strahl dispels this myth in this article: http://www.west-wind.com/weblog/posts/351.aspx.

Create the following constructor for Employee:

public Employee(Person person)
{
    // clone property values
    foreach (var property in person.GetType().GetProperties().Where(property => property.CanRead && property.CanWrite))
    {
        property.SetValue(this, property.GetValue(user, null), null);
    }
}

Now simply instantiate an Employee object like so:

Employee NewEmployee = new Employee(SomePerson);

1 Comment

Thanks brilly. I like your thinking of sticking with native MS issue code that has a better future life likelihood, but in my case, I am dealing with complex objects with child complex objects, all auto-generated by LINQ to SQL. So I think this is where AutoMapper has a reputation for shining more..
0

"they are complex LINQ to SQL Model classes" -- Aaron

If you have a table that maps to one of several possible classes, you can use LINQ to SQL's Inheritance Hierarchies. It means that if you use the same table for several types of conceptual entities, you can have LINQ to SQL automatically create the appropriate class. Each class can have different properties which can be a subset of the columns in the table.

There are some limitations. You can use only one table per inheritance hierarchy and you must have a discriminator column that tells LINQ to SQL which class should be used.

Comments

-2

When you start a new job, is a new person born who is not you, and the old you killed?

If your design is based on that scenario, as it appears, then the practice in your organisation is very strange.

Instead, a person has an employment relationship with an employer.

Don't use inheritance to model roles; allow the same person to have roles which represent their relationships with other objects.

3 Comments

Yeah totally agree, but I am just making up the Person/Employee example to make a very peculiar situation easy to communicate..
@Aaron Then use an example which actually matches the problem, otherwise it's not a real question
Ok Pete, here's the full question. See what you can do: stackoverflow.com/questions/4636707/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.