关于将POCO从一层转换为另一层POCO的设计问题

时间:2010-07-14 22:56:37

标签: c# architecture

我在WCF服务应用程序中有一层业务级对象和一层契约级对象。我所指的业务层对象只是我用来保存数据的实体或POCO对象。我所指的契约级对象是构成我的客户看到的WSDL的对象(也是POCO)。

当我将数据从WCF服务返回到客户端时,请求参数从我的一个或多个合同层对象中水合成XML并转发。

我正在尝试创建一个位于合同和业务层之外的类,它将对象从一个层转换为另一个层。因此,例如,在合同层中,我将有一个类:

public class Person
{
    public string Name { get; set;};
}

我也可以在业务层中拥有一个相同的类(或者可能是不同的),其属性将映射到此类中的属性。

然后我有一个执行如下代码的类:

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

这一切都按预期工作。但是,此转换程序服务的要求之一是将业务层与合同层中的更改隔离开来,此层的一个要求是允许同时存在不同版本的合同层以支持SOAP客户端的向后兼容性。例如,如果在服务的v2中,我想将姓氏添加到person类并将Name更改为FirstName,以便我的SOAP客户端现在可以看到两个数据点,我会有这样的内容:

// 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;};
    }
}

现在,当我需要将V2 Person发送回客户端时,我想将FirstName映射到业务对象的FirstName,将LastName映射到LastName。但是,如果我需要发回一个V1 Person,我会将FirstName映射到Name,然后删除LastName。

我为翻​​译层创建的架构因此是:

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;
    }
}

这节省了我很多时间,因为我可能在V1Translator中有100种不同的翻译方法,但我可能只需要在V2Translator中覆盖2或3,因为在不同的层中可能只有少数对象发生变化。我也在使用工厂实例化相应的Translator类。通过这种方式,我可以在我的特殊TranslationService类上调用TranslatePerson,并让它找出要使用的Translator。但是,这也是问题所在的地方。我无法覆盖基类中的方法,因为返回类型不同。尽管它们都是Contract Person对象,但它们位于不同的命名空间中。我很难通过这个。

有人可以帮助我为这个问题创造一个优雅的解决方案吗?

由于

2 个答案:

答案 0 :(得分:0)

查看AutoMapper,它非常适合减少您在此类情况下需要编写的手动映射代码的数量。

答案 1 :(得分:0)

我写了这两个扩展方法来完成这个确切的问题,它们可能会给你一个很好的起点来解决你的问题!

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;
}

我用它来将亚音速2实体(因此IActiveRecord约束)转换为我自己的POCO