При использовании DTO Automapper и Nhibernate отражают изменения в дочерних коллекциях DTO в обновляемом объекте домена. - PullRequest
3 голосов
/ 16 марта 2012

Я не очень хорошо знаком с этим дизайном, но я надеюсь получить некоторое руководство.

У меня есть серверная служба, которая отправляет DTO интеллектуальному клиенту WPF. На интеллектуальном клиенте WPF пользователь будет изменять, удалять и изменять элементы, а затем изменения отправляются обратно (клиент -> сервер). Например, в настоящее время я работаю над формой сведений о клиенте, и у пользователя есть возможность добавлять, удалять и изменять категории, принадлежащие клиенту, в сетке данных. Когда DTO отправляется обратно на сервер, я хочу загрузить объект домена, связанный с идентификатором в DTO, и применить изменения, внесенные в DTO, к объекту домена, включая все дочерние коллекции.

Я предпринял попытку сделать что-то подобное в приведенном ниже коде с помощью метода UpdateCustomer . Тем не менее, я думаю, что я далеко от цели. Когда код запускается вместо того, чтобы заканчиваться списком {Частное лицо, Компания, НПО, Правительство} Я получаю список {Частное лицо, B2B, Компания, НПО, Правительство} поскольку он явно не удалил запись B2B из исходного списка.

Один вариант, который мне пришёл в голову, - это циклически просматривать коллекцию DTO и сравнивать ее с коллекцией из объекта домена и добавлять, удалять и обновлять в зависимости от того, что было изменено. Однако это казалось действительно громоздким.

Что мне нужно сделать, чтобы применить изменения из DTO к дочерним коллекциям в моем объекте domiain?

Большое спасибо за любую помощь, она будет высоко оценена

Alex

    public class Customer
        {
            public virtual int Id { get; set; }
            public virtual IList<Category> Categories { get; private set; }
            public virtual string Code { get; set; }
            public virtual string Description { get; set; }

            public Customer()
            {
                Categories = new List<Category>();
            }

            public virtual void AddCategory(string categoryName)
            {
                Categories.Add(new Category(categoryName));
            }
        }

        public class Category
        {

            public virtual string CategoryName { get; private set; }
            public virtual Customer Customer {get;set;}
            public virtual int Id { get; set; }

            protected Category(){}

            public Category(string name)
            {
                CategoryName = name;
            }
        }
    }

    public void SetUpAutoMapper()
    {
        Mapper.CreateMap<Category, CategoryDto>();
        Mapper.CreateMap<Customer, CustomerDto>();
        Mapper.CreateMap<CategoryDto, Category>();
        Mapper.CreateMap<CustomerDto, Customer>();
        Mapper.AssertConfigurationIsValid();
    }
       public void SaveCustomer()
        {
            var customer = new Customer{Code="TESTCUST",Description="TEST CUSTOMER"};
             customer.AddCategory("Individual");
             customer.AddCategory("B2B");
             customer.AddCategory("Healthcare");
             customer.AddCategory("NGO");
             repository.Save(customer);
        }

     public CustomerDto GetCustomer(int customerId)
     { 
        var customer = repository.GetCustomer(customerId);
        var customerDto = Mapper.Map<Customer,CustomerDto>(customer);
        return customerDto;
     }

    public void UpateCustomer(CustomerDto customerToUpdate)
    {
        /*imagine that the dto incoming has had the following operations performed on it
        -----add new category----
        customerToUpdate.Categories.Add(new CategoryDto {CategoryName = "Government"});
        ---update existing category---
        customerToUpdate.Categories[2].CategoryName = "Company";
        ---remove category---
        customerToUpdate.Categories.RemoveAt(1);*/

        var customer = repository.GetCustomer(customerToUpdate.Id);
        /* How in this bit do I ensure that the child collection changes are
        propogated into the underlying customer object retrieved from the database*/
        var customer = Mapper.Map<CustomerDto,Customer>(customerToUpdate);
        repository.Save(customer);
    }

public class CustomerDto
    {
        public int Id { get; set; }
        public string Code { get; set; }
        public string Description { get; set; }
        public List<CategoryDto> Categories { get; set; }
    }

    public class CategoryDto
    {
        public int Id { get; set; }
        public string CategoryName { get; set; }
    }

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
      <class name="Customer" table="Customer">
        <id name="Id" column="CustomerId">
          <generator class="native"/>
        </id>
        <property name="Code" />
        <property name="Description" />
        <bag name="Categories" table="Categories" cascade="all" inverse="false">
          <key column="FK_CustomerID" />
          <one-to-many class="Category"/>
        </bag>
      </class>

      <class name="Category" table="Categories">
        <id name="Id" column="CategoryId">
          <generator class="native"/>
        </id>
        <many-to-one name="Customer" column="FK_CustomerId" not-null="true" class="Customer"></many-to-one>
        <property name="CategoryName" />
      </class>
    </hibernate-mapping>

Ответы [ 2 ]

5 голосов
/ 25 марта 2012

Я недавно сделал нечто подобное, но с EF в качестве datatier.Я не знаю, чтобы nhibernate знал, будет ли работать тот же подход.

Основные шаги:

  • Убедитесь, что целевая коллекция загружена из БД и присоединена к графу объектов для отслеживания изменений.
  • .ForMember(dest => dest.Categories, opt => opt.UseDestinationValue())
  • Затем создайте пользовательский IObjectMapper для отображения IList <> в IList , где T: Entity
  • Пользовательский преобразователь IObject использовал некоторый кодиз http://groups.google.com/group/automapper-users/browse_thread/thread/8c7896fbc3f72514

    foreach (var child in source.ChildCollection)
    { 
        var targetChild = target.ChildCollection.SingleOrDefault(c => c.Equals(child)); // overwrite Equals or replace comparison with an Id comparison
        if (targetChild == null)
        { 
            target.ChildCollection.Add(Mapper.Map<SourceChildType, TargetChildType>(child));
        } 
        else
        { 
            Mapper.Map(child, targetChild);
        } 
    } 
    
  • Наконец, последний кусок логики для проверки всех идентификаторов в targetCollection существует в sourceCollection и удаляет их, если их нет.

В конце не так уж много кода, и его можно использовать в других действиях.

1 голос
/ 19 марта 2012
Mapper.CreateMap<Customer, CustomerDto>()
    .ForMember(dest => dest.Categories, opt => opt.MapFrom(src =>src.Categories));

или

Mapper.CreateMap<IList<Category>, IList<CategoryDto>>();

что-то вроде этого, чтобы сказать autopper также отобразить список.

...