3

I have a repository which gets IEnumerable of different types.

I am able to do this by using :

switch (returnType)
{
    case ReturnType.HR:
        _repo.GetSystemManuals();
        break;
    case ReturnType.Finance:
        _repo.GetPrivateRecords();
        break;
    case ReturnType.Dev:
        _repo.GetTimeLine();
        break;
    case ReturnType.Admin:
        _repo.GetLedger();
        break;
    case ReturnType.Support:
        _repo.GetRoster();
        break;
}

But this is violating open/close of SOLID principle.

One of the ways I was thinking is to create a dictionary of ,

private static readonly IDictionary<S95Type, IQueryable<Customer>> ReqTypeMapper 
      = new Dictionary<S95Type, IQueryable<HR>>();
ReqTypeMapper.Add(ReturnType.HR, _repo.GetHR()());

But not sure how I can execute different methods with different return types..

Jamiec
  • 133,658
  • 13
  • 134
  • 193
Simsons
  • 12,295
  • 42
  • 153
  • 269
  • Open/Close has nothing to do with a *switch* statement. There's no extension, there's no modification to a type. A *repository* on the other hand isn't a different name for data layer. It's supposed to deal with a *single* entity, not many. – Panagiotis Kanavos Jan 25 '18 at 09:53
  • @PanagiotisKanavos, If I will have different types in future , I will have to modify in my current class. Just trying to understand if there is a better approach. – Simsons Jan 25 '18 at 09:55
  • you don't have a repository, you are trying to create a factory or DI container. A repo is supposed to handle with a single entity. If you want to pass different repos to different clients have them accept an `IRepository` and use a DI container to produce the appropriate instance – Panagiotis Kanavos Jan 25 '18 at 09:59
  • What is the type of repo ? – Mihai Alexandru-Ionut Jan 25 '18 at 09:59
  • @MihaiAlexandru-Ionut, Type of repo is IOfficeRepository.. – Simsons Jan 25 '18 at 09:59
  • That is, if a repo is even necessary - most of the functionality of repositories is provided by ORMs. A thing wrapper over the ORM is actually a smell and typically leads to complex code and bad performance – Panagiotis Kanavos Jan 25 '18 at 10:00
  • @PanagiotisKanavos, So shall we use context class directly inside my controller? Will it not break I(Dependency injection..) – Simsons Jan 25 '18 at 10:03
  • @Simsons you mean the IQueryable. Unless you mean "how do I call Update from the Controller". *Instead* of using one big `IOfficeRepository` you use pass `IRepository`, `IRepository` etc to the controlelrs that need them. If you have a controller that needs bot an `IRepository` and `IRepository` it means your controller is probably too fat. Controllers are supposed to provide endpoints for REST resources. – Panagiotis Kanavos Jan 25 '18 at 10:17
  • 3
    @Simons to put it another way, IOfficeRepository breaks the Single Responsibility Principle by trying to be everyone's repo – Panagiotis Kanavos Jan 25 '18 at 10:20

3 Answers3

4

I think you should rethink your design and create one repository for each type. A repsoitory by definition should not deal with many different entities. That would be a clean and maintainable solution.

user743414
  • 936
  • 10
  • 23
  • 1
    Of course it should, entities which which exist only in the context of another entity(aggregate) should never ever have their own repository. Having a single repository managing them enforces constraints on them in a single place - the repository of the aggregate. If you start creating repositories for each entity, you are creating a margin for error by adding more potential ways in which the consistency of the data can be lost. – nikksan Jun 01 '19 at 13:16
1

You do not need a huge switch.

Just call the method dynamically.

_repo.getType().GetMethod("Get" + ((ReturnType)returnType).ToString()).Invoke(null,null)
Mihai Alexandru-Ionut
  • 47,092
  • 13
  • 101
  • 128
1

Adding simple case where You have translation between Your enum type and the method itself in Dictionary.

//Define type
public enum ReturnType {
    HR,
    Dev,
    Finance
}

//Define methods
public void HRMethod() { Console.WriteLine("HR"); }
public void DevMethod() { Console.WriteLine("Dev"); }
public void FinanceMethod() { Console.WriteLine("Finance"); }

//Create a dictionary, where You add particular method for particular type
Dictionary<ReturnType, Action> myDict = new Dictionary<ReturnType, Action>();
myDict.Add(ReturnType.HR, HRMethod);
myDict.Add(ReturnType.Dev, DevMethod);
myDict.Add(ReturnType.Finance, FinanceMethod);

//Whenever the call occurs
myDict[ReturnType.HR].Invoke(); 
> HR

Edit:

With return type of IEnumerable it would look like this:

//Define methods
public IEnumerable<HR> GetHR() { return new List<HR>() {new HR() {Name="HR" } }; }
public IEnumerable<Dev> GetDev() { return new List<Dev>() {new Dev() {Name="Dev" } }; }

//Create dict + fill
Dictionary<ReturnType, Func<object>> myDict2 = new Dictionary<ReturnType, Func<object>>();
myDict2.Add(ReturnType.HR, GetHR);
myDict2.Add(ReturnType.Dev, GetDev);

//Work with it as with result type
var lRes = (myDict2[ReturnType.HR].Invoke() as IEnumerable<HR>);
Console.WriteLine(lRes.First().Name);
> HR

Solution2:

A little complicated approach is to: Create custom attribute, over each enum value set the attribute with value of the method to call (or name of method, see below). Once You have this, at start time with reflection You will read these attributes, create Dictionary or a Service, which will provide required method.

Creating own attribute with delegate isn't possible (as of: Is it possible to have a delegate as attribute parameter? ). So You have to use "hacky" solution around including type of original class and then the method name.

Tatranskymedved
  • 4,194
  • 3
  • 21
  • 47
  • Your solution1 still wont work - it does when you can hardcode eg `HR` as the enum and return but if you have a variable `returnType` as the OP does, then it just wont work. Demo: http://rextester.com/BAE41261 – Jamiec Jan 25 '18 at 11:19
  • Is myDict2.Add(ReturnType.HR, GetHR); is avalid statement? Shouldn't it be myDict2.Add(ReturnType.HR, ()>{return GetHR();});? – Simsons Jan 25 '18 at 11:33
  • @Simsons Your syntax is including Lamba syntax, You can ommit that. https://stackoverflow.com/questions/12267280/difference-between-func-with-delegate-and-lambda-expression – Tatranskymedved Jan 25 '18 at 11:40
  • @Jamiec The point of this code was to show how You zip together value from `Enum` and method name. Going to concrete example (as You have described), it isn't written elsewhere, that the abstraction and parametrization must be there everywhere. Further speaking, at some point You need to get to the bottom level and say -> `this is the code that does the job`. – Tatranskymedved Jan 25 '18 at 11:49
  • 1
    There is no code that does this job - you cannot use generics here - the solution (as others have pointed out) is to inject a repo that does just one thing in the right place, not have a multipurpose repo and then try to genericize it using a dictionary of methods which return different values. Its just not possible in either of the ways you describe. – Jamiec Jan 25 '18 at 11:54
  • @Jamiec I agree on that, the difference that we are talking about is in "code-solution" and "principle-solution" approach. By principle, repo version is correct. By "shady" and "will-work" solution, You can use code above. – Tatranskymedved Jan 25 '18 at 11:56