【问题标题】:Design Question About Converting POCOs From One Layer Into POCOs In Another Layer关于将一层的 POCO 转换为另一层的 POCO 的设计问题
【发布时间】:2011-03-16 03:18:12
【问题描述】:

我在 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 人员发送回客户端时,我想将业务对象中的 FirstName 映射到 FirstName,并将 LastName 映射到 LastName。但是,如果我需要发回 V1 人员,我会将 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 对象,但它们位于不同的命名空间中。我很难通过这个。

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

谢谢

【问题讨论】:

    标签: c# architecture


    【解决方案1】:

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

    【讨论】:

    • 要是我昨天看到了就好了:(
    【解决方案2】:

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

    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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-22
      • 1970-01-01
      • 2013-05-09
      • 2010-09-30
      • 2011-05-23
      • 2011-07-05
      • 1970-01-01
      • 2011-05-07
      相关资源
      最近更新 更多