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

2

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

Здесь мой код только для двух объектов:

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 с помощью метода Get:

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 для написания общего метода? Думаю, это можно легко сделать с отражением.

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

  • 0
    Это .NET Core, верно?
  • 0
    Да, это .NET Core
Теги:
.net-core
entity-framework-core

1 ответ

2
Лучший ответ

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

Во-первых, было бы лучше, если бы у вас был эксклюзивный репозиторий для этой службы, где можно было бы запросить любой тип сущности. Там вы должны использовать соглашение, чтобы получить эти имена свойств, и вы можете запросить их с помощью 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);
    }
}
  • 0
    Это очень хорошо, но я получаю ошибку: метод EF.Property <T> может использоваться только в запросах LINQ. Я использую 2.1.1 EF Core.
  • 0
    Этот запрос работает: 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 ();
Показать ещё 3 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню