Как лучше проверить Enum и вызвать методы репозитория

У меня есть репозиторий, который получает IEnumerable разных типов.

Я могу сделать это, используя:

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;
}

Но это нарушает принцип открытия/закрытия SOLID.

Один из способов, которым я думал, - это создать словарь ,

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

Но не уверен, как я могу выполнять разные методы с разными типами возврата.


person Simsons    schedule 25.01.2018    source источник
comment
Open/Close не имеет ничего общего с оператором switch. Нет расширения, нет модификации типа. С другой стороны, репозиторий — это не другое название уровня данных. Предполагается, что он имеет дело с одним объектом, а не со многими.   -  person Panagiotis Kanavos    schedule 25.01.2018
comment
@PanagiotisKanavos, если в будущем у меня будут другие типы, мне придется изменить свой текущий класс. Просто пытаюсь понять, есть ли лучший подход.   -  person Simsons    schedule 25.01.2018
comment
у вас нет репозитория, вы пытаетесь создать фабрику или контейнер DI. Предполагается, что репо обрабатывается одним объектом. Если вы хотите передать разные репозитории разным клиентам, попросите их принять IRepository<T> и использовать контейнер DI для создания соответствующего экземпляра.   -  person Panagiotis Kanavos    schedule 25.01.2018
comment
Что такое тип репо?   -  person Mihai Alexandru-Ionut    schedule 25.01.2018
comment
@MihaiAlexandru-Ionut, тип репо - IOfficeRepository..   -  person Simsons    schedule 25.01.2018
comment
То есть, если репо вообще необходимо — большая часть функционала репозиториев обеспечивается ORM. Оболочка над ORM на самом деле является запахом и обычно приводит к сложному коду и плохой производительности.   -  person Panagiotis Kanavos    schedule 25.01.2018
comment
@PanagiotisKanavos, так должны ли мы использовать класс контекста непосредственно внутри моего контроллера? Не сломается ли я (инъекция зависимостей..)   -  person Simsons    schedule 25.01.2018
comment
@Simsons, вы имеете в виду IQueryable. Если вы не имеете в виду, как мне вызвать обновление из контроллера. Вместо использования одного большого IOfficeRepository вы используете передачу IRepository<Ledger>, IRepository<Roster> и т. д. тем контроллерам, которым они нужны. Если у вас есть контроллер, которому нужны IRepository<Ledger> и IRepository<Roster>, это означает, что ваш контроллер, вероятно, слишком толстый. Предполагается, что контроллеры предоставляют конечные точки для ресурсов REST.   -  person Panagiotis Kanavos    schedule 25.01.2018
comment
@Simons, другими словами, IOfficeRepository нарушает принцип единой ответственности, пытаясь быть репозиторием для всех.   -  person Panagiotis Kanavos    schedule 25.01.2018


Ответы (3)


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

person user743414    schedule 25.01.2018
comment
Конечно, так и должно быть, сущности, которые существуют только в контексте другой сущности (совокупности), никогда не должны иметь своего собственного репозитория. Наличие единого репозитория, управляющего ими, налагает на них ограничения в одном месте — репозитории агрегата. Если вы начнете создавать репозитории для каждой сущности, вы создадите поле для ошибок, добавив больше потенциальных способов, в которых может быть потеряна согласованность данных. - person nikksan; 01.06.2019

Вам не нужен огромный коммутатор.

Просто вызовите метод динамически.

_repo.getType().GetMethod("Get" + ((ReturnType)returnType).ToString()).Invoke(null,null)
person Mihai Alexandru-Ionut    schedule 25.01.2018
comment
Эй, может быть, имя типа Enum не совпадает с именем метода.. Редактировал ли вопрос.. - person Simsons; 25.01.2018
comment
Вам также не нужно отражение - использование дженериков и перегрузка методов выполняет то же самое, не жертвуя безопасностью типов. - person Panagiotis Kanavos; 25.01.2018

Добавление простого случая, когда у вас есть перевод между вашим типом enum и самим method в 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

Изменить:

С возвращаемым типом IEnumerable это будет выглядеть так:

//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

Решение 2.

Немного сложный подход: создайте пользовательский attribute, поверх каждого значения перечисления установите attribute со значением вызываемого метода (или имя метода, см. ниже). Как только вы это сделаете, во время запуска с отражением вы прочитаете эти атрибуты, создадите Dictionary или Service, которые предоставят требуемый метод.

Создание собственного атрибута с делегатом невозможно (начиная с: Возможно ли использовать делегат в качестве параметра атрибута?). Таким образом, вы должны использовать «хакерское» решение, включая тип исходного класса, а затем имя метода.

person Tatranskymedved    schedule 25.01.2018
comment
Ваше решение1 по-прежнему не будет работать - оно работает, когда вы можете жестко закодировать, например, HR как перечисление и вернуть, но если у вас есть переменная returnType, как это делает OP, тогда оно просто не будет работать. Демо: rextester.com/BAE41261 - person Jamiec; 25.01.2018
comment
Является ли myDict2.Add(ReturnType.HR, GetHR); является действительным заявлением? Разве это не должно быть myDict2.Add(ReturnType.HR, ()›{return GetHR();});? - person Simsons; 25.01.2018
comment
@Simsons Ваш синтаксис включает синтаксис Lamba, вы можете его опустить. stackoverflow.com/ вопросы/12267280/ - person Tatranskymedved; 25.01.2018
comment
@Jamiec Целью этого кода было показать, как вы объединяете значение из Enum и имени метода. Переходя к конкретному примеру (как Вы описали), нигде не написано, что абстракция и параметризация должны быть везде. Далее, в какой-то момент Вам нужно добраться до нижнего уровня и сказать -› this is the code that does the job. - person Tatranskymedved; 25.01.2018
comment
Нет кода, который выполняет эту работу - вы не можете использовать здесь дженерики - решение (как указывали другие) состоит в том, чтобы ввести репо, которое делает только одну вещь в нужном месте, не иметь многоцелевого репо, а затем попытаться обобщить его используя словарь методов, которые возвращают разные значения. Это просто невозможно ни одним из способов, которые вы описываете. - person Jamiec; 25.01.2018
comment
@Jamiec Я согласен с этим, разница, о которой мы говорим, заключается в подходе «код-решение» и «принцип-решение». В принципе версия репо правильная. С помощью сомнительного и работающего решения вы можете использовать код выше. - person Tatranskymedved; 25.01.2018