0

I am using repository pattern in a .NET MVC project where I need to fetch data using the same function but in a two different structures(conditionally).

To be more specific, there are cases where I need to fetch the full version of the model having a bunch of properties but there are also cases where I need to fetch a frugal version of the model (mostly for security reasons).

The code so far:

public async Task<IEnumerable<AnswerMinimalDto>> GetQuestionsForUser(int userId)
{
    IEnumerable<Answer> foundAnswers = await this.repository.getAnswersByUser(userId);

    return Mapper.Map<IEnumerable<AnswerMinimalDto>>(foundAnswers);
}

So the mapping should happen conditionally:

....return Mapper.Map<IEnumerable<AnswerMinimalDto>>(foundAnswers);

or

....return Mapper.Map<IEnumerable<AnswerFullDto>>(foundAnswers);

Here we have two object oriented principles conflicted. First thought is to have two different methods to satisfy the Single Responsibility principle. On the other hand, by having two different methods doing the same job is a duplication.

I am going with the approach of a single method. What I've tried so far is to make an approach using Tuples to return both models and handle the required result from the controller (out can not apply since my method is async). But I am not really happy with the approach.

To the point, I wonder if there is any elegant/preferable way to return the data in different structure chosen conditionally.

Any help is welcome.

George George
  • 391
  • 1
  • 5
  • 14
  • When you look at a problem and think; "I know I'll solve this using AutoMapper", you now have two problems – Liam Nov 30 '18 at 16:30

2 Answers2

2

having two different methods doing the same job is a duplication

But they are not doing the same job! one gives you more data, another less data. That's two different things in my view.

Also trying to get controller to decide what to present to the client is a smell - business logic is spelled out to controller.

As you said - you can have 2 methods that give different data. But I would go further, and segregate the interfaces: one interface, two implementations. IRepository and FullRepositoryImpl and FrugalRepositoryImpl.

And let your DI decide which one is required at the moment, because I bet this is not a single occurrence where you need to present a limited set of data.

trailmax
  • 31,605
  • 20
  • 126
  • 225
  • I definitely agree with that approach although I end up using the generic () way. Thank you for your time and your response. – George George Nov 30 '18 at 16:52
1

You can solve this simply using Generics:

public async Task<IEnumerable<T>> GetQuestionsForUser<T>(int userId)
{
    IEnumerable<Answer> foundAnswers = await this.repository.getAnswersByUser(userId);

    return Mapper.Map<IEnumerable<T>>(foundAnswers);
}

IEnumerable<AnswerMinimalDto> a = await GetQuestionsForUser<AnswerMinimalDto>(foundAnswers);
IEnumerable<AnswerFullDto> b = await GetQuestionsForUser<AnswerFullDto>(foundAnswers);

Though I do agree with a lot that trailmax says on this TBH.

IMO DTO objects and Automapper are code smells/anti patterns. Why don't you just return your underlying object?

Liam
  • 22,818
  • 25
  • 93
  • 157
  • this solution looks clean and simple. Thanks a lot. As for your question, I follow the general project approach which EntityModel stay to DAL and use DTO for the rest transactions - I am not sure if I get 100% what you mean – George George Nov 30 '18 at 16:51
  • Your assuming that what MS put into it's tutorials is the "general approach". Much of what Ms pushes is it's own technology and what they say is not always best practice. Here are some actual [design patterns/best practice](https://www.dofactory.com/net/design-patterns). There is no need that all code needs to be [lava layered](http://mikehadlow.blogspot.com/2014/12/the-lava-layer-anti-pattern.html) or use [DTO 'd](https://stackoverflow.com/questions/1440952/why-are-data-transfer-objects-dtos-an-anti-pattern). The less said about EF the better. – Liam Nov 30 '18 at 16:56