Total results of paged projected nhibernate query with aggregates - c#

I'm dynamically building a nhibernate projected query that needs to implement paging. Something like...
var projections = Projections.ProjectionList();
foreach (var p in projection.Projections)
{
IProjection newProjection = null;
switch (p.AggregateFunc)
{
case AggregateFuncTypeEnum.GroupProperty:
newProjection = Projections.GroupProperty(p.Path);
break;
case AggregateFuncTypeEnum.Sum:
newProjection = Projections.Sum(p.Path);
break;
default:
newProjection = Projections.Property(p.Path);
break;
}
projections.Add(newProjection, p.Name);
}
criteria.SetProjection(projections).SetResultTransformer(new AliasToBeanResultTransformer(projectionType));
I can get the first 15 results like so
criteria.SetFirstResult(0);
criteria.SetMaxResults(15);
var results = criteria.List();
But I also need to send another query to get the total number of records but so far I've failed to figure this out. The projection still needs to be applied i.e. if the results are grouped by 'code' with a sum of 'cost' then 100 records might return 20 rows, and it's the 20 I'm interested in.
How do I get the total number of records that will be returned? Thanks

maybe this:
var rowcount = CriteriaTransformer.Clone(criteria);
var goupprojections = Projections.ProjectionList();
var projections = Projections.ProjectionList();
foreach (var p in projection.Projections)
{
IProjection newProjection = null;
switch (p.AggregateFunc)
{
case AggregateFuncTypeEnum.GroupProperty:
newProjection = Projections.GroupProperty(p.Path);
goupprojections.Add(Projections.GroupProperty(p.Path), p.Name);
break;
case AggregateFuncTypeEnum.Sum:
newProjection = Projections.Sum(p.Path);
break;
default:
newProjection = Projections.Property(p.Path);
break;
}
projections.Add(newProjection, p.Name);
}
criteria.SetProjection(projections).SetResultTransformer(new AliasToBeanResultTransformer(projectionType));
if (goupprojections.Aliases.Length == 0)
{
rowcount.SetProjection(Projections.RowCount())
}
else
{
rowcount.SetProjection(Projections.Count(goupprojections))
}
var results = criteria.Future();
var count = rowcount.FutureValue<int>();

Related

ASP.NET - Help to understand this block of code

I'm trying to understand a block of code from a school project, but I'm not understanding what this does.
I know that this is a controller method and is passing some information to the view?
Can you help me?
public ActionResult Index(int ? page, string sort) {
using(istecEntities ctx = new istecEntities()) {
var Persons = ctx.Persons.ToList()
.Select(p => new Person {
Num = p.num,
Name = p.Name,
Area = p.Area,
Grades = p.Grades as List <Grade>
}).ToList<Person>();
switch (sort) {
case "numdesc":
Persons = Persons.OrderByDescending(p =>p.Num).ToList<Person>();
break;
case "NameDesc":
Persons = Persons.OrderByDescending(p =>p.Name).ToList<Person>();
break;
case "NameAsc":
Persons = Persons.OrderBy(p =>p.Name).ToList<Person>();
break;
default:
Persons = Persons.OrderBy(p =>p.Num).ToList<Person>();
break;
}
ViewBag.sortnum = (String.IsNullOrEmpty(sort)) ?
"numdesc": "";
ViewBag.sortName = (sort == "NameDesc") ?
"NameAsc": "NameDesc";
ViewBag.sort = sort;
int size = 3;
int pagenumber = (page ?? 1);
return View(Persons.ToPagedList<Person>(pagenumber, size));
}
}

Quickest way to iterate through two lists and see if Values match

I have two separate lists I generate from different databases. I then compare those two by looking for a matching ID, if the ID matches I assign some variables. The issue I am running into is the way I iterate and compare is by using find method in LINQ. This seems very slow when dealing with lots of data. Here is my code
private void GetSerialNumber(POSHData poshData, MerchantTerminal foundTerminal)
{
var result = poshData.poshSerialNumbers.Find(x => x.termid == foundTerminal.terminalID);
if (result != null)
{
foundTerminal.terminalSerialNumber = result.terminalSerialNumber;
foundTerminal.pinPadSerialNumber = result.pinPadSerialNumber;
foundTerminal.appName = result.appName;
foundTerminal.appVersion = result.appVersion;
foundTerminal.pINPadRecord = result.pinPadRecord;
foundTerminal.terminalRecord = result.terminalRecord;
foundTerminal.LastTransactionDateTime = result.LastTransaction;
if (foundTerminal.pinPadSerialNumber.Length == 0)
{
foundTerminal.pinPadSerialNumber = result.pinPadSerialNumber;
}
switch (result.SettlementType)
{
case "2":
foundTerminal.PABX = "Journal";
break;
case "3":
foundTerminal.PABX = "Batch Close Required";
break;
default:
foundTerminal.PABX = "Unknown";
break;
}
foundTerminal.AdviceText = result.tranAdjust;
foundTerminal.AuthOnly = result.tranAuthOnly;
}
}
What would be the fastest and efficient way to do this? Thanks for the help!
var result = poshData.poshSerialNumbers
.Where(x => x.Key == foundTerminal.terminalID)
.FirstOrDefault();

build linq query based off parameters

I'm trying to create a query based on parameters from my datatable. I'm trying to perform something like the following except I keep getting errors and having to basically recreate the entire linq query in each case statement with one parameter different ie. OrderBy or OrderByDecending.
Is there a way to build the query based on the parameters?
public JsonResult IndexData(DTParameterViewModel param)
{
IEnumerable<DataTableRowViewModel> rows = (from j in jobs select j)
.Skip(param.Start)
.Take(param.Length)
.AsEnumerable()
.Select(j => new DataTableRowViewModel
{
Id = j.Id,
ArrivalDate = j.ArrivalDate.ToString(Constants.DATE_FORMAT),
DueDate = j.DueDate?.ToString(Constants.DATE_TIME_FORMAT),
Contact = j.Contact,
Priority = j.Priority.GetDisplayName(),
JobType = j.JobType.GetDisplayName(),
SapDate = j.SapDate.ToString()
});
foreach(DTOrder order in param.Order)
{
ascDesc = order.Dir;
switch (order.Column)
{
case 0:
orderCol = 0;
colName = "Id";
if (ascDesc == "desc")
{
rows = (from j in jobs select j)
.OrderByDescending(j => j.Id);
}
else
{
rows = (from j in jobs select j)
.OrderBy(j => j.Id)
}
break;
case 1:
orderCol = 1;
colName = "ArrivalDate";
if (ascDesc == "desc")
{
rows = (from j in jobs select j)
.OrderByDescending(j => j.ArrivalDate)
}
else
{
rows = (from j in jobs select j)
.OrderBy(j => j.ArrivalDate)
}
break;
}
There are more complex ways of doing it, but I find this the easiest to read, especially for LINQ beginners:
var first=true;
foreach(DTOrder order in param.Order)
{
switch(order.Column)
{
case 0:
if (first)
{
rows=(order.Dir=="asc")?rows.OrderBy(r=>r.Id):rows.OrderByDescending(r=>r.Id);
} else {
rows=(order.Dir=="asc")?rows.ThenBy(r=>r.Id):rows.ThenByDescending(r=>r.Id);
}
break;
case 1:
...
}
first=false;
}
Normally however, you would want to do your Take and Skip after the order, so you would do something like this:
public JsonResult IndexData(DTParameterViewModel param)
{
// Start with the base query
var rows = jobs.AsQueryable();
// Order the query
var first=true;
foreach(DTOrder order in param.Order)
{
switch(order.Column)
{
case 0:
if (first)
{
rows=(order.Dir=="asc")?rows.OrderBy(r=>r.Id):rows.OrderByDescending(r=>r.Id);
} else {
rows=(order.Dir=="asc")?rows.ThenBy(r=>r.Id):rows.ThenByDescending(r=>r.Id);
}
break;
case 1:
...
}
first=false;
}
// Partition the query
rows=rows
.Skip(param.Start)
.Take(param.Length)
.AsEnumerable(); // Or place the AsEnumerable in the projection
// Project the query
var result=rows.Select(j => new DataTableRowViewModel
{
Id = j.Id,
ArrivalDate = j.ArrivalDate.ToString(Constants.DATE_FORMAT),
DueDate = j.DueDate?.ToString(Constants.DATE_TIME_FORMAT),
Contact = j.Contact,
Priority = j.Priority.GetDisplayName(),
JobType = j.JobType.GetDisplayName(),
SapDate = j.SapDate.ToString()
});
return result;
}
Further optimizing (assuming that jobs comes from a database), then you should do an intermediate projection to limit the columns that come back, changing:
// Partition the query
rows=rows
.Skip(param.Start)
.Take(param.Length)
.AsEnumerable(); // Or place the AsEnumerable in the projection
// Project the query
var result=rows.Select(j => new DataTableRowViewModel
to this:
// Partition the query
var result1=rows
.Skip(param.Start)
.Take(param.Length)
/* Selecting only the fields we need */
.Select(j=> new {
j.Id,
j.ArrivalDate,
j.DueDate,
j.Contact,
j.Priority,
j.JobType,
j.SapDate
})
.AsEnumerable(); // Or place the AsEnumerable in the projection
// Project the query
var result=result1.Select(j => new DataTableRowViewModel

how to sort on a field not stored within the db

I have an asp.net mvc site and I'm unable to sort on a field that is calculated when needed in the model.
private decimal _total = -1;
public decimal Total
{
get
{
if (_total < 0)
{
_total = get_total(TableId);
}
return _total;
}
}
private decimal get_total(int id)
{
....Many Calcs
}
I'm trying to sort on Total, but I get the error:
Additional information: The specified type member 'Total' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
Here is my actionlink:
#Html.ActionLink("By Total", "Index", new { sortOrder = ViewBag.Total, currentFilter = ViewBag.CurrentFilter }, new { #class = "btn btn-danger" })
I have found some similar issues, but I just can't figure out what how to sort by this.
And my controller. I tried to edit this down for clarity.
public ActionResult Index(string sortOrder)
{
ViewBag.CurrentSort = sortOrder;
ViewBag.Total = sortOrder == "total" ? "total_desc" : "total";
var records = from u in db.Records.Include(t => t.User).Where(t => t.Active == true)
select u;
switch (sortOrder)
{
case "total":
records = db.Records.OrderBy(u => u.Total).Where(t => t.Active == true);
break;
case "rating_desc":
records = db.Records.OrderByDescending(u => u.Total).Where(t => t.Active == true);
break;
default:
records = db.Records.OrderBy(u => u.Title).Where(t => t.Active == true);
break;
}
return View(records.ToList());
}
Try to call ToList() method before trying to order by this property as this cannot be translated to an SQL statement.
// I assume currently your query is something like this
DbContext.SomeEntity.Where(...).OrderBy(e => e.Total);
// After calling .ToList() you can sort your data in the memory (instead of in db)
DbContext.SomeEntity.Where(...).ToList().OrderBy(e => e.Total);
UPDATE:
The problem is that first you declare the records variable with this line:
var records = from u in db.Records.Include(t => t.User).Where(t => t.Active == true) select u;
Because of this the type of the records variable will be System.Linq.IQueryable<Project.Models.Record> and that's why in the switch case you "needed" to cast with .AsQueryable().
Additionally the initial value will be always overridden in the switch statement therefore it is totally unnecessary to initialize it as you do it currently.
What you should do:
public ActionResult Index(string sortOrder)
{
/* ViewBag things */
IEnumerable<Record> records =
db
.Records
.Include(record => record.User)
.Where(record => record.Active)
.ToList(); // At this point read data from db into memory
// Total property cannot be translated into an SQL statement.
// That's why we call it on memory objects instead of DB entities.
switch (sortOrder)
{
case "total":
records = records.OrderBy(record => record.Total);
break;
case "rating_desc":
records = records.OrderByDescending(record => record.Total);
break;
default:
records = records.OrderBy(record => record.Title);
break;
}
return View(records.ToList());
}
I needed to cast my query as IQueryable, so here is the updated switch:
switch (sortOrder)
{
case "total":
records = db.Records.Where(t => t.Active == true).AsQueryable().OrderBy(u => u.Total));
break;
case "rating_desc":
records = db.Records.Where(t => t.Active == true).AsQueryable.OrderByDescending(u => u.Total).;
break;
default:
records = db.Records.Where(t => t.Active == true).AsQueryable.OrderBy(u => u.Title).;
break;
}

Syntax to execute code block inside Linq query?

Here's some code that (obviously) doesn't compile:
var q = from x in myAnonymousTypeCollection
select new {
x.ID,
CalcField = {
switch(x.SomeField) {
case 1:
return Math.Sqrt(x.Field1);
case 2:
return Math.Pow(x.Field2, 2);
default:
return x.Field3;
}
}
};
You get the picture; I'm trying to calculate CalcField in a completely different way, depending on what the value of SomeField is. I can't use a Func<> (or can I?), because the input type is anonymous. So what's the right syntax to get this to work?
First off, I usually prefer the method chain syntax over the query syntax for Linq. With that you can do this easily.
var q = myAnonymousTypeCollection
.Select(x =>
{
object calcField;
switch(x.SomeField)
{
case 1:
calcField = Math.Sqrt(x.Field1);
case 2:
calcField = Math.Pow(x.Field2, 2);
default:
calcField = x.Field3;
return new
{
x.ID,
CalcField = calcField
};
});
Without using method chains, you need either a method or an Func. Let's assume a Func
//replace these with actual types if you can.
Func<dynamic, dynamic> calculateField =
x =>
{
switch(x.SomeField) {
case 1:
return Math.Sqrt(x.Field1);
case 2:
return Math.Pow(x.Field2, 2);
default:
return x.Field3;
}
var q = from x in myAnonymousTypeCollection
select new { x.Id, CalcField = calculateField(x) };
Note: I didn't write this in an IDE, so please excuse any simple errors.
Here is the MSDN for dynamic. However, I have found that once you need to start passing anonymous types around, it is best to make an actual class.
You could wrap your anonymous function as a (self-executing) Func<> delegate. This assumes you know the return type.
var q = from x in myAnonymousTypeCollection
select new {
ID = x.ID,
CalcField = new Func<double>( () => {
switch(x.SomeField) {
case 1:
return Math.Sqrt(x.Field1);
case 2:
return Math.Pow(x.Field2, 2);
default:
return x.Field3;
}
} )()
};
You could quite easily move the switch logic out into another function like so:
private static T GetReturnValue<T>(myClass x)
{
switch (x)
{
case 1:
return Math.Sqrt(x.Field1);
break;
case 2:
return Math.Pow(x.Field2,
2);
break;
default:
return x.Field3;
break;
}
}
And then you just need to pass your object to that function to get back the value you want:
var q = from x in myAnonymousTypeCollection
select new
{
ID = x.ID,
CalcField = GetReturnValue(x)
};

Categories