У меня около 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





Прежде чем я начну: это решение предполагает, что все ваши сущности следуют соглашениям об именах, которые вы показали в своем образце кода.
Во-первых, было бы лучше, если бы у вас был репозиторий, эксклюзивный для этой службы, где можно было бы делать запросы по любому типу сущности. Там вы должны использовать свое соглашение для получения имен этих свойств и запрашивать их с помощью 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.
Этот запрос работает: 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 ();
Извините, но я не понял. Работает или нет?
Запрос в вашем ответе дает мне ошибку. Итак, я немного изменил ваш запрос (я написал его в своем предыдущем комментарии), и он работает нормально. Остальные работают просто отлично. Большое спасибо за ваши усилия, теперь я лучше понимаю, как писать общие методы.
Конечно, мужик, нет проблем! Я изменю свой ответ, чтобы он соответствовал запросу в вашем комментарии.
Это .NET Core, верно?