Making a function to be called within Linq functions - c#

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);
}
}

Related

Entity Framework Core IEnumerable async

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.

Cannot access variable on mapping (C#)

I have a method on the back end, that gets values related to a foreign key of the table.
Those foreign keys can be nullable, but one of those keys always will have value.
Here is method
public async Task<ListResultDto<QuoteListDto>> GeQuotesTabData(int? landlordId, int? agentId,
int? propertyTenantId)
{
if (landlordId.HasValue)
{
var query = _quoteRepository.GetAll()
.Where(x => x.LandlordId == landlordId);
}
if (agentId.HasValue)
{
var query = _quoteRepository.GetAll()
.Where(x => x.AgentId == agentId);
}
if (propertyTenantId.HasValue)
{
var query = _quoteRepository.GetAll()
.Where(x => x.PropertyTenantId == propertyTenantId);
}
return new ListResultDto<QuoteListDto>(await query.ProjectTo<QuoteListDto>(ObjectMapper)
.OrderBy(x => x.Id).ToListAsync());
}
At this row, I get an error Cannot resolve symbol query
return new ListResultDto<QuoteListDto>(await query.ProjectTo<QuoteListDto>(ObjectMapper)
.OrderBy(x => x.Id).ToListAsync());
How do I need to rewrite my method?
Declare and initialise your variable. Additionally I would re-write you method like so:
public async Task<ListResultDto<QuoteListDto>> GeQuotesTabData(int? landlordId, int? agentId,
int? propertyTenantId)
{
var query = _quoteRepository.GetAll();
if (landlordId.HasValue)
{
query = query.Where(x => x.LandlordId == landlordId);
}
if (agentId.HasValue)
{
query = query.Where(x => x.AgentId == agentId);
}
if (propertyTenantId.HasValue)
{
query = query .Where(x => x.PropertyTenantId == propertyTenantId);
}
return new ListResultDto<QuoteListDto>(await query.ProjectTo<QuoteListDto>(ObjectMapper)
.OrderBy(x => x.Id).ToListAsync());
}
Also taken from this answer, you can create a WhereIf extension to clean up the if statements.
public static IQueryable<TSource> WhereIf<TSource>(
this IQueryable<TSource> source,
bool condition,
Expression<Func<TSource, bool>> predicate)
{
if (condition)
return source.Where(predicate);
else
return source;
}
Making your code look like this:
public async Task<ListResultDto<QuoteListDto>> GeQuotesTabData(int? landlordId, int? agentId,
int? propertyTenantId)
{
var list = await _quoteRepository.GetAll()
.WhereIf(landlordId.HasValue, x => x.LandlordId == landlordId)
.WhereIf(agentId.HasValue, x => x.AgentId == agentId)
.WhereIf(propertyTenantId.HasValue, x => x.PropertyTenantId == propertyTenantId)
.ProjectTo<QuoteListDto>(ObjectMapper)
.OrderBy(x => x.Id)
.ToListAsync();
return new ListResultDto<QuoteListDto>(list);
}
Your problem is variable scope. When you define a variable it is only visible in the scope you define it in.
You define three different query variables in a local scope. None of them are accessible where you try to use it.
You need to define it before using it, something like this:
public async Task<ListResultDto<QuoteListDto>> GeQuotesTabData(int? landlordId, int? agentId,
int? propertyTenantId)
{
IQueryable<Quote> query = null;
if (landlordId.HasValue)
{
query = _quoteRepository.GetAll().Where(x => x.LandlordId == landlordId);
}
if (agentId.HasValue)
{
query = _quoteRepository.GetAll().Where(x => x.AgentId == agentId);
}
if (propertyTenantId.HasValue)
{
query = _quoteRepository.GetAll().Where(x => x.PropertyTenantId == propertyTenantId);
}
return new ListResultDto<QuoteListDto>(await query.ProjectTo<QuoteListDto>(ObjectMapper)
.OrderBy(x => x.Id).ToListAsync());
}
Of course all of your queries should be of the same type. Otherwise you will have to define and execute them in the local scopes.
You should probably also add some error handling of the case where query is null, when you try to use it.

c# - query nested type with LINQ

I have a model that looks like the following:
public class MyType{
public string Id {get;set;}
public string Name{get;set;}
public List<MyType> Children{get;set;}
}
and in my data I have just two level data, meaning my objects will look like:
{
MyType{"1","firstParent",
{
MyType{"2","firstChild",null},
MyType{"3","secondChild",null}}
},
MyType{"4","secondParent",
{
MyType{"5","firstChild",null},
MyType{"6","secondChild",null}}
}
}
How do I query to get MyType object with a specific Id where it might be a parent or child?
The following will return only parents.
collection.FirstOrDefault(c => c.id==id)
You can use Any with a recursive local function to find objects on any level (your data structure would seem to indicate a deeper level is possible)
bool hasIdOrChildren(MyType t, string localId)
{
return t.Id == localId || (t.Children != null && t.Children.Any(o => hasIdOrChildren(o, localId)));
}
collection.FirstOrDefault(c => hasIdOrChildren(c, id));
Or using pre C#7 syntax:
Func<MyType, string, bool> hasIdOrChildren = null;
hasIdOrChildren = (MyType t, string localId) =>
{
return t.Id == localId || (t.Children != null && t.Children.Any(o => hasIdOrChildren(o, localId)));
};
collection.FirstOrDefault(c => hasIdOrChildren(c, id));
If you are only interested in one level, you can drop the reclusiveness:
collection.FirstOrDefault(c => c.Id == id || (c.Children != null && c.Children.Any(o => o.Id == id)));
Edit
The code above gives the parent if any child has the id, you can also flatten the whole tree structure using SelectMany also with a recursive function:
IEnumerable<MyType> flattenTree(MyType t)
{
if(t.Children == null)
{
return new[] { t };
}
return new[] { t }
.Concat(t.Children.SelectMany(flattenTree));
};
collection
.SelectMany(flattenTree)
.FirstOrDefault(c => c.Id == id);
This method can be useful for any type of processing where you need to flatten the tree.
You could build a list of all MyType including children and then query on it like this :
collection.SelectMany(c => c.Children).Concat(collection).Where(c => c.id == id)
I think you're looking for
var flattenedList = IEnumerable.SelectMany(i => i.ItemsInList);
This flattens the list and gives back one list with all items in it.
In your case you need to select
collection.SelectMany(c => c.Type).Concat(collection).Where(item => item.Id == 5);
MSDN
You still got the childs in your joined parents here, but you can still erase them or ignore them.
I think, you should flatten collection using SelectMany method, then use FirstOrDefault to get element by id:
MyType selected = collection
.SelectMany(obj => new MyType[] {obj, obj.NestedList})
.FirstOrDefault(obj => obj.id == id);

Retrieving entities with related tables c# using REST API2

I am building a web API that is suppose to populate data from a linked child table using a where clause.
I have attempted using include() with where() as per eager loading but without success.
public IQueryable<Market> getAllActive()
{
return db.Markets.Where(c => c.IsActive == true).Include(d => d.TravelCentres.Where(e => e.IsActive == true));
}
On researching, there are recommendations that I use explicit loading but it keeps error about the need to cast the data type. I am lost of ideas at the moment and will appreciate any help. Here is my code:
private TravelCentresDbContext db = new TravelCentresDbContext();
public IQueryable<Market> getAllActive()
{
//return db.Markets.Where(c => c.IsActive == true).Include(d => d.TravelCentres);
var result = db.Markets
.Where(c => c.IsActive == true)
.Select(p => new
{
Market = p.MarketId,
TravelCentres = p.TravelCentres.Where(x => x.IsActive == true)
});
return (IQueryable<Market>)result;
}
I get this exception message Unable to cast object of type
'System.Data.Entity.Infrastructure.DbQuery1[<>f__AnonymousType42[System.String,System.Collections.Generic.IEnumerable1[TravelCentres.Models.TravelCentre]]]'
to type 'System.Linq.IQueryable1[TravelCentres.Models.Market]'.
Blockquote
result is not an IQuerytable<Market>, it's an IQueryable of an anonymous type with properties Market and TravelCenters. So (IQueryable<Market>)result is an invalid cast. It would be advisable to create a model with Market and TravelCenters properties and then return that.
public class MyModel
{
public int MarketId { get; set; }
public IEnumerable<TravelCentre> TravelCentres { get; set; }
}
.
var result = db.Markets
.Where(c => c.IsActive == true)
.Select(p => new MyModel()
{
Market = p.MarketId,
TravelCentres = p.TravelCentres.Where(x => x.IsActive == true)
});
return (IQueryable<MyModel>)result;

Fluent NHibernate Where on Empty String

I'm applying a .Where()-restriction on an IQueryOver<T,T> in FluentNH, as such:
.Where(x => x.Col1 == null || x.Col1 == "");
Which generates the following SQL:
WHERE (Col1 IS NULL OR Col1 = NULL)
How can I make NH understand that empty string means empty string?
You can write your Where clause like this:
.Where(Restrictions.On<ClassType>(obj => obj.Col1).IsNull ||
Restrictions.On<ClassType>(obj => obj.Col1).IsLike(#""))
Alternatively, if you're doing this on several queries you should consider creating a query extension:
public static class QueryExtention {
public static IQueryOver<E, F> WhereStringIsNullOrEmpty<E, F>(this IQueryOver<E, F> query, Expression<Func<E, object>> expression) {
var property = Projections.Property(expression);
var criteria = Restrictions.Or(Restrictions.IsNull(property),
Restrictions.Eq(property, string.Empty));
return query.Where(criteria);
}
}
Then you should be able to create something like:
.QueryOver<ClassType>()
.WhereStringIsNullOrEmpty(obj => obj.Col1)

Categories

Resources