C
C#•5mo ago
UltraWelfare

How to structure query functions in services

I have the following function in my service layer csharp: PS: A category can have List<Item>
public async Task<List<Category>> GetCategories()
{
await using var db = await contextFactory.CreateDbContextAsync();
return await db.Categories
.AsNoTracking()
.OrderBy(c => c.Sort)
.ToListAsync();
}
public async Task<List<Category>> GetCategories()
{
await using var db = await contextFactory.CreateDbContextAsync();
return await db.Categories
.AsNoTracking()
.OrderBy(c => c.Sort)
.ToListAsync();
}
Sometimes I need GetCategories() to just be the Category without any navigation properties included. But also sometimes I need GetCategories() to also include the Items navigation property... I'm thinking of having two functions with two separate dtos (one has only the category, the other one has the category + the items). But I was wondering if there's a better way to do this
16 Replies
Angius
Angius•5mo ago
Definitely sounds like a job for two different methods, selecting into two different DTOs
Aslan Akbey
Aslan Akbey•5mo ago
but you can use it only with ef core 🙂
UltraWelfare
UltraWelfare•5mo ago
Yeah I know about Include the question is how to structure it I could do a public async Task<List<Category>> GetCategories(bool includeItems)
Aslan Akbey
Aslan Akbey•5mo ago
Task<IQueryable<T>> GetListAsync(Expression<Func<T, bool>>? predicate = null,
Func<IQueryable<T>, IOrderedQueryable<T>>? orderBy = null,
Func<IQueryable<T>, IIncludableQueryable<T, object>>? include = null,
bool enableTracking = true,
CancellationToken cancellationToken = default);
Task<IQueryable<T>> GetListAsync(Expression<Func<T, bool>>? predicate = null,
Func<IQueryable<T>, IOrderedQueryable<T>>? orderBy = null,
Func<IQueryable<T>, IIncludableQueryable<T, object>>? include = null,
bool enableTracking = true,
CancellationToken cancellationToken = default);
UltraWelfare
UltraWelfare•5mo ago
I don't think I'd like to make the consumer choose the includes...
Aslan Akbey
Aslan Akbey•5mo ago
public async Task<IQueryable<T>> GetListAsync(Expression<Func<T, bool>>? predicate = null, Func<IQueryable<T>, IOrderedQueryable<T>>? orderBy = null, Func<IQueryable<T>, IIncludableQueryable<T, object>>? include = null, bool enableTracking = true, CancellationToken cancellationToken = default) { IQueryable<T> queryable = Table.AsQueryable(); if (!enableTracking) queryable = queryable.AsNoTracking(); if (include != null) queryable = include(queryable); if (predicate != null) queryable = queryable.Where(predicate); if (orderBy != null) return await Task.FromResult(orderBy(queryable)); return await Task.FromResult(queryable); }
UltraWelfare
UltraWelfare•5mo ago
Otherwise why bother having a service layer for the query? you could straight up get the context at the controller or in the desktop gui function and do everything there
Aslan Akbey
Aslan Akbey•5mo ago
public async Task<IQueryable<T>> GetListAsync(Expression<Func<T, bool>>? predicate = null, Func<IQueryable<T>, IOrderedQueryable<T>>? orderBy = null, Func<IQueryable<T>, IIncludableQueryable<T, object>>? include = null, bool enableTracking = true, CancellationToken cancellationToken = default)
{
IQueryable<T> queryable = Table.AsQueryable();
if (!enableTracking) queryable = queryable.AsNoTracking();
if (include != null) queryable = include(queryable);
if (predicate != null) queryable = queryable.Where(predicate);
if (orderBy != null)
return await Task.FromResult(orderBy(queryable));
return await Task.FromResult(queryable);
}
public async Task<IQueryable<T>> GetListAsync(Expression<Func<T, bool>>? predicate = null, Func<IQueryable<T>, IOrderedQueryable<T>>? orderBy = null, Func<IQueryable<T>, IIncludableQueryable<T, object>>? include = null, bool enableTracking = true, CancellationToken cancellationToken = default)
{
IQueryable<T> queryable = Table.AsQueryable();
if (!enableTracking) queryable = queryable.AsNoTracking();
if (include != null) queryable = include(queryable);
if (predicate != null) queryable = queryable.Where(predicate);
if (orderBy != null)
return await Task.FromResult(orderBy(queryable));
return await Task.FromResult(queryable);
}
UltraWelfare
UltraWelfare•5mo ago
I appreciate your input but I'm a bit sceptical of making the consumer of the function be mindful about Includes
Aslan Akbey
Aslan Akbey•5mo ago
You should define this in the data access or persistence layer and use it only in the service layer. What do you see as a problem? The only problem is that you cannot use it in Dapper or a similar orm other than EF Core.
Angius
Angius•5mo ago
public async Task<List<CategoryDto>> GetCategories()
{
await using var db = await contextFactory.CreateDbContextAsync();
return await db.Categories
.OrderBy(c => c.Sort)
.Select(c => new CategoryDto {
Id = c.Id,
Name = c.Name
})
.ToListAsync();
}
public async Task<List<CategoryDto>> GetCategories()
{
await using var db = await contextFactory.CreateDbContextAsync();
return await db.Categories
.OrderBy(c => c.Sort)
.Select(c => new CategoryDto {
Id = c.Id,
Name = c.Name
})
.ToListAsync();
}
public async Task<List<CategoryWithStuffDto>> GetCategoriesWithStuff()
{
await using var db = await contextFactory.CreateDbContextAsync();
return await db.Categories
.OrderBy(c => c.Sort)
.Select(c => new CategoryWithStuffDto {
Id = c.Id,
Name = c.Name,
Stuff = c.Stuff.Select(s => new StuffDto {
Name = s.Name,
CreatedAt = c.CreatedAt
})
})
.ToListAsync();
}
public async Task<List<CategoryWithStuffDto>> GetCategoriesWithStuff()
{
await using var db = await contextFactory.CreateDbContextAsync();
return await db.Categories
.OrderBy(c => c.Sort)
.Select(c => new CategoryWithStuffDto {
Id = c.Id,
Name = c.Name,
Stuff = c.Stuff.Select(s => new StuffDto {
Name = s.Name,
CreatedAt = c.CreatedAt
})
})
.ToListAsync();
}
That's how I'd do it
Aslan Akbey
Aslan Akbey•5mo ago
sorry, I misunderstood the question
Angius
Angius•5mo ago
Well, ideally I'd just inject the context into the service instead of using the factory, but I guess you're using it in Blazor or some such
Aslan Akbey
Aslan Akbey•5mo ago
I think factory is more performant I misunderstood the problem. Sorry, I just realized. I think you want to call the same method in two different ways in two different services in the Service Layer, is that correct?
UltraWelfare
UltraWelfare•5mo ago
Yep, blazor hybrid
Want results from more Discord servers?
Add your server
More Posts