Получить необходимые свойства класса

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

Вот мой код только для двух сущностей:

public class GetParamsService : IGetParamsService
{
    private readonly IMemoryCache _cache;
    private readonly MemoryCacheEntryOptions _cacheOptions;
    private readonly IDealTypeRepository _dealTypeRepository;
    private readonly ICurrencyRepository _currencyRepository;      

    public GetParamsService(IMemoryCache memoryCache, 
        IDealTypeRepository dealTypeRepository,
        ICurrencyRepository currencyRepository)
    {
        _cache = memoryCache;
        _cacheOptions = new MemoryCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromHours(2));

        _dealTypeRepository = dealTypeRepository;
        _currencyRepository = currencyRepository;
    }

    public async Task<(int idDealType, string dealTypeName)> GetDealTypeParams(
        string dealTypeCode)
    {
        if (!_cache.TryGetValue(CacheKeys.IdDealType, out int idDealType)
            | !_cache.TryGetValue(CacheKeys.DealTypeName, out string dealTypeName))
        {
            var dealType = await _dealTypeRepository
                .Get(x => x.Code == dealTypeCode, dealTypeCode);

            idDealType = dealType.IdDealType;
            dealTypeName = dealType.Name;

            _cache.Set(CacheKeys.IdDealType, idDealType, _cacheOptions);
            _cache.Set(CacheKeys.DealTypeName, dealTypeName, _cacheOptions);
        }

        return (idDealType, dealTypeName);
    }

    public async Task<(int idCurrency, string currencyName)> GetCurrencyParams(
        string currencyCode)
    {
        if (!_cache.TryGetValue(CacheKeys.IdCurrency, out int idCurrency)
            | !_cache.TryGetValue(CacheKeys.CurrencyName, out string currencyName))
        {
            var currency = await _currencyRepository
                .Get(x => x.Code == currencyCode, currencyCode);

            idCurrency = currency.IdCurrency;
            currencyName = currency.Name;

            _cache.Set(CacheKeys.IdCurrency, idCurrency, _cacheOptions);
            _cache.Set(CacheKeys.CurrencyName, currencyName, _cacheOptions);
        }

        return (idCurrency, currencyName);
    }
}

Итак, методы GetDealTypeParams и GetCurrencyParams в значительной степени одинаковы, и я хочу создать один общий метод вместо множества похожих методов. Думаю, в этом есть смысл.

Проблема в том, что я не знаю, как получить «правильные свойства для данной сущности» в классе CacheKeys:

public static class CacheKeys
{
    public static string IdDealType => "_IdDealType";

    public static string DealTypeName => "_DealTypeName";

    public static string IdCurrency => "_IdCurrency";

    public static string CurrencyName => "_CurrencyName";

    // ...

}

Каждый репозиторий наследуется от GenericRepository с помощью метода Получать:

public class DealTypeRepository : GenericRepository<DealTypeEntity>, IDealTypeRepository
{       
    public DealTypeRepository(DbContextOptions<MyContext> dbContextOptions)
        : base(dbContextOptions)
    {

    }
}

public class GenericRepository<TEntity> where TEntity : class
{
    private readonly DbContextOptions<MyContext> _dbContextOptions;

    public GenericRepository(DbContextOptions<MyContext> dbContextOptions)
    {
        _dbContextOptions = dbContextOptions;
    }

    public async Task<TEntity> Get(Expression<Func<TEntity, bool>> predicate, string code)
    {
        try
        {
            using (var db = new MyContext(_dbContextOptions))
            {
                using (var tr = db.Database.BeginTransaction(
                    IsolationLevel.ReadUncommitted))
                {
                    var entity = await db.Set<TEntity>().AsNoTracking()
                        .FirstAsync(predicate);

                    tr.Commit();

                    return entity;
                }
            }
        }
        catch (Exception e)
        {
            throw new Exception("Error on getting entity by code: {code}");
        }
    }
}

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

Обновлено: Я не уверен, нужно ли мне пробовать один общий метод, поскольку каждая сущность имеет свойство Id со своим собственным именем (например, IdDealType для dealType, IdCurrency для валюты)

Это .NET Core, верно?

Gabriel Rainha 11.10.2018 16:05

Да, это .NET Core

Dmitry Stepanov 11.10.2018 16:07
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
2
125
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

Во-первых, было бы лучше, если бы у вас был репозиторий, эксклюзивный для этой службы, где можно было бы делать запросы по любому типу сущности. Там вы должны использовать свое соглашение для получения имен этих свойств и запрашивать их с помощью EF.Property. Поскольку все ваши запросы, похоже, находятся в столбце «Код», мы также можем упростить параметры в методе этого репо.

public class ParamRepository : IParamRepository
{
    private readonly DbContextOptions<MyContext> _dbContextOptions;

    public ParamRepository(DbContextOptions<MyContext> dbContextOptions)
    {
        _dbContextOptions = dbContextOptions;
    }

    public async Task<(int id, string name)> GetParamsByCode<TEntity>(string code) where TEntity : class
    {
        string entityName = typeof(TEntity).Name;
        string idProp = $"Id{entityName}";
        string nameProp = $"{entityName}Name";
        try
        {
            using (var db = new MyContext(_dbContextOptions))
            {
              var entity = await db.Set<TEntity>().AsNoTracking()
                        .Where(p => EF.Property<string>(p, "Code") == code)
                        .Select(p => new { Id = EF.Property<int>(p, idProp), Name = EF.Property<string>(p, nameProp)})
                        .FirstAsync();

              return (id: entity.Id, name: entity.Name);
            }
        }
        catch (Exception e)
        {
            throw new Exception("Error on getting entity by code: {code}");
        }
    }
}

Вам также потребуется реорганизовать ключи кеша, чтобы они были созданы в соответствии с соглашениями:

public static class CacheKeys
{
    public static string GetIdKey<TEntity>() => $"_Id{typeof(TEntity).Name}";
    public static string GetNameKey<TEntity>() => $"_{typeof(TEntity).Name}Name";
}

Затем GetParamsService становится проще:

public class GetParamsService
{
    private readonly IMemoryCache _cache;
    private readonly MemoryCacheEntryOptions _cacheOptions;
    private readonly IParamRepository _paramRepository;

    public GetParamsService(IMemoryCache memoryCache,
        IParamRepository paramRepository)
    {
        _cache = memoryCache;
        _cacheOptions = new MemoryCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromHours(2));

        _paramRepository = paramRepository;
    }

    public async Task<(int id, string name)> GetParams<TEntity>(string code) where TEntity : class
    {
        string cacheIdKey = CacheKeys.GetIdKey<TEntity>();
        string cacheNameKey = CacheKeys.GetNameKey<TEntity>();
        if (!_cache.TryGetValue(cacheIdKey, out int cacheId)
            | !_cache.TryGetValue(cacheNameKey, out string cacheName))
        {
            var param = await _paramRepository.GetParamsByCode<TEntity>(code);

            cacheId = param.id;
            cacheName = param.name;

            _cache.Set(cacheIdKey, cacheId, _cacheOptions);
            _cache.Set(cacheNameKey, cacheName, _cacheOptions);
        }

        return (cacheId, cacheName);
    }
}

Это очень хорошо, но я получаю сообщение об ошибке: Метод EF.Property <T> можно использовать только в запросах LINQ. Я использую 2.1.1 EF Core.

Dmitry Stepanov 12.10.2018 09:09

Этот запрос работает: var entity = await db.Set <TEntity> () .AsNoTracking () .Where (p => EF.Property <string> (p, "Code") == code) .Select (p => новый {Id = EF.Property <int> (p, idProp), Name = EF.Property <string> (p, nameProp)}). FirstAsync ();

Dmitry Stepanov 12.10.2018 09:24

Извините, но я не понял. Работает или нет?

Gabriel Rainha 14.10.2018 03:04

Запрос в вашем ответе дает мне ошибку. Итак, я немного изменил ваш запрос (я написал его в своем предыдущем комментарии), и он работает нормально. Остальные работают просто отлично. Большое спасибо за ваши усилия, теперь я лучше понимаю, как писать общие методы.

Dmitry Stepanov 14.10.2018 19:10

Конечно, мужик, нет проблем! Я изменю свой ответ, чтобы он соответствовал запросу в вашем комментарии.

Gabriel Rainha 15.10.2018 21:34

Другие вопросы по теме