How to avoid duplication with QueryOver - c#

I have two methods and don't like the duplication:
public Order LatestOrderOver(decimal amount)
{
return session.QueryOver<Order>()
.Where(o => o.Amount > amount)
.OrderBy(sr => sr.CompleteUtcTime).Desc
.Take(1)
.SingleOrDefault<Order>();
}
public Order LatestAmericanOrderOver(decimal amount)
{
return session.QueryOver<Order>()
.Where(o => o.Amount > amount && o.Country == "USA")
.OrderBy(sr => sr.CompleteUtcTime).Desc
.Take(1)
.SingleOrDefault<Order>();
}
What is the best way to avoid duplication when you have similar criteria (in the Where clause) used in the QueryOver and similar options at the end?

If you are using Linq To Objects, you can just refactor out the delegate:
private Order LatestOrderOver(Func<Order, bool> f) {
return
session.QueryOver<Order>()
.Where(f)
.OrderBy(sr => sr.CompleteUtcTime).Desc
.Take(1)
.SingleOrDefault<Order>();
}
public Order LatestOrderOver(decimal amount) {
return LatestOrderOver(o => o.Amount > amount);
}
public Order LatestAmericanOrderOver(decimal amount) {
return LatestOrderOver(o => o.Amount > amount && o.Country == "USA");
}
Otherwise it might work with just changing Func<> to Expression<>, but I don't have much experience with that.

Beside Guffa's suggestion, what do you think about an extension method?
public static class QueryOverExtensions
{
public static Order LastOrder(this IQueryOver<Order, Order> query)
{
return query
.Where(o => o.Amount > amount)
.OrderBy(sr => sr.CompleteUtcTime).Desc
.Take(1)
.SingleOrDefault<Order>();
}
// Other query over extension methods
}
Then you could write your methods as:
public Order LatestOrderOver(decimal amount)
{
return session.QueryOver<Order>()
.LastOrder();
}
public Order LatestAmericanOrderOver()
{
return session.QueryOver<Order>()
.Where(o => o.Country == "USA")
.LastOrder();
}

Related

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.

How compare classes with generic types in C#

How can I write the below code with is T
public IList<IElement> OfClass(Type type)
{
return list
.Where(o => o.GetType() == type)
.ToList();
}
Something like this:
public IList<IEtabsElement> OfClass(....)
{
return list
.Where(o => o is ...)
.ToList();
}
UPDATE
This is my solution, so far. Is it okay?
public IList<IElement> OfClass<T>()
{
return list
.Where(o => o is T)
.ToList();
}
You can create a generic method instead:
public IList<T> OfClass<T>()
{
return list
.Where(o => o.GetType() == typeof(T))
.ToList();
}
This would work, but is the same as the existing method OfType, for example:
var myvehicles = new List<Vehicle> { new Car(), new Bike()};
var mycars = myvehicles.OfType<Car>();

Sorting in Linq by constructing the linq statement as a string?

The user have the option to sort by price or by date listed. Both can be sorted in ascending or descending. They both can be used or one of them.
What is the best practical method to use in such a situation ?
Can I make 1 linq statement and replace the words "ascending"/"descending" or remove them from the statement by modifying a string ? (in other words, construct the linq statement like sql?)
Instead of relying on strings, you can use the SortOrder enum:
public MyCollection OrderedByPrice(SortOrder sortOrder)
{
if (sortOrder == SortOrder.Ascending)
{
return new MyCollection(this.OrderBy(x => x.Price));
}
else
{
return new MyCollection(this.OrderByDescending(x => x.Price));
}
}
As per your comments, if you want to order by both you could use ThenBy
public MyCollection OrderedByPriceThenByDate(SortOrder sortOrder)
{
if (sortOrder == SortOrder.Ascending)
{
return new MyCollection(this.OrderBy(x => x.Price)
.ThenBy(y => y.Date));
}
else
{
return new MyCollection(this.OrderByDescending(x => x.Price)
.ThenByDescending(y => y.Date));
}
}
you can also build an expression to do it
public IEnumerable<T> ExecuteSort<T>(
IQueryable<T> src, Expression<Func<T,bool>> predicate, SortOrder sortOrder)
{
if (sortOrder == SortOrder.Ascending)
{
return src.OrderBy(predicate));
}
else
{
return src..OrderByDescending(predicate));
}
}
ExecuteSort(src, v => v.Price, ortOrder.Ascending);

Best way to order a List<type> by field C# Lambda

I have the following code that works fine, but I was wondering if there is any better way to order the select depending on what the user choose.
public List<TB_PRODUTO> GetAll(int ID_Empresa, String Order)
{
if(Order.Equals("COD_HERBALIFE"))
return Ent.TB_PRODUTO.Where(x => x.ID_EMPRESA == ID_Empresa).Select(x => x).OrderBy(x => x.COD_HERBALIFE).ToList();
else if (Order.Equals("DESCRICAO"))
return Ent.TB_PRODUTO.Where(x => x.ID_EMPRESA == ID_Empresa).Select(x => x).OrderBy(x => x.DESCRICAO).ToList();
else
return Ent.TB_PRODUTO.Where(x => x.ID_EMPRESA == ID_Empresa).Select(x => x).OrderBy(x => x.PRECO).ToList();
}
Thanks in advance.
private static object GetPropertyValue(object obj, string propertyName)
{
return obj.GetType().GetProperty(propertyName).GetValue(obj, null);
}
public List<TB_PRODUTO> GetAll(int ID_Empresa, String Order)
{
return Ent.TB_PRODUTO.Where(x => x.ID_EMPRESA == ID_Empresa).Select(x => x).OrderBy(x => GetPropertyValue(x, Order)).ToList();
}
GetPropertyValue will return the property matching the Order string, provided the string exactly matches the property.

Linq Optional ORing (Method Syntax)

How do I combine these two methods into one?
public override Expression<Func<Attachment, bool>> MatchingCriteria
{
get { return a => a.Activity.Parent.ActivityUsers.Any(x => (x.User.Id == id)); }
}
and
public override Expression<Func<Attachment, bool>> MatchingCriteria
{
get { return a => a.Activity.ActivityUsers.Any(x => (x.User.Id == id)); }
}
notably, the idea is that I want to check the root record for links to a certain User. But, a may be a root or may be a child (one decendant/level only; not recursive). I want an ORing, so to speak, of these two Linq expressions.
Maybe something like:
public override Expression<Func<Attachment, bool>> MatchingCriteria
{
get
{
return a => a.Activity.Parent.ActivityUsers
.Concat(a.Activity.ActivityUsers).Any(x => (x.User.Id == id));
}
}
Or since you're only worried about a single level deep you could probably use:
public override Expression<Func<Attachment, bool>> MatchingCriteria
{
get
{
return a => a.Activity.Parent.Any(a2 =>
a2.ActivityUsers.Any(x =>
(x.User.Id == id) || x.Activity.ActivityUsers.Any(y =>
y.User.Id == id)));
}
}
This option is probably more appropriate.
public override Expression<Func<Attachment, bool>> MatchingCriteria
{
get { return a => a.Activity.Parent.ActivityUsers.Any(x => (x.User.Id == id))
|| a.Activity.ActivityUsers.Any(x => (x.User.Id == id));
}
}

Categories

Resources