Ordering in LINQ Statement Ineffective - linq

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

Related

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

How to fix the wait operation is timed out? Works some time but is not working finely

I m a learner of MVC and has very limited knowledge. This is working but too slow.
Here are my few questions.
1.Does using the controller instead of the API degrade the performance?
2.Are there any cons to implementing an individual search for each column?
3.What is bad practice in this code? How can I correct them?
4.Table compounds have more than 200k rows. how can I make it as fast as possible with Ajaxified search, sortable, individual search for each column and pagination?
[OutputCache(Duration = 10, VaryByParam = "none")]
public ActionResult AjaxCompound(JQueryDataTableParamModel param)
{
IQueryable<Compound> allCompounds = _context.Compounds.Include(p => p.Oil);
IEnumerable<Compound> filteredCompounds;
if (!string.IsNullOrEmpty(param.sSearch))
{
filteredCompounds = allCompounds
.Where(c => c.Oil.CommonName.Contains(param.sSearch)
//||
//c.Oil.BotanicalName.Contains(param.sSearch)
||
c.Name.Contains(param.sSearch)
||
c.RI.Contains(param.sSearch)
||
c.MolecularWeight.Contains(param.sSearch)
||
c.MolecularFormula.Contains(param.sSearch)
||
c.AreaPercent.Contains(param.sSearch)
);
}
else
{
filteredCompounds = allCompounds;
}
var sortColumnIndex = Convert.ToInt32(Request["iSortCol_0"]);
Func<Compound, string> orderingFunction = (c => sortColumnIndex == 1 ? c.Oil.CommonName :
//sortColumnIndex == 2 ? c.Oil.BotanicalName :
sortColumnIndex == 2 ? c.Name :
sortColumnIndex == 3 ? c.RI :
sortColumnIndex == 4 ? c.MolecularFormula :
sortColumnIndex == 5 ? c.MolecularWeight :
c.AreaPercent);
var commonFilter = Convert.ToString(Request["sSearch_1"]);
//var botanicalFilter = Convert.ToString(Request["sSearch_2"]);
var name = Convert.ToString(Request["sSearch_2"]);
var rI = Convert.ToString(Request["sSearch_3"]);
var mW = Convert.ToString(Request["sSearch_4"]);
var mF = Convert.ToString(Request["sSearch_5"]);
var areaPercent = Convert.ToString(Request["sSearch_6"]);
if (!string.IsNullOrEmpty(commonFilter))
{
filteredCompounds = filteredCompounds.Where(c => c.Oil.CommonName.Contains(commonFilter));
}
//if (!string.IsNullOrEmpty(botanicalFilter))
//{
// filteredCompounds = filteredCompounds.Where(c => c.Oil.BotanicalName.Contains(botanicalFilter));
//}
if (!string.IsNullOrEmpty(name))
{
filteredCompounds = filteredCompounds.Where(c => c.Name.Contains(name));
}
if (!string.IsNullOrEmpty(rI))
{
filteredCompounds = filteredCompounds.Where(c => c.RI.Contains(rI));
}
if (!string.IsNullOrEmpty(mF))
{
filteredCompounds = filteredCompounds.Where(c => c.MolecularFormula.Contains(mF));
}
if (!string.IsNullOrEmpty(mW))
{
filteredCompounds = filteredCompounds.Where(c => c.MolecularWeight.Contains(mW));
}
if (!string.IsNullOrEmpty(areaPercent))
{
filteredCompounds = filteredCompounds.Where(c => c.AreaPercent.Contains(areaPercent));
}
var sortDirection = Request["sSortDir_0"];
if (sortDirection == "asc")
filteredCompounds = filteredCompounds.OrderBy(orderingFunction);
else
filteredCompounds = filteredCompounds.OrderByDescending(orderingFunction);
var displayedCompounds = filteredCompounds
.Skip(param.iDisplayStart)
.Take(param.iDisplayLength);
var result = from c in displayedCompounds
select new[] { Convert.ToString(c.PaperId), c.Oil.CommonName, /*c.Oil.BotanicalName,*/ c.Name, c.RI, c.MolecularWeight, c.MolecularFormula, c.AreaPercent };
return Json(new
{
sEcho = param.sEcho,
iTotalRecords = allCompounds.Count(),
iTotalDisplayRecords = filteredCompounds.Count(),
aaData = result
},
JsonRequestBehavior.AllowGet);
}

how to get count new message found as per enquiry/ticket with two way conditions

i 'm working with crm application. i developed it finally i'm getting confused how i detect new messages as per flag must have display and message not have to true.
here is my db schema for understanding problem.
here is my constructed query to get new message count :
int new_messages = 0;
foreach (var enquiry in db.Enquiries.Where(i => (i.ForwardTo.Equals(userid) || i.AttendBy.Equals(userid))).ToList())
{
if (enquiry != null)
{
bool IsIns = true;
foreach (var flag in db.Flags.Where(f => f.Enquiry_History_id.Equals(enquiry.Ref_no) && f.User_id.Equals(userid)).Select(f => new { IsDisplay = f.IsDisplay }).ToList())
{
if (flag != null)
{
if (flag.IsDisplay == false)
{
IsIns = false;
}
}
}
if (IsIns == true)
{
foreach (var message in db.Messages.Where(m => m.Enquiry_History_id.Equals(enquiry.Ref_no) && m.User_id.Equals(userid)).Select(m => new { IsRead = m.IsRead }).ToList())
{
if (message.IsRead == true)
{
//do another stuff
}
else
{
new_messages++;
}
}
}
}
}
lbl_msg.Text = new_messages.ToString();
here i have to take each enquiry that have been AttendBy or ForwardBy to user and check it first it should be display is not false and after that check as per each message should not have IsRead to true. i'm trying with my best way but this not gives me out put as i want.
i have an idea just insert two values one for true which generate message and second for false to froward. and at finding out only which have IsRead to false.
something like this way :
using (DataClassesDataContext db = new DataClassesDataContext())
{
string userid = db.Users.Where(u => u.Username.Equals((String)Session["Username"])).Select(u => u.Ref_no).SingleOrDefault().ToString();
bool IsIns = true;
foreach (var enquiry in db.Enquiries.Where(i => i.AttendBy.Equals(userid) || i.ForwardTo.Equals(userid)).ToList())
{
if (enquiry != null)
{
foreach (var flag in db.Flags.Where(f => f.Enquiry_History_id.Equals(enquiry.Ref_no) && f.User_id.Equals(userid)).Select(f => new { IsDisplay = f.IsDisplay }).ToList())
{
if (flag.IsDisplay == false)
{
IsIns = false;
}
if (IsIns == true)
{
foreach (var message in db.Messages.Where(m => m.Enquiry_History_id.Equals(enquiry.Ref_no)).ToList())
{
if (message != null)
{
bool IsIns1 = false;
foreach (var messageflag in db.MessageFlags.Where(mf => mf.MessageId.Equals(message.Id) && mf.UserId.Equals(userid)).Select(mf => new
{
IsRead = mf.IsRead,
User_id = mf.UserId,
Message = message.body,
CreatedDate = message.Created_date
}).ToList())
{
if (messageflag.IsRead == false)
{
IsIns1 = true;
}
if (IsIns1 == true)
{
DataRow dr = dt.NewRow();
dr["Ref_no"] = enquiry.Ref_no.ToString();
dr["Name"] = db.Users.Where(u => u.Ref_no.Equals(messageflag.User_id)).Select(u => u.FirstName + ' ' + u.LastName).SingleOrDefault().ToString();
dr["UserId"] = db.Users.Where(u => u.Ref_no.Equals(messageflag.User_id)).Select(u => u.Ref_no).SingleOrDefault().ToString();
dr["Message"] = messageflag.Message.ToString();
dr["CreatedDate"] = messageflag.CreatedDate.ToString();
dt.Rows.Add(dr);
}
}
}
}
}
}
}
}
myDataSet.Tables.Add(dt);
lbl_count_messages.Text = myDataSet.Tables[0].Rows.Count.ToString();
if (myDataSet.Tables[0].Rows.Count == 0)
{
message_alert.Visible = false;
}
Repeater_Messages.DataSource = myDataSet;
Repeater_Messages.DataBind();
}

OrderByDescending linq query

I am trying to show deal.type as decending order by date but I cannot get the result to show in Descending order.
[Authorize(Roles = "admin")]
[HttpGet]
[Queryable(PageSize = 10)]
public HttpResponseMessage Get([FromUri] Query query)
{
var data = db.database_ICs.AsQueryable();
if (query.price_type != null)
{
data = data.Where(c => c.Cover == query.price_type);
}
if (query.deal_type != null)
{
data = data.Where(c => c.Type == query.deal_type)
.OrderByDescending(c => c.UploadDate);
}
if (query.startDate != null)
{
data = data.Where(c => c.UploadDate >= query.startDate);
}
if (query.endDate != null)
{
data = data.Where(c => c.UploadDate <= query.endDate);
}
if (!data.Any())
{
var message = string.Format("No data was found");
return Request.CreateErrorResponse(HttpStatusCode.NotFound, message);
}
return Request.CreateResponse(HttpStatusCode.OK, data);
}
Any help would be very much appreciated.
Please, try change this:
[Queryable(PageSize = 10)]
For this:
[Queryable(PageSize = 10, EnsureStableOrdering = false)]
More informations here.
Hope that helps!
See ya!

MVC 3 - How to know if query returns 0 rows

Probably pretty easy, but I can't get it sorted out. I want to redirect the user to an error page if the requested id doesn't exist in the database. My code:
public ActionResult Details(int id)
{
DetailsAdViewModel DAVM = new DetailsAdViewModel();
DAVM.Ad = db.Ads.Include("Images").Where(a => a.AdId == id).First();
DAVM.FirstImage = db.Images.Where(a => a.AdId == id).OrderBy(a => a.ImageId).Take(1);
// make sure the ad isn't deleted or that it really exists
if (DAVM.Ad == null)
{
return RedirectToAction("ShowError", "Error", new { errorCode = "adDeleted" });
}
return View(DAVM);
}
This doesn't work, and there is an server error if I enter a false id.
Use .FirstOrDefault() instead of .First(). This will return null if not record is found instead of throwing an exception:
DAVM.Ad = db.Ads.Include("Images").FirstOrDefault(a => a.AdId == id);
Now you can check if DAVM.Ad is null:
if (DAVM.Ad == null)
{
return RedirectToAction("ShowError", "Error", new { errorCode = "adDeleted" });
}
Found a solution:
public ActionResult Details(int id)
{
if (db.Ads.Any(a => a.AdId == id))
{
DetailsAdViewModel DAVM = new DetailsAdViewModel();
DAVM.Ad = db.Ads.Include("Images").Where(a => a.AdId == id).First();
DAVM.FirstImage = db.Images.Where(a => a.AdId == id).OrderBy(a => a.ImageId).Take(1);
return View(DAVM);
}
else
{
return RedirectToAction("ShowError", "Error", new { errorCode = "adDeleted" });
}
}

Resources