0

Presentation layer call a method (CreateEvent) in my application layer. This method use generic parameters :

public async Task<string> CreateEvent<T, TDocument>(T @event)
 where T : class
 where TDocument : Document
 {
            using (var scope = _serviceProvider.CreateScope())
            {
                var myRepository = scope.ServiceProvider.GetRequiredService<ICarrierEventRepository<TDocument>>();

                var eventMapped = _mapper.Map<TDocument>(@event);

                await myRepository.InsertOneAsync(eventMapped);

                return eventMapped.Id.ToString();
            }
}

Parameter T is object define in presentation layer and TDocument is abstract class that my entities (Domain layer) inherit.

 public abstract class Document : IDocument
    {
        public ObjectId Id { get ; set ; }
        
        //some other properties....
    }

Example of entity :

    public class PaackCollection : Document
    {
        public string ExternalId { get; set; }

        public DateTime At { get; set; }

         //some other properties....
    }

In presentation layer, I call my CreateEvent method like this :

[HttpPost]
 public async Task<IActionResult> Post(PayLoadPaackModel payLoadPaackModel)
 {
            var idCreated = await _carrierEventService.CreateEvent<PayLoadPaackModel, Domain.Entities.MongoDb.PaackCollection>(payLoadPaackModel);

            //some code here....

            return Ok("OK");
}

It's possible to use type of Domain.Entities.MongoDb.PaackCollection as parameter knowing that it belongs to the domain layer ? Normally presentation layer communicate only with application layer.

Thanks for advices

UPDATE This solution works :

Call CreateEvent :

await _carrierEventService.CreateEvent(paackEventMapped);
public async Task<string> CreateEvent<T>(T @event)
            where T : class
        {
            using (var scope = _serviceProvider.CreateScope())
            {
                Type typeParameterType = typeof(T);

                if (typeParameterType.Equals(typeof(PaackEventDto)))
                {
                    var eventMapped = _mapper.Map<PaackEvent>(@event);

                    var carrierEventRepository = scope.ServiceProvider.GetRequiredService<ICarrierEventRepository<PaackEvent>>();

                    await carrierEventRepository.InsertOneAsync(eventMapped);

                    return eventMapped.Id.ToString();
                }
                else if (typeParameterType.Equals(typeof(LaPosteEventDto)))
                {
                    var eventMapped = _mapper.Map<LaposteEvent>(@event);

                    var carrierEventRepository = scope.ServiceProvider.GetRequiredService<ICarrierEventRepository<LaposteEvent>>();

                    await carrierEventRepository.InsertOneAsync(eventMapped);

                    return eventMapped.Id.ToString();
                }
                else
                    return default;
            }
        }

Is there another solution to use generic for avoid to have lot of condition to compare object ? Because I can have 50 differents objects...

UPDATE

I found solution to get the DestinationType for mapper :

var destinationMap = _mapper.ConfigurationProvider.GetAllTypeMaps().First(t => t.SourceType == typeParameterType);

var destType = destinationMap.DestinationType;

var eventMapped = _mapper.Map(@event, typeParameterType, destType);

It's working, now how I can get type of carrierEventRepository with destType ? I try this var repository = typeof(ICarrierEventRepository<>).MakeGenericType(destType); but I can use method of my repository...

Julien Martin
  • 197
  • 2
  • 15
  • 1
    It is possible to use your entities where ever you want in your project, but it is considered bad practice and might cause lot of issues as application becomes more complex. It might be better to have Models or Dtos which you can pass to Application layer and in application layer you can map your Models or Dtos to your Domain Entities. – Manik Apr 07 '22 at 11:53

2 Answers2

1

Here is another example where I am passing a Dto to my Api base class.

 public async Task<ServiceResponse<TServiceResponce>> CreateAsyncServiceWrapper<TServiceResponce, TModelToCreate>(string url, TModelToCreate ModelToCreate)
    { Removed Code}

I am calling it like this

 _serviceResponce = await _compRepo.CreateAsyncServiceWrapper<ServiceResponse<CompanyDto>, CreateCompanyDto>(StaticDetails.CompanyAPIPath, model);

Here is an example from one of my blogs.

    /// <summary>
    /// Create a new company Record.
    /// </summary>
    /// <param name="createCompanyDto"></param>
    /// <returns></returns>      
    [HttpPost]
    [ProducesResponseType(StatusCodes.Status200OK, Type = typeof(CompanyDto))]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status404NotFound)] 
    [ProducesResponseType(StatusCodes.Status500InternalServerError)]
    public async Task<ActionResult<CompanyDto>> CreateCompany([FromBody] CreateCompanyDto createCompanyDto)
    {
        if (createCompanyDto == null)
        {
            return BadRequest(ModelState);
        }

        if (!ModelState.IsValid) { return BadRequest(ModelState); }

        var _newCompany = await _companyService.AddCompanyAsync(createCompanyDto);

        if (_newCompany.Success == false && _newCompany.Message == "Exist")
        {
            return Ok(_newCompany);
        }


        if (_newCompany.Success == false && _newCompany.Message == "RepoError")
        {
            ModelState.AddModelError("", $"Some thing went wrong in respository layer when adding company {createCompanyDto}");
            return StatusCode(500, ModelState);
        }

        if (_newCompany.Success == false && _newCompany.Message == "Error")
        {
            ModelState.AddModelError("", $"Some thing went wrong in service layer when adding company {createCompanyDto}");
            return StatusCode(500, ModelState);
        }

        //Return new company created
        return CreatedAtRoute("GetCompanyByGUID", new { CompanyGUID = _newCompany.Data.GUID }, _newCompany);

    }
Manik
  • 97
  • 1
  • 7
  • Yes you're right but , I don't know how I can map TDocument parameter in my controller ? Because it's TDocument who give me the domain entity to use with my repository. – Julien Martin Apr 07 '22 at 13:19
  • @JulienMartin - If I may suggest you need to create a Dto that either have same properties as your payLoadPaackModel or more depending upon requirements. You pass the Dto down to your Application layer and in application layer you can use AutoMapper or Manully map the properties to your entity. `public async Task CreateEvent(T @event) where T : class where TDocument : Document`...... **T and TDocument are generic type** you might want to read up on it. In your method you can check `if (TObject.GetType().Equals(typeof(YourObject))) ` to make sure. – Manik Apr 07 '22 at 15:23
  • Yes I see, but TDocument can't be Domain.Entities.MongoDb.PaackCollection, it would be PaackCollectionDto and next I use AutoMapper to map to my entity Domain.Entities.MongoDb.PaackCollection. that's right ? – Julien Martin Apr 07 '22 at 15:35
  • I update post with modification. – Julien Martin Apr 07 '22 at 16:00
  • @JulienMartin - **TDocument is IDocument** in your updated code example and **TDocument : Document** in your first example code. So yes, as long as your DTO or Model passed to your application layer reflect Document object. You are on right path :). – Manik Apr 08 '22 at 08:24
-1

I finally found solution :

To get destination type with Automapper, I use _mapper.ConfigurationProvider.GetAllTypeMaps(), MakeGenericType help me to have my ICarrierEventRepository<T> and with this Post help me to use dynamic keyword for call method InsertOneAsync.

public async Task<string> CreateEvent<T>(T @event)
            where T : class
{
    using (var scope = _serviceProvider.CreateScope())
    {
        //Get destination type
        Type typeParameterType = typeof(T);
        var destinationMap = _mapper.ConfigurationProvider.GetAllTypeMaps().First(t => t.SourceType == typeParameterType);
        var destType = destinationMap.DestinationType;

        //Map with destination type
        var eventMapped = _mapper.Map(@event, typeParameterType, destType);

        //Get repository register in services
        var repository = typeof(ICarrierEventRepository<>).MakeGenericType(destType);
                dynamic repo = scope.ServiceProvider.GetRequiredService(repository);

        //Insert on database
        await repo.InsertOneAsync((dynamic)eventMapped);

        //Get generate id
        return ((dynamic)eventMapped).Id.ToString();
   }
}
Julien Martin
  • 197
  • 2
  • 15
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Apr 09 '22 at 00:06
  • Why I’ve -1 score ? – Julien Martin Apr 10 '22 at 10:06
  • Score will get better @JulienMartin if you improve on what was the prblem and what, where and how you resoved it :) – Manik Apr 20 '22 at 08:27