LINQ: Cannot get more than two Where clauses to work - linq

I cannot get the following to work properly. I want to search my database using more than 2 conditions where it will be possible to search only on one, two or all three, thus narrowing the search. At the moment, I can only get it to work with one condition at a time, and it only evaluates the first condition when having more than 3.
How can I Include more than 2 conditions? I have tried several proposed solutions from other sources, that uses if statements and where clauses, but so far none of them have worked.
This is my controller
public IActionResult Index(string searchString, int searchProject, string searchTitel)
{
var Projektdokumenter = from p in _context.ProjekterDokutypeDokuundertype
.Include(p => p.Dokumenttype)
.Include(p => p.Dokuundertype)
.Include(p => p.Forfatter)
.Include(p => p.P)
.Include(p => p.Sprog)
select p;
if (!String.IsNullOrEmpty(searchString) ^ searchProject != null ^ !String.IsNullOrEmpty(searchTitel))
{
if (!String.IsNullOrEmpty(searchString))
{
var c = Projektdokumenter.Where(p => p.Forfatter.Initialer.Contains(searchString)).ToList();
return View(c);
}
{
if (searchProject != 0)
{
var d = Projektdokumenter.Where(p => p.P.ProjektId.Equals(searchProject)).ToList();
return View(d);
}
{
if(!String.IsNullOrEmpty(searchTitel))
{
var f = Projektdokumenter.Where(p => p.Titel.Contains(searchTitel)).ToList();
return View(f);
}
}
}
}
var e = Projektdokumenter.Where(c.Contains(searchString) && p.P.ProjektId.Equals(searchProject) && p.Titel.Contains(searchTitel)).ToList();
return View(e);
}

IQueryable can be combined, so just write in native way:
{
var query = _context.ProjekterDokutypeDokuundertype
.Include(p => p.Dokumenttype)
.Include(p => p.Dokuundertype)
.Include(p => p.Forfatter)
.Include(p => p.P)
.Include(p => p.Sprog)
.AsQueryable();
if (!string.IsNullOrEmpty(searchString))
{
query = query.Where(p => p.Forfatter.Initialer.Contains(searchString));
}
if (searchProject != 0)
{
query = query.Where(p => p.P.ProjektId == searchProject);
}
if (!string.IsNullOrEmpty(searchTitel))
{
query = query.Where(p => p.Titel.Contains(searchTitel))
}
return View(query.ToList());
}

Related

ElasticSearch NEST ObjectInitializer syntax to fluent syntax translation not working

Given this ObjectInitializer NEST query
var mustClauses = new List<QueryContainer>
{
new QueryStringQuery
{
Query = queryFilter.Query,
Lenient = true
},
new MatchQuery
{
Field = new Field("status"),
Query = queryFilter.Status,
Lenient = true,
Operator = Operator.And
},
new DateRangeQuery
{
Field = new Field("timeSent"),
LessThanOrEqualTo = now,
GreaterThanOrEqualTo = GetDateTimeFor(queryFilter.TimeCriteria, now)
}
};
return client.SearchAsync<Ingestion.Entities.ElasticSearch.MessageData>(sd => sd.Query(q => q.Bool(b => b.Must(mustClauses.ToArray())))
.Sort(x => x.Descending(b => b.TimeSent))
.From(from)
.Size(pageSize));
which works, and outputs the following query to my Visual Studio Output window:
{"from":0,"query":{"bool":{"must":[{"match":{"status":{"lenient":true,"operator":"and","query":"Success"}}},{"range":{"timeSent":{"gte":"2021-12-14T03:39:26.5126419Z","lte":"2021-12-21T03:39:26.5126419Z"}}}]}},"size":20,"sort":[{"timeSent":{"order":"desc"}}]}
I am trying to convert it to fluent query syntax like this:
return client.SearchAsync<Ingestion.Entities.ElasticSearch.MessageData>(sd => sd.Query(q => q.Bool(b => b.Must(
mu => mu
.QueryString(qs => qs
.Query(queryFilter.Query)
.Lenient(true)),
mu =>
{
if (string.IsNullOrEmpty(queryFilter.Status))
return null;
return mu
.Match(ma => ma.Field(f => f.Status == queryFilter.Status)
.Lenient(true)
.Operator(Operator.And));
},
mu =>
{
if (queryFilter.TimeCriteria == TimeCriteria.All)
return null;
return mu.DateRange(dr => dr
.Field(f => f.TimeSent)
.LessThanOrEquals(now)
.GreaterThanOrEquals(GetDateTimeFor(queryFilter.TimeCriteria, now)));
})))
.Sort(x => x.Descending(b => b.TimeSent))
.From(from)
.Size(pageSize));
and it's not working. When I run this query, the Match query on that Status field does not appear in the NEST output.
Any guidance/help would be appreciated.
The status lambda expression should be
mu =>
{
if (string.IsNullOrEmpty(queryFilter.Status))
return null;
return mu
.Match(ma => ma.Field(f => f.Status)
.Query(queryFilter.Status)
.Lenient(true)
.Operator(Operator.And));
},

Cannot compose correct Linq query for a class having collection properties - getting EfCore5 translation errors

I have flat collection of the following data (IQueryable<Article>) which was obtained by querying DB:
ArticleId
LanguageName
ArticleText
ExtraData1
ExtraData2
1
English
EngText1
Something1
Something2
1
English
EngText2
Another1
Another2
1
French
FraText1
Blabla1
2
English
EngText2
Ololo1
Blabla2
2
German
GerText1
Naturlisch2
Now I need to fill the IQueryable<AgregatedArticle>: the idea is grouping by ArticleId and putting repeating data into nested list:
public class AgregatedArticle {
public int ArticleId { get; set; }
public List<Data> ArticleTexts { get; set; }
public class Data {
public string LanguageName { get; set; }
public string ArticleText { get; set; }
}
}
Unfortunately, I cannot make it: I am getting various EfCore5 translation errors and don't know: if it's me or EfCore5 bugs or limitations. I wasted 3 days trying different approaches. Please help - I was unable to find suitable examples in Internet. The problem comes up when I try to fill ArticleTexts property.
Here is the simplified example:
private async Task<IQueryable<LawArticleAggregated>> GetLawArticlesGroupedById(DbSet<LawArticleDetail> dbSet, string userContentLangRestriction = null)
{
var dbContext = await GetDbContextAsync();
var articlesQuery =
(from articleIds in dbSet.Select(x => x.ArticleId).Distinct()
from articlesPerId in dbSet
.Where(x => x.ArticleId == articleIds.ArticleId)
join askedL in dbContext.Langs
.Where(l => l.LanguageCode == userContentLangRestriction)
on
articlesPerId.LanguageCode
equals
askedL.StringValue
into askedLanguages
from askedLawLanguage in askedLanguages.DefaultIfEmpty()
join fallbackL in dbContext.Langs
.Where(l => l.LanguageCode == CoreConstants.LanguageCodes.English)
on
articlesPerId.LanguageCode
equals
fallbackL.StringValue
into fallbackLanguages
from fallbackLanguage in fallbackLanguages.DefaultIfEmpty()
select new
{
ArticleId = articleIds.ArticleId,
ArticleText = articlesPerId.ArticleText,
LanguageName = askedLawLanguage.ShortName ?? fallbackLanguage.ShortName
})
.OrderBy(x => x.ArticleId).ThenBy(x => x.LanguageName).ThenBy(x => x.ArticleText);
await articlesQuery.LoadAsync();
var aggregatedArticleData = articlesQuery.Select(x => new
{
ArticleId = x.ArticleId,
ArticleText = x.ArticleText,
LanguageName = x.LanguageName
});
var aggregatedArticles = articlesQuery.Select(x => x.ArticleId).Distinct().Select(x => new ArticleAggregated
{
ArticleId = x.ArticleId,
ArticleTexts = aggregatedArticleData.Where(a => a.ArticleId == x.ArticleId)
.Select(x => new LawArticleAggregated.Data
{
ArticleText = x.ArticleText,
LanguageName = x.LanguageName
}).ToList()
});
return aggregatedArticles;
}
For this specific code the exception is as follows:
Unable to translate collection subquery in projection since the parent
query doesn't project key columns of all of it's tables which are
required to generate results on client side. This can happen when
trying to correlate on keyless entity or when using 'Distinct' or
'GroupBy' operations without projecting all of the key columns.
I think I have reverse engineered your query. Big difference that we cannot return IQueryable from this funcrtio, but prepared IEnumerable. So if you have pagination later, better to pass page info into function parameters.
private async Task<IEnumerable<LawArticleAggregated>> GetLawArticlesGroupedById(DbSet<LawArticleDetail> dbSet, string userContentLangRestriction = null)
{
var dbContext = await GetDbContextAsync();
var articlesQuery =
from article in dbSet
from askedLawLanguage in dbContext.Langs
.Where(askedLawLanguage => askedLawLanguage.LanguageCode == userContentLangRestriction && article.LanguageCode == askedLawLanguage.StringValue)
.DefaultIfEmpty()
from fallbackLanguage in dbContext.Langs
.Where(fallbackLanguage => fallbackLanguage.LanguageCode == CoreConstants.LanguageCodes.English && article.LanguageCode == fallbackLanguage.StringValue)
.DefaultIfEmpty()
select new
{
ArticleId = article.ArticleId,
ArticleText = article.ArticleText,
LanguageName = askedLawLanguage.ShortName ?? fallbackLanguage.ShortName
};
articlesQuery = articlesQuery
.OrderBy(x => x.ArticleId)
.ThenBy(x => x.LanguageName)
.ThenBy(x => x.ArticleText);
var loaded = await articlesQuery.ToListAsync();
// group on the client side
var aggregatedArticles = loaded.GroupBy(x => x.ArticleId)
.Select(g => new ArticleAggregated
{
ArticleId = g.Key,
ArticleTexts = g.Select(x => new LawArticleAggregated.Data
{
ArticleText = x.ArticleText,
LanguageName = x.LanguageName
}).ToList()
});
return aggregatedArticles;
}
I ended up with the following implementation (I show it "as is", without simplification from the first message to demonstrate the approach, slightly modified from the initial variant to use proper paging):
private async Task<IEnumerable<LawArticleAggregated>> GetLawArticlesGroupedByIdListAsync(
DbSet<LawArticleDetail> dbSet,
Expression<Func<IQueryable<LawArticleDetail>, IQueryable<LawArticleDetail>>> filterFunc,
int skipCount,
int maxResultCount,
string userContentLangRestriction = null,
CancellationToken cancellationToken = default
)
{
var dbContext = await GetDbContextAsync();
var articlesQuery =
(from articleIds in filterFunc.Compile().Invoke(dbSet).Select(x => new { x.TenantId, x.LawArticleId })
.Distinct().OrderBy(x => x.TenantId).OrderByDescending(x => x.LawArticleId).Skip(skipCount).Take(maxResultCount)
from articlesPerId in dbSet
.Where(x => x.TenantId == articleIds.TenantId && x.LawArticleId == articleIds.LawArticleId)
join askedL in dbContext.FixCodeValues
.Where(l =>
l.DomainId == CoreConstants.Domains.CENTRAL_TOOLS
&& l.CodeName == CoreConstants.FieldTypes.LANGUAGE
&& l.LanguageCode == userContentLangRestriction)
on
articlesPerId.LanguageCode
equals
askedL.StringValue
into askedLanguages
from askedLawLanguage in askedLanguages.DefaultIfEmpty()
join fallbackL in dbContext.FixCodeValues
.Where(l =>
l.DomainId == CoreConstants.Domains.CENTRAL_TOOLS
&& l.CodeName == CoreConstants.FieldTypes.LANGUAGE
&& l.LanguageCode == CoreConstants.LanguageCodes.English)
on
articlesPerId.LanguageCode
equals
fallbackL.StringValue
into fallbackLanguages
from fallbackLanguage in fallbackLanguages.DefaultIfEmpty()
select new
{
TenantId = articleIds.TenantId,
LawArticleId = articleIds.LawArticleId,
Shortcut = articlesPerId.Shortcut,
ArticleText = articlesPerId.ArticleText,
LanguageName = askedLawLanguage.ShortName ?? fallbackLanguage.ShortName
})
.OrderBy(x => x.TenantId).ThenByDescending(x => x.LawArticleId).ThenBy(x => x.Shortcut).ThenBy(x => x.LanguageName).ThenBy(x => x.ArticleText);
var articleList = await articlesQuery.ToListAsync(cancellationToken);
var aggregatedArticles = articleList.GroupBy(x => new { x.TenantId, x.LawArticleId })
.Select(g => new LawArticleAggregated
{
TenantId = g.Key.TenantId,
LawArticleId = g.Key.LawArticleId,
ArticleTexts = g.Select(x => new LawArticleAggregated.Data
{
Shortcut = x.Shortcut,
ArticleText = x.ArticleText,
LanguageName = x.LanguageName
}).ToList()
});
return aggregatedArticles;
}
private async Task<long> GetLawArticlesGroupedByIdCountAsync(
DbSet<LawArticleDetail> dbSet,
Expression<Func<IQueryable<LawArticleDetail>, IQueryable<LawArticleDetail>>> filterFunc,
CancellationToken cancellationToken = default
)
{
return await filterFunc.Compile().Invoke(dbSet).GroupBy(x => new { x.TenantId, x.LawArticleId }).LongCountAsync(cancellationToken);
}

Dynamic Bool Query for NEST (ElasticSearch)

Good day:
I'm trying to achieve a dynamic Bool Query. Example...
QueryContainer addressQuery = null;
QueryContainer typeQuery = null;
BoolQueryDescriptor <Facility> boolQuery = new BoolQueryDescriptor<Facility>();
QueryContainerDescriptor<Facility> sh = new QueryContainerDescriptor<Facility>();
SearchDescriptor<Facility> mainSh = new SearchDescriptor<Facility>();
if (search.searchTerm != null)
{
addressQuery = sh.Term(f => f.Address, search.searchTerm) ||
sh.Match(m => m.Field(f => f.Address).Query(search.searchTerm) ) ||
sh.Term(f => f.ZipCode, search.searchTerm) ||
sh.Match(m => m.Field(f => f.State).Query(search.searchTerm));
boolQuery = boolQuery.Should(b => addressQuery);
}
if (search.type != null) {
typeQuery = sh.Term(f => f.Types.First(), search.type);
boolQuery = boolQuery.Must(b => typeQuery && addressQuery);
}
if (boolQuery != null)
{
mainSh = mainSh.Query(q => q.Bool(b => boolQuery));
request = await this._elasticClient.SearchAsync<Facility>(s => mainSh.Size(search.size));
}
This query is turning into a OR between AddressQuery and TypeQuery however, I'd like to achieve an And condition between both query. Effectively making the query dynamic. Is this possible? Example: dynamic addressQuery && typeQuery.
Thanks.
Fire it out..you can && or || query containers...sorter like:
queryContainer1=queryContainer1 && queryContainer2
Then .bool(b => b(queryContainer1)).

IQueryable Sort not working

What is wrong with the code below with regards to sorting? The sort code is hit, but the sorting is never applied to the results.
var results = new List<Location>();
var county = context.boc_County.Where(x => x.Description.Contains(phrase.ToLower())).ToList();
results.AddRange(_mapper.MapCountyFromDb(county));
var town = context.boc_Town.Where(x => x.Description.Contains(phrase.ToLower())).ToList();
results.AddRange(_mapper.MapTownFromDb(town));
if (orderBy == "Identifier")
{
if (direction == "ASC")
results = results.OrderBy(x => x.Identifier);
else
results = results.OrderByDescending(x => x.Identifier);
}
if (orderBy == "Type")
{
if (direction == "ASC")
results = results.OrderBy(x => x.LocationType.ToString());
else
results = results.OrderByDescending(x => x.LocationType.ToString());
}
if (orderBy == "Description")
{
if (direction == "ASC")
results = results.OrderBy(x => x.Description);
else
results = results.OrderByDescending(x => x.Description);
}
var model = new LocationSearchResult()
{
Locations = query.Skip(page * pageSize).Take(pageSize),
TotalCount = query.Count()
};
return model;
OrderBy and OrderByDescending don't change the caller, they return new IQueryable/IEnuemrable instead. You have to assign it back to another (or the same) variable. Otherwise calling them has no sense.
Because you're using List<T> you have to add additional ToList() call to make it compile and work:
if (orderBy == "Identifier")
{
if (direction == "ASC")
results = results.OrderBy(x => x.Identifier).ToList();
else
results = results.OrderByDescending(x => x.Identifier).ToList();
}
// (...)
or you can use List<T>.Sort instead:
if (orderBy == "Identifier")
{
if (direction == "ASC")
results.Sort((x1, x2) => x1.Compare(x2));
else
results.Sort((x1, x2) => x2.Compare(x1));
}

Ordering in LINQ Statement Ineffective

I'm having trouble getting some ordering working in a LINQ statement I've been working on. Here is the code:
public static List<Contact> GetAllTheCusts(string fName,
string lName,
string middleName,
int? customerId,
string sort,
int pageIndex,
int pageSize)
{
AWEntities entities = Common.GetContext();
int skipCount = pageSize * pageIndex;
var contacts = entities.Contacts
.Include("Individuals.Customer")
.Where(c => customerId.HasValue
? c.Individuals.Any(i => i.CustomerID == customerId.Value)
: c.Individuals.Any(i => i.Customer.CustomerID == i.CustomerID))
.Where(c => string.IsNullOrEmpty(fName) || c.FirstName.Contains(fName))
.Where(c => string.IsNullOrEmpty(lName) || c.LastName.Contains(lName))
.Where(c => string.IsNullOrEmpty(middleName) || c.MiddleName.Contains(middleName));
.Select(c => c);
IOrderedQueryable<Contact> contactsOrdered = contacts.OrderByDescending(o => o.ContactID);;
return contactsOrdered.Skip(skipCount).Take(pageSize).ToList();
}
For some reason, the OrderByDescending method is not performing its role. Can anybody help please.
Cheers
Thought I would post the final working code here. This has successfully converted the stored procedure to LINQ-to-Entities:
public static List<Contact> GetCustomersWithContactDetails(string fName, string lName, string middleName, int? customerId,
string sort, int pageIndex, int pageSize, out int count)
{
AWEntities entities = Common.GetContext();
int skipCount = pageSize * pageIndex;
var contacts = entities.Contacts
.Include("Individuals.Customer")
.Where(c => customerId.HasValue
? c.Individuals.Any(i => i.CustomerID == customerId.Value)
: c.Individuals.Any(i => i.Customer.CustomerID == i.CustomerID))
.Where(c => string.IsNullOrEmpty(fName) || c.FirstName.Contains(fName))
.Where(c => string.IsNullOrEmpty(lName) || c.LastName.Contains(lName))
.Where(c => string.IsNullOrEmpty(middleName) || c.MiddleName.Contains(middleName));
// Get ordering based on the "sort" parameter
var contactsOrdered = GetOrdering(sort, contacts);
count = contacts.Count();
return contactsOrdered.Skip(skipCount).Take(pageSize).ToList();
}
private static IOrderedQueryable<Contact> GetOrdering(string sort, IQueryable<Contact> contacts)
{
IOrderedQueryable<Contact> contactsOrdered;
if (string.IsNullOrEmpty(sort))
{
contactsOrdered = contacts.OrderBy(o => o.LastName);
}
else
{
SortDirection sortDirection = sort.EndsWith("ASC", StringComparison.Ordinal)
? SortDirection.asc
: SortDirection.desc;
switch (sort.Substring(0, sort.IndexOf(' ')))
{
case "ContactID":
{
if (sortDirection == SortDirection.asc)
{
contactsOrdered = contacts.OrderBy(o => o.ContactID);
}
else
{
contactsOrdered = contacts.OrderByDescending(o => o.ContactID);
}
break;
}
case "LastName":
{
if (sortDirection == SortDirection.asc)
{
contactsOrdered = contacts.OrderBy(o => o.LastName);
}
else
{
contactsOrdered = contacts.OrderByDescending(o => o.LastName);
}
break;
}
case "FirstName":
{
if (sortDirection == SortDirection.asc)
{
contactsOrdered = contacts.OrderBy(o => o.FirstName);
}
else
{
contactsOrdered = contacts.OrderByDescending(o => o.FirstName);
}
break;
}
case "MiddleName":
{
if (sortDirection == SortDirection.asc)
{
contactsOrdered = contacts.OrderBy(o => o.MiddleName);
}
else
{
contactsOrdered = contacts.OrderByDescending(o => o.MiddleName);
}
break;
}
default:
{
contactsOrdered = contacts.OrderBy(o => o.ContactID);
break;
}
}
}
return contactsOrdered;
}
Cheers

Resources