Использование Automapper с внедрением зависимостей в сервисе для переноса из исходного проекта в целевой проект - PullRequest
0 голосов
/ 11 апреля 2020

Я впервые использую Automapper в базовом проекте. net, который использует внедрение зависимостей для передачи данных из старой унаследованной базы данных в новую архитектуру микросервисов, при этом каждый микросервис подключается к собственной базе данных (используя DDD принципы). К сожалению, невозможно перенести данные из sql в sql, и я предпочел бы создать приложение, которое извлекает данные из источника, а затем передает данные в новое приложение через совокупность root и модели.

У меня есть 3 проекта в моем решении:

  1. Основная точка входа, которая создает коллекцию сервисов с помощью DbContexts (исходный и целевой), добавляет целевой API, добавляет автоматический обработчик и создает / создает экземпляр передачи класс обслуживания
  2. Проект содержит исходный / унаследованный контекст БД, модели, а также профиль Automapper для создания карты.
  3. Сервисный проект, в котором существуют контексты sourceDB и targetDB и где выполняется Map.

Мне нужно добавить (я думаю) «Преобразование значений» для полей внешнего ключа и правильно перестроить структуры данных в новых базах данных. Моя проблема в том, добавить ли преобразование значения в профиль в пункте 2), но это не может получить доступ к целевому контексту в 3) Только когда служба запущена, существуют и исходный, и целевой контексты, и у меня есть доступ к обоим. Я посмотрел в документе «Прочитать документы для Automapper», но не могу найти способ добавить обработчик в проекте 2), который вызывается только в 3), когда сопоставление столбца имеет место.

ОБНОВЛЕНИЕ

  1. Код в Program.cs ->. net core 3.1 консольное приложение
    static void Main(string[] args)
        {
          var serviceCollection = new ServiceCollection();
          ServiceProvider serviceProvider;
          serviceCollection
           .AddDbContext<Source.Models.SourceDbContext>(options => options.UseSqlServer(config.GetConnectionString("Source")))
           .AddDbContext<Target.Models.ModelDbContext>(options => options.UseSqlServer(config.GetConnectionString("Target")))
           .AddScoped<ILoggerFactory, LoggerFactory>()
           .AddScoped<Source.Models.Services.IReadSourceService, Source.Models.Services.ReadSourceService>()
           .AddAutoMapper(typeof(Source.Models.Currency).Assembly);
          serviceProvider = serviceCollection.BuildServiceProvider();
    }
В исходном проекте модели базы данных
public class MappingProfile : Profile
  {
    public DataTakaMappingProfile(IReadSourceService service)
    {
      this.CreateMap<Source.Models.Instrument, Target.Models.Instruments.Instrument>()
        .ForMember(c => c.InstrumentId, opt => opt.Ignore())
        .ForMember(c => c.TrackingState, opt => opt.Ignore())
        .ForMember(c => c.CurrencyId, opt => opt.ConvertUsing(new CurrencyIdConverter(), c => c.CurrencyID));
    }
В сервисном проекте
  public class ReadSourceService : IReadSourceService
  {
    private readonly SourceDbContext context;
    private readonly Target.Models.ModelDbContext targetContext;
    private readonly ILogger<ReadSourceService> logger;
    private readonly IMapper mapper;

    /// <summary>
    /// Initializes a new instance of the <see cref="ReadSourceService"/> class.
    /// </summary>
    /// <param name="sourceContext">Source DB context </param>
    /// <param name="targetContext">Target DB context</param>
    /// <param name="logger">logger</param>
    /// <param name="mapper">mapper</param>
    public ReadSourceService(
      SourceDbContext context,
      Target.Models.ModelDbContext targetContext,
      ILogger<ReadSourceService> logger,
      IMapper mapper)
    {
      this.context = context;
      this.targetContext = targetContext;
      this.logger = logger;
      this.mapper = mapper;
    }

    /// <summary>
    /// Perform tha data comparison
    /// </summary>
    public void TransferInstrument()
    {

// perform the mapping here
    }
}

Здесь мне нужно преобразовать для источника CurrencyId в Source.Instruments в новый CurrencyId в Target.Instruments

ОБНОВЛЕНИЕ 2

Инструмент "AB C", имеющий ссылку на внешний ключ в валюте "USD", будет перенесен из источника в цель. Валюты на данном этапе уже переведены. CurrencyCode: "USD", скорее всего, будет иметь разные значения первичного ключа, например:

  • SourceDB -> CurrencyID = 23, CurrencyCode = "USD"
  • TargetDB -> CurrencyID = 45 , CurrencyCode = "USD"

Когда отображается запись инструмента для InstrumentCode: "AB C", мне нужно перехватить сопоставление в свойстве "CurrencyID" и изменить значение с 23 на 45.

Моя структура проекта выглядит следующим образом

- ProjectA                    - console app.
  └─ Program.cs
- ProjectB                    - source model classes
  ├─ Source                   - folder for data models in your source DB
  └─ MappingProfile.cs
- ProjectC                    - source model classes
  └─ DataTransferService.cs
- ProjectD                    - target model classes. This is just a nuget package that contains the new app/updated models in target DB

ОБНОВЛЕНИЕ 3 Я попытался создать преобразователь значений, а затем добавить его в профиль ForMember. Я также попытался использовать интерфейс IReadSourceService в конструкторе для распознавателя, надеясь, что DI будет использовать это, но в любом случае я получу исключение AutoMapper.AutoMapperMappingExa для элемента назначения: CurrencyId:

Не удается создать экземпляр Тип ForeignKeyResolver

      this.CreateMap<Source.Models.Instrument, Target.Models.Instruments.Instrument>()
        .ForMember(dest => dest.CurrencyId, opt => opt.MapFrom<ST.DataTaka.Models.Services.ForeignKeyResolver>());
  public class ForeignKeyResolver : IValueResolver<object, object, int>
  {
    private readonly List<Tuple<int, int>> mappings;

    public ForeignKeyResolver(List<Tuple<int, int>> mappings)
    {
      this.mappings = mappings;
    }

    public int Resolve(object source, object dest, int destMember, ResolutionContext context)
    {
      return mappings.Where(c => c.Item1 == (int)source).FirstOrDefault().Item2;
    }
  }

1 Ответ

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

Спасибо за добавленные детали, однако, все еще не совсем понятно, о чем вы просите. Если вы спрашиваете, как прочитать исходные данные из БД, сопоставить их с целевыми моделями, а затем записать их в целевую БД, мне кажется, что все, что вам нужно, это просто заполнить TransferInstrument(). Упрощенный пример (немного угадывая, как выглядит ваш контекст):

public void TransferInstrument()
{
    foreach (var sourceInstrument in this.context.Instruments)
    {
        var targetInstrument =
            this.mapper.Map<Target.Models.Instruments.Instrument>(sourceInstrument);
        this.targetContext.Instruments.Add(targetInstrument);
    }
    this.targetContext.SaveChanges();
}

Это то, что вы ищете? Если нет, то, пожалуйста, объясните, что, по вашему мнению, вам не хватает в вашем коде. И , пожалуйста, покажите нам, что вы пробовали, и скажите нам, почему это не работает так, как вы намереваетесь .


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

- ProjectA                    - console app.
  ├─ Program.cs
  ├─ MappingProfile.cs
  └─ DataTransferService.cs
- ProjectB                    - model classes
  ├─ Source                   - folder for data models in your source DB
  └─ Target                   - folder for data models in your target DB

Основываясь на том, что вы показали, внедрение инъекций кажется немного излишним, если только для этого нет какой-то другой цели, которой у вас нет показано на рисунке. ProjectB будет содержать ваши SourceDbContext и ModelDbContext (цель) и классы для моделей, используемых в этих двух контекстах. Если сопоставление логики c и перенос данных из источника в цель уместно только в этом консольном приложении, я не вижу причины для создания отдельного проекта для него - но опять же: может быть, в этом есть что-то большее, чем показано в вопрос?

ProjectA обрабатывает все бизнес-логики c и соединения с БД, в то время как ProjectB определяет только то, как выглядят модели.

...