0

I am trying to convert a class to another class using the Mapper class, It works for the non-user-defined types when it comes to user-defined types, It's showing an Exception.

Unhandled Exception: System.ArgumentException: Object of type 'DataModal.TblBook' cannot be converted to type 'DomainModal.Book'. at System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast) at System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig) at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)

I have tried Creating a new Generic Method during runtime using this answer How do I use reflection to call a generic method?

in TblStudent.cs the type is of TblBook and in Student.cs the type is of Book I am unable to convert it.

But I am failing to convert it.

Here is my Mapper.cs

using DomainModal;
using DataModal;
using System.Linq;
using System;
using System.Reflection;

namespace DataAccessLogic
{
    public class Mapper<T, U> where U : new()
    {
        public U Convert(T input)
        {
            U newCastType = new U();
            var fromObjectProperties = input.GetType().GetProperties();
            var toObjectProperties = newCastType.GetType().GetProperties();

            foreach (var parentProperty in fromObjectProperties)
            {

                foreach (var childProperty in toObjectProperties)
                {
                    if (parentProperty.Name == childProperty.Name)
                    {
                        childProperty.SetValue(newCastType, parentProperty.GetValue(input));
                    }
                }
            }
            /*var fromObjectProperties = input.GetType().GetProperties();
            foreach (var parentProperty in fromObjectProperties)
            {

            }*/
            return newCastType;

        }

    }
}

TblStudent.cs

using System;

namespace DataModal
{
    public class TblStudent
    {

        public string EmailId { get; set; }
        public string Password { get; set; }
        public string StudentName { get; set; }
        public string StudentId { get; set; }
        public string PhoneNumber { get; set; }
        public TblBook GetBook { get; set; }

    }
}

Student.cs

using System;

namespace DomainModal
{
    public class Student
    {

        public string EmailId { get; set; }
        public string Password { get; set; }
        public string StudentName { get; set; }
        public string StudentId { get; set; }
        public string PhoneNumber { get; set; }
        public Book GetBook { get; set; }

    }
}

Full Source Code here: https://gitlab.com/chintuyadavsara/console-application

I don't know Where I am doing wrong. Any help will be appreciated

Thank you.

chintuyadavsara
  • 1,509
  • 1
  • 12
  • 23

2 Answers2

2

Example by using Reflection to recursively call the generic method on the properties which are not of the same Type (but have the same name):

public class Mapper
{
    public static TRes Convert<TIn, TRes>(TIn obj)
    {
        TRes targetInstance = Activator.CreateInstance<TRes>();
        var sourceTypePropertyInfos = obj.GetType().GetProperties();
        var targetTypePropertyInfos = targetInstance.GetType().GetProperties();

        foreach (var sourceTypePropertyInfo in sourceTypePropertyInfos)
        {
            foreach (var targetTypePropertyInfo in targetTypePropertyInfos)
            {
                if (sourceTypePropertyInfo.Name == targetTypePropertyInfo.Name)
                {
                    if (sourceTypePropertyInfo.PropertyType == targetTypePropertyInfo.PropertyType)
                    {
                        targetTypePropertyInfo.SetValue(targetInstance, sourceTypePropertyInfo.GetValue(obj));
                    }
                    else
                    {
                        var sourcePropertyValue = sourceTypePropertyInfo.GetValue(obj);
                        var methodInfo = typeof(Mapper).GetMethod(nameof(Mapper.Convert));
                        var genericMethodInfo = methodInfo.MakeGenericMethod(sourceTypePropertyInfo.PropertyType, targetTypePropertyInfo.PropertyType);
                        var targetValue = genericMethodInfo.Invoke(new Mapper(), new[] { sourcePropertyValue });
                        targetTypePropertyInfo.SetValue(targetInstance, targetValue);
                    }
                }
            }
        }
        return targetInstance;
    }
}

Call it as:

var student = Mapper.Convert<TblStudent, Student>(tblStudent);
kaffekopp
  • 2,551
  • 6
  • 13
  • Its Throwing an exception! `System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation. ---> System.MissingMethodException: No parameterless constructor defined for this object.` – chintuyadavsara Oct 24 '18 at 07:28
  • Where does it throw? I just ran it (using Book classes with just a test string property), worked for me. – kaffekopp Oct 24 '18 at 07:30
  • Copied the code into the Mapper class and changed the calling as you suggested!, Then the exception occured! :( – chintuyadavsara Oct 24 '18 at 07:33
  • I think in the `createInstance` method the exception – chintuyadavsara Oct 24 '18 at 07:38
  • So, do your classes have parameterless constructors? TblStudent and Student should work fine if the really look as the code in your question. How about the book classes? – kaffekopp Oct 24 '18 at 08:03
  • Ah yes, both your Book classes lack parameterless constructors. Add such as: public Book() {} and: public TblBook() {} ,then it will work. – kaffekopp Oct 24 '18 at 08:22
  • It ran without any exception but when i try to print the book details in App.cs file, it is getting an empty values. like `FactoryBusiness.GetStudentRepo().GetStudentDetails(username).GetBook.GetBookName()` – chintuyadavsara Oct 24 '18 at 08:32
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/182401/discussion-between-kaffekopp-and-chintuyadavsara). – kaffekopp Oct 24 '18 at 08:34
1

You can do something like this.

public class Mapper<T, U> where U : new()
    {
        public U Convert(T input)
        {
            U newCastType = new U();
            var fromObjectProperties = input.GetType().GetProperties();
            var toObjectProperties = newCastType.GetType().GetProperties();

            foreach (var parentProperty in fromObjectProperties)
            {

                foreach (var childProperty in toObjectProperties)
                {

                    if((parentProperty.Name == childProperty.Name) && parentProperty.PropertyType.IsClass && parentProperty.PropertyType.Name != "String")
                    {

                        var typeInfo = typeof(Mapper<,>);
                        var genericType = typeInfo.MakeGenericType(parentProperty.PropertyType, childProperty.PropertyType);

                        var genericMethodInfo = genericType.GetMethod("Convert");
                        var ojb = Activator.CreateInstance(genericType);
                        var targetValue = genericMethodInfo.Invoke(ojb, new[] { parentProperty.GetValue(input) });
                        childProperty.SetValue(newCastType, targetValue);
                    }
                    else if ((parentProperty.Name == childProperty.Name))
                    {
                        childProperty.SetValue(newCastType, parentProperty.GetValue(input));
                    }
                }
            }
            /*var fromObjectProperties = input.GetType().GetProperties();
            foreach (var parentProperty in fromObjectProperties)
            {

            }*/
            return newCastType;

        }

    }
dotnetstep
  • 17,065
  • 5
  • 54
  • 72
  • Exception `Unhandled Exception: System.ArgumentException: GenericArguments[1], 'DomainModal.Book', on 'DataAccessLogic.Mapper`2[T,U]' violates the constraint of type 'U'. ---> System.TypeLoadException: GenericArguments[1], 'DomainModal.Book', on 'DataAccessLogic.Mapper`2[T,U]' violates the constraint of type parameter 'U'.` – chintuyadavsara Oct 24 '18 at 08:19