Entity Framework Core IEnumerable async - c#

I have implemented a repository pattern in my project and have a CoinRepository and I want to add a new method (GetValues) which retrieves only one column (values) based on a condition from the Coin table which has multiple columns.
Here is the CoinRepositopry class and the method.
public class CoinRepository : Repository<Coin>, ICoinRepository
{
public CoinRepository(MyContext context) : base(context) { }
public IEnumerable<decimal> GetValuesAsync(int gameId, int gameTableId, string partnerCurrencyId)
{
return GetAllAsync().Result
.Where(c => c.GameId == gameId && c.CurrencyId == partnerCurrencyId)
.Select(c => c.Value);
}
}
The GetAllAsync method is a method in the IRepository interface which returns a Task <IEnumerable<Entity>>.
public async Task<IEnumerable<T>> GetAllAsync(Expression<Func<T, bool>> filter = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, string includeProperties = null)
{
IQueryable<T> query = dbSet;
if (filter != null)
query = query.Where(filter);
if (includeProperties != null)
foreach (var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
query = query.Include(includeProperty);
if (orderBy != null)
return await orderBy(query).ToListAsync();
return await query.ToListAsync();
}
My questions are:
Should I make the GetValuesAsync an async method?
Does the GetAllAsync method execute a query in the database and retrieve all the records, then apply the conditions in code - or does it execute the query in the database like this SELECT c.value FROM COIN c WHERE <condition> ?
If my code has problems and it is not fast enough, how can I modify it and refactor it in the most optimal way?
Thank you

Should I make the GetValuesAsync an async method?
Yes, definitely. Async propagates all the way up the call-stack. By accessing Result you're blocking the thread and defeating the purpose of async.
Does the GetAllAsync method execute a query in the database, retrieve all the records and then apply the condition in the code or execute the query in the database like this SELECT c.value FROM COIN c WHERE?
You haven't provided an expression for Where so it will retrieve all rows from the database and filter in-memory.
If my code has problems and it is not fast enough, how can I modify it and refactor it in the most optimal way?
public class CoinRepository : Repository<Coin>, ICoinRepository
{
public CoinRepository(MyContext context) : base(context) { }
public async Task<IEnumerable<decimal>> GetValuesAsync(int gameId, int gameTableId, string partnerCurrencyId)
{
var coins = await GetAllAsync(c => c.GameId == gameId && c.CurrencyId == partnerCurrencyId,
includeProperties: nameof(Coin.Value));
return coins.Select(c => c.Value);
}
}
This way, you pass an expression to GetAllAsync which can be used to generate an SQL where clause, and specify only the Value column to retrieve.

Related

Temporal tables with Linq2Sql

I've got a generic repository to either get en entity by ID or to get all entities:
internal class Repository<TEntity> : IRepository<TEntity>
where TEntity : BaseEntity
{
protected SaiContext Context { get; }
/// <summary>Gets the entity set.</summary>
protected virtual DbSet<TEntity> Set => Context.Set<TEntity>();
public Repository(SaiContext context)
{
Context = context;
}
public async Task<TEntity> GetAsync(int entityId, IEnumerable<string> includeProperties = null)
{
try
{
return await GetQueryableWithIncludes(includeProperties).SingleAsync(entity => entity.Id == entityId);
}
catch (InvalidOperationException)
{
throw new EntityNotFoundException(typeof(TEntity), entityId);
}
}
public async Task<IEnumerable<TEntity>> GetAllAsync(IEnumerable<string> includeProperties = null)
{
return await GetQueryableWithIncludes(includeProperties).ToListAsync();
}
protected IQueryable<TEntity> GetQueryableWithIncludes(IEnumerable<string> includeProperties = null)
{
IQueryable<TEntity> queryable = Set;
if (includeProperties == null)
{
return queryable;
}
foreach (var propertyName in includeProperties)
{
queryable = queryable.Include(propertyName);
}
return queryable;
}
}
After having configured the DbContext for entity relations, navigation properties along with all the rest is being loaded correctly for all entities.
Now I've been asked to use temporal SQL tables so that all entities have a validity range.
With SQL I'd include FOR SYSTEM_TIME AS OF #validityDate in the query.
What is the easiest way (if there is any) to adapt the existing implementation in order to respect #validityDate?
What I've tried:
Look for a way to configure the wanted system time when performing the SQL query. ISSUE: I couldn't find a way.
Expose the query through a table valued function allowing to pass #validityDate as a parameter. ISSUE: I can't pass the parameter using Linq2Sql (or at least I didn't figurte out how).
Create a table valued function performing the joins (instead of letting EF do them) so it can be called with context.FromSqlRaw(<query>). ISSUE: How to create the c# object tree? (multiple rows are being returned as there are 1 to many relations)
All examples using temporal tables I've found use FromSqlRaw. If possible I'd like to avoid it, as it means that the entire DB context sonfiguration becomes useless and additional code for the mappings has to be included.
I've found the solution with the efcore-temporal-query (nuget) library.
The code has been adapted to use temporal tables as described in the README.
The repository methods now accept an optional parameter validityDate:
public async Task<TEntity> GetAsync(int entityId, DateTime? validityDate = null, IEnumerable<string> includeProperties = null)
{
try
{
var query = GetQueryableWithIncludes(includeProperties);
query = GetQueryableWithValidityDate(query, validityDate);
return await query.SingleAsync(entity => entity.Id == entityId);
}
catch (InvalidOperationException)
{
throw new EntityNotFoundException(typeof(TEntity), entityId);
}
}
protected IQueryable<TEntity> GetQueryableWithIncludes(IEnumerable<string> includeProperties = null)
{
IQueryable<TEntity> queryable = Set;
if (includeProperties == null)
{
return queryable;
}
foreach (var propertyName in includeProperties)
{
queryable = queryable.Include(propertyName);
}
return queryable;
}
private static IQueryable<TEntity> GetQueryableWithValidityDate(IQueryable<TEntity> query, DateTime? validityDate)
{
return validityDate.HasValue ? query.AsOf(validityDate.Value) : query;
}
Where te relevant part for the historized query is query.AsOf(validityDate.Value).

IQueryable.Count for query involving navigation properties throws because they aren't loaded

Having a query that performs filtering that requires Navigation Properties which are explicitly included would not load them when the .Count() method is used on the query. Causing exceptions when the filtering expression in the Where() clauses run.
Changing the .Count() to .ToList() however would make Entity Framework include the referenced navigation properties and produces correct results
It seems that the Count() method ignores the Navigation Properties and their value is null in the filtering expression
My Project is using ASP Net Core 1.1 and EntityFramework 1.1.4
Sample Code
public class ItemsService : IItemsService
{
private readonly ApplicationDbContext dbContext;
public ItemsService(ApplicationDbContext dbContext)
{
this.dbContext = dbContext;
}
public ISearchResult Search(ISearchOptions options)
{
var query = this.dbContext.Item
.Include(i => i.Vehicle)
.Include(x => x.Part).ThenInclude(p => p.PartCategory)
.Where(i => i.IsDeleted == false);
query = this.ApplyMakeFiltering(query, options.Make);
query = this.ApplyModelFiltering(query, options.Model);
var total = query.Count(); // Throws => NavigationProperty Vehicle is null
var total2 = query.ToList().Count; // Works Correctly
return new SearchResult(options, query, total);
}
private IQueryable<Item> ApplyMakeFiltering(IQueryable<Item> query, string make)
{
return this.Filter(
query,
make, // item.Vehicle is null for `Count()` but set correctly for `ToList()`
item => this.StringMatches(item.Vehicle.CustomVehicleMakeName, make));
}
private IQueryable<Item> ApplyModelFiltering(IQueryable<Item> query, string model)
{
return this.Filter(
query,
model,
item => this.StringMatches(item.Vehicle.CustomVehicleModelName, model));
}
private IQueryable<Item> Filter<T>(IQueryable<Item> query, T expected, Predicate<Item> matcher)
{
if (expected == null) return query;
return query.Where(item => matcher(item));
}
private bool StringMatches(string a, string b)
{
if (String.IsNullOrEmpty(a)) return false;
return a.Equals(b, StringComparison.OrdinalIgnoreCase);
}
}
What is the proper way to obtain the count of matched results after filtering?

Unit of Work Pattern And Updating Entity Collection Properties

I'm using generic repository pattern with unit of work implementation for my project.
Recently I've come to an issue which I could not solve. When I try to update an entity's collection property (i.e: Add a new associated entity) and call update on my UoW (to delegate it to repository and obviously EF) it does not save to the database.
My generic repository:
public class GenericRepository<TEntity> where TEntity : class
{
internal MyDBContext context;
internal DbSet<TEntity> dbSet;
public GenericRepository(MyDBContext context)
{
this.context = context;
this.dbSet = context.Set<TEntity>();
}
internal virtual IQueryable<TEntity> BuildQuery(Expression<Func<TEntity,bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "")
{
IQueryable<TEntity> query = dbSet.AsNoTracking();
foreach (var include in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(include);
}
if (filter != null)
query = query.Where(filter);
if (orderBy != null)
return orderBy(query);
return query;
}
public virtual IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "")
{
IQueryable<TEntity> query = BuildQuery(filter, orderBy, includeProperties);
return query.ToList();
}
public virtual void Update(TEntity entity)
{
dbSet.Attach(entity);
context.Entry<TEntity>(entity).State = EntityState.Modified;
}
}
My Unit of Work implementation
public class UnitOfWork : IDisposable
{
//code removed for clarity
public GenericRepository<CorporateServiceCategory> ServiceCategories
{
get
{
if(this.serviceCategoryRepository == null)
{
serviceCategoryRepository = new GenericRepository<CorporateServiceCategory>(context);
}
return serviceCategoryRepository;
}
}
public void Commit()
{
context.SaveChanges();
}
}
What I'm trying to do is:
using(var unit = new UnitOfwork())
{
//unit.Companies is a generic repository instance for Company entities.
var company = unit.Companies.Get(filter: f => f.Id == 1).SingleOrDefault();
company.ServiceCategories.Add(new ServiceCategory {Name = "Demo"});
unit.Companies.Update(company);
//This is a call to context.SaveChanges();
unit.Commit();
}
I expect this code to create a new Company -> ServiceCategory association and add a record to the database. When I do the same operation without Unit of Work but using DbContext itself, it works.
What am I doing wrong with my UoW & Generic Repository implementation?
Thanks to SOfanatic's comment, the problem has solved now.
I've updated my GenericRepository's BuildQuery method to reflect SOfanatic's suggestion and it worked.
Here is the updated BuildQuery method:
internal virtual IQueryable<TEntity> BuildQuery(Expression<Func<TEntity,bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "")
{
IQueryable<TEntity> query = this.context.IsReadOnly ? dbSet.AsNoTracking() : dbSet;
foreach (var include in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(include);
}
if (filter != null)
query = query.Where(filter);
if (orderBy != null)
return orderBy(query);
return query;
}
DbContext.IsReadOnly is a custom property I added to my DbContext implementation. That way if I want to load entities in a kind of "read-only" mode (only selects) I disable lazy loading, proxy generation and change tracking so it increases EF performance a bit.

Generic repository, function explanation / c#

I have found some interesting generic repository, but I can't figure out what the function does: PerformInclusions(includeProperties, query);
Call to PerformInclusions,
public T Single(Expression<Func<T, bool>> where, string includeProperties)
{
try
{
IQueryable<T> query = IDbSet;
query = PerformInclusions(includeProperties, query);
return query.Single(where);
}
catch (InvalidOperationException ex)
{
return null;
}
}
PerformInclusions
private static IQueryable<T> PerformInclusions(string includeProperties,
IQueryable<T> query)
{
if (includeProperties != null && includeProperties.Length > 0)
{
foreach (var includeProperty in includeProperties.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
}
return query;
}
I can use the single function without the second parameter.
Euser test = Adapter.EuserRepository.Single(u => u.EuserEmail.Equals(user.EuserEmail), "");
So i have 2 questions what does the function PerformInclusions() do and can someone give me an example what the includeproperties would be in the call to the single() function.
Thanks in advance
as #Andrei says this is allowing you to use familiar EF include syntax to eager load navigation properties. The other alternative for nav property eager loading is via lambdas as below:
public T GetBy(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includes)
{
var result = GetAll();
if (includes.Any())
{
foreach (var include in includes)
{
result = result.Include(include);
}
}
return result.FirstOrDefault(predicate);
}
If you have an entity with a navigation property as below:
public class Test{
public int Id {get;set;}
public SomethingElse Thing {get;set;}
}
_repo.Single(t => t.Id == 1, ""); OR _repo.GetBy(t => t.Id == 1);
will return an entity where Thing is null
_repo.Single(t => t.Id == 1, "Thing"); OR _repo.GetBy(t => t.Id == 1, t=>t.Thing);
will return an entity with Thing populated via the foreign key
For some more details on what Navigation properties in EF are check out my blog http://blog.staticvoid.co.nz/2012/7/17/entity_framework-navigation_property_basics_with_code_first

Making a function to be called within Linq functions

Can I make a function to be called within Linq functions using EF?
for example
int GetEmployeeStatus(string empID)
{
using (DB = new db())
{
var status = db.EmployeeStatus
.Where(e => e.EmpID == empID && e.StatusEndDate == null)
.Select(e => e.Status)
.SingleOrDefault();
return status;
}
}
Now is there a way to use the function above anywhere in my applciation in some way like this:
....
var empList = db.Employees
.Where(e => e.CostCenterID == 123
&& GetEmployeeStatus(e.EmpID) == 1);
....
I do not want to write the creteria for finding the employee status over and over again, is there a way to do that or something similar in concept?
One more thing, I know the way I write the function up will cause a database trip for every row, I hope there is way to avoid that and just to embed the query within the Linq so it will be called once.
You can use an extension function:
public static class DbContextExtensions
{
public static IQueryable<Employee> WhereX(this IQueryable<Employee> queryable, int id)
{
return queryable.Where(e => e.CostCenterID == 123);
}
}

Categories

Resources