Вопрос проектирования о преобразовании POCO из одного слоя в POCO в другой слой - PullRequest
3 голосов
/ 15 июля 2010

У меня есть слой объектов бизнес-уровня и слой объектов уровня контракта в приложении-службе 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 службы я хочу добавить фамилию к классу персонажа и изменить имя на 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, я сопоставлю 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;
    }
}

Это экономитУ меня много времени, потому что у меня может быть 100 различных методов перевода в V1Translator, но мне, возможно, придется переопределить только 2 или 3 в V2Translator, потому что может быть только несколько объектов, которые изменяются в разных слоях.Я также создаю экземпляр соответствующего класса Translator, используя фабрику.Таким образом, я могу просто вызвать TranslatePerson для моего специального класса TranslationService и заставить его выяснить, какой Translator использовать.Однако здесь также возникает проблема. Я не могу переопределить метод в базовом классе, потому что возвращаемые типы отличаются.Несмотря на то, что они оба являются объектами Contract Person, они находятся в разных пространствах имен.Мне трудно это пройти.

Может ли кто-нибудь помочь мне найти элегантное решение этой проблемы?

Спасибо

Ответы [ 2 ]

0 голосов
/ 08 марта 2011

Я написал эти 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

0 голосов
/ 15 июля 2010

Проверьте AutoMapper , он отлично подходит для уменьшения количества кода ручного отображения, который вам нужно написать в подобных сценариях.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...