ServiceStack ormLite chaning OrderBy - c#

I am trying to so the following:
var routines = con.Select<Table>(con.From<Table>().OrderBy(p => p.Field1).ThenBy(i => i.Field2));
The above works perfectly. But I want a rather more generic approach and parse a string like sort="field1,field2". I have the following code:
int sortFieldCount = 0;
var itemsq = con.From<Table>();
foreach (var name in orderByField.Split(',')) {
if(sortFieldCount == 0)
itemsq = sortOrderAscending ? itemsq.OrderBy(name) : itemsq.OrderByDescending(name);
else
itemsq = sortOrderAscending ? itemsq.ThenBy(name) : itemsq.ThenByDescending(name);
sortFieldCount++;
}
But the above code seems to overwrite the first OrderBy. Is there a solution to such a problem?
Thanks

Other ways you can perform multiple Order By's with ServiceStack.OrmLite include:
var orderByAnonType = db.Select(db.From<Track>().OrderBy(x => new { x.Album, x.Name }));
var orderByString = db.Select(db.From<Track>().OrderByFields("Album","Name"));
// Use `-` prefix to inverse sort order, e.g. Album Descending
var orderByString = db.Select(db.From<Track>().OrderByFields("-Album","Name"));
var orderByArray = db.Select(db.From<Track>().OrderBy(x => new[]{ "Album","Name" }));
So you could get a flexible OrderBy like AutoQuery's OrderBy with:
var fields = orderByString.Split(',', StringSplitOptions.RemoveEmptyEntries);
q.OrderByFields(fields);
Here's a live example of this you can play around with on gistlyn.com

There is a couple problems in the accepted answer I'd like to address.
First is the possibility of SQL injection attacks. ServiceStack does not fully validate what you pass in to the list of sort columns. While it will detect some of the more obvious attacks, you could still slip in things like calls to stored functions.
The second problem is descending sorts. It's not obvious from the API, but you can pass in "columnName DESC" rather than just "columnName". In fact, this is how it is able to support "Album,Name", it just passes it directly to SQL with the barest amount of validation.
public IList<Employee> SortBy(string lastName, string sortByColumnA, bool isDescendingA, string sortByColumnB, bool isDescendingB)
{
if (!Utilities.EmployeeColumnNames.Contains(sortByColumnA))
throw new ArgumentOutOfRangeException(nameof(sortByColumnA), "Unknown column " + sortByColumnA);
if (!Utilities.EmployeeColumnNames.Contains(sortByColumnB))
throw new ArgumentOutOfRangeException(nameof(sortByColumnB), "Unknown column " + sortByColumnB);
var sortDirectionA = isDescendingA ? " DESC " : "";
var sortDirectionB = isDescendingB ? " DESC " : "";
using (var db = _dbConnectionFactory.OpenDbConnection())
{
return db.Select(db.From<Employee>().Where(x => x.LastName == lastName)
.OrderBy(sortByColumnA + sortDirectionA + "," + sortByColumnB + sortDirectionB)).ToList();
}
}

Related

Await results from DataContext.ExecuteQuery

I have a method which is being called by an MVC controller which, in turn, is being called using Ajax in a web application. It used to look like this:
public static IEnumerable<DepartmentViewModel> GetDepartments()
{
DataContext db = new DataContext(ConfigurationHelper.DepartmentsConnectionString);
string sql = "SELECT DISTINCT RTRIM(DEP_CODE) AS [DepartmentCode], RTRIM(DEP_NAME) AS
[DepartmentName] " +
"FROM [departmentinfo].[dbo].[Dep_School_Faculty] " +
"WHERE [DEPARTMENT_IN_USE] = 'Y' AND [VALID] = 'Y' " +
"ORDER BY [DepartmentName]";
IEnumerable<DepartmentViewModel> departments = db.ExecuteQuery<DepartmentViewModel>(sql);
return departments;
}
The SQL query takes a few seconds to execute, which is why it is being called by Ajax after the rest of the web page loads, so the user can be getting on with other stuff while the department list loads. This works great.
Now, though, I need to modify the query slightly and do something with the results of the data on the back end before passing it to the controller, so I updated my method to look like this:
public static async Task<IEnumerable<AcademicAreaViewModel>> AllAcademicAreas()
{
IEnumerable<DepartmentSchoolFacultyModel> deptSchoolFaculty = await GetAllAcademicAreas();
using (DataContext db = new DataContext(ConfigurationHelper.DepartmentsConnectionString))
{
string sql = "SELECT DISTINCT RTRIM(DEP_CODE) AS [DepartmentCode], RTRIM(DEP_NAME) AS [DepartmentName], " +
"RTRIM(SCHOOL_CODE) AS [SchoolCode], RTRIM(SCHOOL_NAME) AS [SchoolName], " +
"RTRIM(FACULTY_CODE) AS [FacultyCode], RTRIM(FACULTY_NAME) AS [FacultyName] " +
"FROM [departmentinfo].[dbo].[Dep_School_Faculty] " +
"WHERE [DEPARTMENT_IN_USE] = 'Y' AND [VALID] = 'Y'";
deptSchoolFaculty = db.ExecuteQuery<DepartmentSchoolFacultyModel>(sql);
};
IEnumerable<AcademicAreaViewModel> departments = deptSchoolFaculty
.GroupBy(d => d.DepartmentCode)
.Select(g => new AcademicAreaViewModel()
{
Name = g.First().DepartmentName,
Code = "Department:" + g.First().DepartmentCode
})
.ToList();
IEnumerable<AcademicAreaViewModel> schools = deptSchoolFaculty
.GroupBy(d => d.SchoolCode)
.Select(g => new AcademicAreaViewModel()
{
Name = g.First().SchoolName,
Code = "School:" + g.First().SchoolCode
})
.ToList();
IEnumerable<AcademicAreaViewModel> academicAreas = departments.Concat(schools);
return academicAreas;
}
The problem I then hit was that the line beginning IEnumerable<AcademicAreaViewModel> departments = deptSchoolFaculty was throwing a null exception error because the deptSchoolFaculty variable hadn't yet been populated with data.
So, I thought, here's a typical use case for asynchronous programming, which just happens to my nemesis (no matter how much I read on the subject or implement it, it just never "clicks" in my head).
I refactored my code like this:
public static async Task<IEnumerable<AcademicAreaViewModel>> AllAcademicAreasAsync()
{
IEnumerable<DepartmentSchoolFacultyModel> deptSchoolFaculty = await GetAllAcademicAreasAsync();
IEnumerable<AcademicAreaViewModel> departments = deptSchoolFaculty
.GroupBy(d => d.DepartmentCode)
.Select(g => new AcademicAreaViewModel()
{
Name = g.First().DepartmentName,
Code = "Department:" + g.First().DepartmentCode
})
.ToList();
IEnumerable<AcademicAreaViewModel> schools = deptSchoolFaculty
.GroupBy(d => d.SchoolCode)
.Select(g => new AcademicAreaViewModel()
{
Name = g.First().SchoolName,
Code = "School:" + g.First().SchoolCode
})
.ToList();
IEnumerable<AcademicAreaViewModel> academicAreas = departments.Concat(schools);
return academicAreas;
}
private static Task<IEnumerable<DepartmentSchoolFacultyModel>> GetAllAcademicAreasAsync()
{
return Task.Run(() =>
{
IEnumerable<DepartmentSchoolFacultyModel> deptSchoolFaculty = new List<DepartmentSchoolFacultyModel>();
using (DataContext db = new DataContext(ConfigurationHelper.DepartmentsConnectionString))
{
string sql = "SELECT DISTINCT RTRIM(DEP_CODE) AS [DepartmentCode], RTRIM(DEP_NAME) AS [DepartmentName] " +
"FROM [departmentinfo].[dbo].[Dep_School_Faculty] " +
"WHERE [DEPARTMENT_IN_USE] = 'Y' AND [VALID] = 'Y' " +
"ORDER BY [DepartmentName]";
return db.ExecuteQuery<DepartmentSchoolFacultyModel>(sql);
}
});
}
Unfortunately, the same line throws the same null exception error, even though I'm using the await keyword to, I hoped, wait for the result of the SQL query before proceeding.
First of all, I don't understand why, in the original version of my code, the method seemed to wait for the SQL query to be complete before returning results (since results were always returned to my page), but now that I'm trying to work with the results in the same method it doesn't.
Secondly, whilst I suspect that the answer to my problem may be to use one of the asynchronous data methods instead of DataContext.ExecuteQuery, I would like to understand why what I've written doesn't work. Also, I much prefer the conciseness of ExecuteQuery for writing the results of the sql query straight into a C# model in one line.
You have problem with disposing DataContext before finishing enumeration of the result. It can be fixed by adding .ToList() call or extending scope for using.
deptSchoolFaculty = db.ExecuteQuery<DepartmentSchoolFacultyModel>(sql).ToList();

Removing white space from db result in LINQ query

I'm attempting to remove white space from a post code field in a database so that when I compare it to the users input I'm comparing both strings with no spaces in the post code at all so it shouldn't matter how the post code is entered.
This is my LINQ query with the replace function that doesn't appear to be working:
List<SchoolReferanceDTO> res = db.SchoolReferences.Where(x => x.SchoolReferencePostcode.Replace(" ", "").Contains(Postcode)).Select(x => new SchoolReferanceDTO()
{
SchoolReferenceSchoolId = x.SchoolReferenceSchoolId,
SchoolReferenceEstablishmentName = x.SchoolReferenceEstablishmentName,
SchoolReferenceStreet = x.SchoolReferenceStreet,
SchoolReferenceLocality = x.SchoolReferenceLocality,
SchoolReferenceAddress3 = x.SchoolReferenceAddress3,
SchoolReferenceTown = x.SchoolReferenceTown,
SchoolReferenceCounty = x.SchoolReferenceCounty,
SchoolReferencePostcode = x.SchoolReferencePostcode,
SchoolReferenceEmail = x.SchoolReferenceEmail
}).ToList();
And the string I'm comparing it to:
postcode = postcode.Replace(" ", string.Empty);
One approach is to drop Replace, and use LIKE instead. Since postal codes are generally short, you could transform the target code ST14BJ to %S%T%1%4%B%J% (demo), and use LIKE operator:
var postPattern = Regex.Replace(postcode, "(?<=.|^)(?=.|$)", "%");
List<SchoolReferanceDTO> res = db.SchoolReferences
.Where(x => SqlFunctions.PatIndex(postPattern, x.SchoolReferencePostcode) >= 0)
.Select(...);

Appending to the Select part of a query

We have a duplicate part of our LINQ METHOD syntax query. Here is a contrived example.
IQueryable<orders> query = _context.Set<orders>();
var result = query.Select(p => new{
REMAINING = p.qtyOrdered + p.alreadysent,
AWATING = p.qtyOrdered + p.alreadysent
}).ToList();
So we are trying to resolve the duplicate part by putting something in a method and then calling that and getting some sort of result. So something like this....
private IQueryable WhatsLeft()
{
IQueryable<orders> query = _context.Set<orders>();
return query.Select(p => new{p.qtyOrdered + p.alreadysent});
}
IQueryable<orders> query = _context.Set<orders>();
var result = query.Select(p => new{
REMAINING = WhatsLeft(),
AWATING = WhatsLeft()
}).ToList();
Is this at all possible and if so can anyone give me some brief advise on how I would achieve this.
Wouldn't you just simply pass the Order object to the new function directly?
private int Total(Order order)
{
return order.qtyOrdered + order.alreadySent;
}
IQueryable<orders> query = _context.Set<orders>();
var result = query.Select(p => new{
REMAINING = Total(p),
AWATING = Total(p)
}).ToList();
If I understand what you're after correctly. I can't remember off the top of my head how well Linq to sql etc can handle functions, interpreting them into SQL functions. Maybe you could give it a try.
Alternatively, to reduce the complexity of the function (to facilitate L2S conversion) you can make the parameters granular on the function such as:
private int Total(int left, int right)
{
return left + right;
}
Then make the call more like:
var result = query.Select(p => new{
REMAINING = Total(p.qtyOrdered, p.alreadysent),
AWATING = Total(p.qtyOrdered, p.alreadysent)
}).ToList();
UPDATE:
Have you thought about querying the calculation up front?
var result = query.Select(c => c.qtyOrdered + c.alreadysent).Select(p => new {
REMAINING = p,
AWAITING = p
}).ToList();

LINQ to Entities create dynamic field

I am trying to create a dynamic field using LINQ to Entities w/ EF5. Based on certain conditions (which I've encapsulated in a function) the dynamic field will be populated with different values.
I referenced the post here but when I run the code below, I get the follow error:
LINQ to Entities does not recognize the method 'System.String FormatName()' method, and this method cannot be translated into a store expression.
public static IEnumerable<dynamic> SelectAllCustomers()
{
using (var db = new DatabaseContext())
{
var query = db.Customer.Select(c => new
{
c.ID,
FullNameLastFirstMiddle = FormatName(c.First_Name, c.Middle_Name, c.Last_Name),
}
);
return query.ToList();
}
}
private static string FormatName(string first, string middle, string last)
{
//format name
if (!String.IsNullOrWhiteSpace(first))
{
if (!String.IsNullOrWhiteSpace(middle))
return last + ", " + first + " " + middle;
else
return last + ", " + first;
}
else
{
if (!String.IsNullOrWhiteSpace(middle))
return last + ", " + middle;
else
return last;
}
}
Any ideas on how best to dynamically build a field with sofisticated logic using LINQ to Entities?
String formatting like that doesn't really need to be translated to SQL and performed on the database side in the first place. Instead just query the database for the information that you need and then perform the string manipulation on the application side using LINQ to objects:
var query = db.Customer.Select(c => new
{
c.ID,
c.First_Name,
c.Middle_Name,
c.Last_Name,
})
.AsEnumerable()
.Select(c => new
{
c.ID,
FullNameLastFirstMiddle =
FormatName(c.First_Name, c.Middle_Name, c.Last_Name),
});

Is there a way to specify a field name programatically in linq?

I know about dynamic LINQ, but I'm wondering if there's a way to do this without building up a string of a query. I'd like to be able to use all of those nice built in LINQ calls like Contains, Count, Distinct, etc without having to worry about the SQL needed to create them. What I want to do is:
AdventureWorks2008R2Entities AWE = new AdventureWorks2008R2Entities();
var query = AWE.Employees.AsQueryable();
object FieldToQuery = ?;
if (textBox1.Text != "") query = query.Where(x => x.FieldToQuery.Contains(textBox1.Text));
Is something like this possible some how, or is it going against the fundamentals of LINQ?
Consider using PredicateBuilder from LINQKit as an easier alternative to building the expression tree by hand:
var predicate = PredicateBuilder.True<Employee>();
if (textBox1.Text != "") {
var txt = textBox1.Text;
predicate = predicate.And(e => e.Field1ToQuery.Contains(txt));
}
if (textBox2.Text != "") {
var txt = textBox2.Text;
predicate = predicate.And(e => e.Field2ToQuery.Contains(txt));
}
var AWE = new AdventureWorks2008R2Entities();
var query = AWE.Employees.AsExpandable().Where(predicate);
(The var txt is needed because under the covers these expressions are translated into the corresponding SQL (simplified):
SELECT *
FROM Employees
WHERE Field1ToQuery LIKE '%' + #p_1 + '%'
where the parameters are filled with the value of the variable. SQL Server has no knowledge of the textbox on the client-side -- the following SQL will fail:
SELECT *
FROM Employees
WHERE Field1ToQuery LIKE '%' + textBox1.Text + '%'
and (I suppose) passing the entire textbox as a parameter would be too complex.)
You can use
https://github.com/PoweredSoft/DynamicLinq
Nuget package
https://www.nuget.org/packages/PoweredSoft.DynamicLinq/
And do
queryable.Query(t => t.Contains("PropertyOrField", "search value"));
if you want to query multiple fields you can do
queryable.Query(t => t.Contains("FirstName", "Dav").OrContains("FirstName", "Jo"));

Categories

Resources