开发者

Design Question About Converting POCOs From One Layer Into POCOs In Another Layer

开发者 https://www.devze.com 2023-01-07 00:31 出处:网络
I have a layer of business-level objects and a layer of contract-level objects in a WCF service application. The business layer objects I\'m referring to are just entity or POCO objects that I\'m usin

I have a layer of business-level objects and a layer of contract-level objects in a WCF service application. The business layer objects I'm referring to are just entity or POCO objects that I'm using to hold data. The contract-level objects I'm referring to are the objects that make up the WSDL my clients see (also POCOs).

When I return data from my WCF service to a client the the request parameters are hydrated from one or more of my contract layer objects into XML and forwarded on.

I'm trying to create a class that sits outside of the contract and business layers that will translate objects from one layer to another. So, for instance, in the contract layer I would have a class like:

public cl开发者_StackOverflow社区ass Person
{
    public string Name { get; set;};
}

I could also have an identical class (or it could be different) in the business layer whose property would be mapped to the property in this class.

I then have a class that executes code that looks like this:

public Contract.Person TranslatePerson(Business.Person person)
{
    Contract.Person result = new Contract.Person();
    result.Name = person.Name;
    return result;
}

This all works as expected. However, one of the requirements of this translator service is to insulate the business layer from changes in the contract layer and, one of the requirements of this layer is to allow different versions of the contract layer to exist simultaneously to support backwards compatibility for SOAP clients. For instance if in v2 of the service I want to add last name to the person class and change Name to FirstName so that my SOAP clients can now see both data points I will have something like this:

// remains for backwards compatibility for V1 clients
namespace Contract.V1
{
    public class Person
    {
        public string Name { get; set;};
    }
}

namespace Contract.V2
{
    public class Person
    {
        public string FirstName { get; set;};
        public string LastName { get; set;};
    }
}

Now when I need to send back a V2 Person to the client I want to map FirstName to FirstName from the business object and LastName to LastName. However, if I need to send back a V1 Person I will map FirstName to Name and just drop LastName.

The architecture I created for my translation layer is thusly:

public class V1Translator
{
    public virtual Contract.V1.Person TranslatePerson(Business.Person person)
    {
        Contract.V1.Person result = new Contract.V1.Person();
        result.Name = person.Name;
        return result;
    }
}

public class V2Translator : V1Translator
{
    public override Contract.V2.Person TranslatePerson(Business.Person person)
    {
        Contract.V2.Person result = new Contract.V2.Person();
        result.Name = person.Name;
        return result;
    }
}

This saves me a lot of time because I might have 100 different translation methods in V1Translator but I may only have to override 2 or 3 in V2Translator because there may be only a few objects that change in the different layers. I am also instantiating the appropriate Translator class using a factory. In this manner I can just call TranslatePerson on my special TranslationService class and have it figure out which Translator to use. This, however, is also where the problem comes in. I can't override the method in the base class because the return types are different. Even though they are both Contract Person objects, they are in different namespaces. I'm having difficulty getting passed this.

Can someone help me create an elegant solution to this problem?

Thanks


Check out AutoMapper, it's excellent at reducing the amount of manual mapping code you'll need to write in scenarios like this.


I wrote these 2 extension methods to accomplish this exact problem, they may give you a good starting point to solve your issue!

public static Y To<X, Y>(this X source) where X : IActiveRecord where Y: class
{
    try
    {
        Y target = Activator.CreateInstance(typeof(Y)) as Y;

        BindingFlags memberAccess = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly | BindingFlags.SetProperty;

        PropertyInfo[] targetProperties = target.GetType().GetProperties(memberAccess);
        foreach (MemberInfo Field in targetProperties)
        {
            string name = Field.Name;

            if (Field.MemberType == MemberTypes.Property)
            {
                PropertyInfo targetProperty = Field as PropertyInfo;
                PropertyInfo sourceProperty = source.GetType().GetProperty(name, memberAccess);

                if (sourceProperty == null) { continue; }

                if (targetProperty.CanWrite && sourceProperty.CanRead)
                {
                    object targetValue = targetProperty.GetValue(target, null);
                    object sourceValue = sourceProperty.GetValue(source, null);

                    if (sourceValue == null) { continue; }

                    if (targetProperty.PropertyType.FullName == sourceProperty.PropertyType.FullName)
                    {
                        object tempSourceValue = sourceProperty.GetValue(source, null);
                        targetProperty.SetValue(target, tempSourceValue, null);
                    }
                }
            }
        }

        return target;
    }
    // it's important to return null if there are any errors.
    catch { return null; }
}


public static IList<Y> To<X, Y>(this BindingListEx<X> collection) where X : IActiveRecord where Y : class
{
    IList<Y> returnList = new List<Y>();

    foreach (X item in collection)
        returnList.Add(item.To<X,Y>());

    return returnList;
}

I use this to convert subsonic 2 entities (hence the IActiveRecord constraint) to my own POCO's

0

精彩评论

暂无评论...
验证码 换一张
取 消