我在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对象,但它们位于不同的命名空间中。我很难通过这个。
有人可以帮助我为这个问题创造一个优雅的解决方案吗?
由于
答案 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