Как отобразить вложенный объект json в POCO с несколькими пользовательскими классами, используя AutoMapper? - PullRequest
0 голосов
/ 23 апреля 2020

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

Вот объект JSON, который я отправляю своему API:

{
    "userName": "Test_06",
    "password": "@00a00B00c",
    "firstName": "Test",
    "lastName": "Test",
    "address": {
        "houseNumber": 1,
        "appartementBus": "A bus 34",
        "street": "Teststreet",
        "ZIP": "0000",
        "country": "Test"
    }
}

И я пытаюсь отобразить объект адреса в POCO адреса:

using System.Collections.Generic;

namespace Kubex.Models
{

  public class Address
  {
      public int Id { get; set; }
      public int HouseNumber { get; set; }
      public string AppartementBus { get; set; }

      public int StreetId { get; set; }
      public virtual Street Street { get; set; }
      public int ZIPId { get; set; }
      public virtual ZIP ZIP { get; set; }
      public int CountryId { get; set; }
      public virtual Country Country { get; set; }

      public virtual ICollection<User> Users { get; set; }
      public virtual ICollection<Company> Companies { get; set; }
      public virtual ICollection<Post> Posts { get; set; }
  }
}

Следующие типы в основном совпадают, пример Country выглядит как

using System.Collections.Generic;

    namespace Kubex.Models
    {
        public class Country
        {
            public byte Id { get; set; }
            public string Name { get; set; }

            public virtual ICollection<Address> Addresses { get; set; }
        }
    }

Ошибка I Я получаю от Почтальона следующее:

{
    "errors": {
        "address.ZIP": [
            "Error converting value \"0000\" to type 'Kubex.Models.ZIP'. Path 'address.ZIP', line 10, position 18."
        ],
        "address.street": [
            "Error converting value \"Teststreet\" to type 'Kubex.Models.Street'. Path 'address.street', line 9, position 27."
        ],
        "address.country": [
            "Error converting value \"Test\" to type 'Kubex.Models.Country'. Path 'address.country', line 11, position 22."
        ]
    },
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "|cca8afa7-463e762ee0ed670d."
}

Я пытался создать сопоставления для каждого Типа, от строки до Типа. но я продолжаю получать ошибки. Выглядит так: также пробовал ConstructUsing.

    CreateMap<string, Country>()
        .ConvertUsing(s => new Country { Name = s });

    CreateMap<string, ZIP>()
        .ConvertUsing(s => new ZIP { Code = s });

    CreateMap<string, Street>()
        .ConvertUsing(s => new Street { Name = s });

Нужно ли создавать карту для Address тоже? Если да, то как мне это сделать, потому что я не знаю, как он видит объект и как мне создать карту для него.

Заранее большое спасибо.

РЕДАКТИРОВАТЬ:

Это часть DTO. Я отправляю конечную точку API для регистрации пользователя. Используется следующее сопоставление:

CreateMap<UserRegisterDTO, User>();

И вот как DTO выглядит так:

using Kubex.Models;

namespace Kubex.DTO
{
    public class UserRegisterDTO
    {
        public string UserName { get; set; }
        public string Password { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public Address Address { get; set; }
    }
}

Вот как выглядит класс User:

using System.Collections.Generic;
using Microsoft.AspNetCore.Identity;

namespace Kubex.Models
{
    public class User : IdentityUser
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string EmployeeNumber { get; set; }

        public int? AddressId { get; set; }
        public virtual Address Address { get; set; }

        public virtual ICollection<License> Licenses { get; set; }
        public virtual ICollection<Contact> Contacts { get; set; }
        public virtual ICollection<Post> Posts { get; set; }
    }
}

В моем сервисе я использую эту строку для сопоставления:

var newUser = _mapper.Map<User>(dto);

1 Ответ

0 голосов
/ 23 апреля 2020

Если я правильно понимаю, класс Address, используемый в UserRegisterDTO, такой же, как и в User. Не говоря уже о том, что это напрямую проблема, но с точки зрения архитектуры, поскольку вы смешиваете свой DTO с моделью базы данных (я полагаю), DTO должен ссылаться только на простые типы или другие DTO.

Следовательно, я предпочел бы что-то вроде этого:

public class UserRegisterDTO
{
   public string UserName { get; set; }
   public string Password { get; set; }
   public string FirstName { get; set; }
   public string LastName { get; set; }
   public AddressDTO Address { get; set; }
}

public class AddressDTO {
   public string HouseNumber { get;set; }
   public string AppartementBus { get;set; }
   public string Street { get;set; }
   public string ZIP { get;set; }
   public string Country { get;set; }
}

и, возможно, затем сопоставлять AddressDTO с адресом:

   CreateMap<AddressDTO, Address>()
     .ForMember(d => d.Country, d => d.MapFrom(s => new Country { Name = s.Country }))
  // cut for brevity

Но я все еще не нравится. Поскольку это отображение будет создавать экземпляр страны с каждым запросом и, в зависимости от ваших настроек, он может даже создать новую строку в базе данных. Может быть, лучше было бы поискать страну в рамках существующих?

Вы можете передать список экземпляров страны через контекст сопоставления и использовать его в ResolveUsing, но я лично предпочитаю, чтобы в сопоставлениях AutoMapper не было сложных логик c, поскольку это разбрасывает бизнес-логики c как минимум в двух местах. и усложняет юнит-тестирование.

Automapper отлично подходит для выполнения простых вещей, но если вы чувствуете, что он раздвинут до предела, на мой взгляд, лучше сделать хотя бы некоторые части сопоставления вручную.

...