I am using the Dynamic Linq Library / Sample from Microsoft to do ordering on a list. So for example I have the following C# code:
myGrid.DataSource=repository.GetWidgetList()
.OrderBy(sortField + " " + sortDirection).ToList();
I have a case where my Object have a 0:1 relationship with another object, which has a property that might be displayed in the grid. When we try and sort this, it works fine so long as all my Widgets have this child. We are ordering by Child.Name for example. When Child is null however, we get the null reference exception.
I have some options here which I know I could select into an anonymous type and bind to that, I could also expose the Child.Name on the parent object and handle this via code (Which I don't like comprising my object model for this).
In an ideal world I'd like to update the library to handle this case. Before I dive into it, I'm wondering if anyone has ran across this or not and has a solution already?
Edit
Looks like I didn't explain well enough. I am using the Dynamic Linq Library which comes with the C# samples. This library adds some nice extensions that let you use a string inplace of a lambda expression So my code is actually something like this:
private void BindGrid(sortField,sortDirection)
{
this.grid.DataSource=....OrderBy("MyField ASC")....
}
Of course the string there is replaced with the parameters. But this allows us to change the sorting dynamically as the user clicks on a grid header. We don't have to if then else logic to handle all the permutations.
My solution as I documented bellow changes my nice clean method into:
private void BindGrid()
{
var sortField=this._sortField;
if (sortField=="Child.Name")
{
sortField="iif(Child==null,null,Child.Name)";
}
this.grid.DataSource=repository.GetWidgetList()
.OrderBy(sortField + " " + this._sortDirection)
.ToList();
}
And while this works, this now means I have to update this code as we add new fields or properties which we want to expose in the grid which are on a child object.
If I understand you correctly, I think you want this:
repository.GetParentObjects()
.OrderBy(p => p.Child == null ? "" : p.Child.Name);
LINQ will be able to generate SQL that mimics this expression.
On solution I've found which in my case is not ideal again would be to detect when the expression is going to access the child, to change the sort expression to be
iif(Child == null,null,Child.Name) ASC
Ideally this logic can be baked into the dynamic library, I'd rather not have to modify each grid all over the place to handle all the cases this will impact.
I had the same issue but the best solution I found was to make your code a little more generic by changing it into this:
private void BindGrid()
{
var sortField = this._sortField;
var splitted_sortField = this._sortField.Split(new char[]{'.'}, StringSplitOptions.RemoveEmptyEntries);
if (splitted_sortField.Length > 1)
{
sortField = "iif("+splitted_sortField[0]+"==null,null,"+sortField+")";
}
this.grid.DataSource = repository.GetWidgetList()
.OrderBy(sortField + " " + this._sortDirection)
.ToList();
}
Not perfect, won't like give access to childs of childs, but saves u from updating your code every time u get a new nullable child.
I don't really understand the problem (maybe because it is friday evening here already...), but can't you sort the list like this:
myGrid.DataSource=repository.GetWidgetList()
.OrderBy(w => w.SortField).ToList();
where SortField is the property you want to sort on.
This should work even when the value is null...
Sorry if it's maybe completely beside the point...
Related
I'm doing string based Dynamic Linq and need to apply a nested where clause.
The answer here gets me half way there. However, the columns in two tables have matching ids. I need to be able to reference via an alias or other, like below:
rolesCollection
.Where("AssignedUsers.Where(AssignedUsers.TypId == rolesCollection.TypId).Any()");
Any idea how to accomplish this? I don't have the ability to pass in an object, this has to be purely a string based solution in the context of a genericized API search method. This is just an example of what I need ... I don't have the ability to join or anything via code. I'm looking for the solution in the string based portion of the example code.
Do you want something like that;
rolesCollection = rolesCollection.Where(x => assignedUsers.Any(t => t.TypId == x.TypId)).ToList();
The AssignedUsers object should, in this theoretical example, have an AssignedUsers_Typ collection, and you can simply reference below, without having to reference the Typ columns from both tables.
rolesCollection.Where("AssignedUsers_Typ.Any()");
I was able to resolve my issue using this solution.
I have a PowerShell object and I want to filter the results.
public Collection<PSObject> Groups;
I have successfully achieved this using linq if I need to filter based on a direct member. But what if I want to filter based on an object 1 or 2 levels deep?
Here is the linq query to filter on a direct member:
var groupMembershipFilter = (dynamic)CsQ.Groups.Where(x => x.Members["AgentsByUri"].Value != null).ToList();
However I need to drill down another level. In PowerShell this would be easy:
$x.AgentsByUri.AbsoluteUri
I have tried all sorts but cant figure out how to make this work. So that you can better understand the object structure here are a couple of screen shots:
From the above screen shots you can see the "AgentsByUri" is a collection. Inside that collection I want to test if the property "AbsoluteUri" contains a certain string value.
The other thing I dont understand is why I have to use "Members" and cant just use "BaseObject" - this structure look far more similar to PowerShell and would be my preference if possible as it translates better to my PowerShell brain.
Excuse my terminology, I'm reasonably new to C#! Hopefully this makes sense :)
Any help or guidance would be much appreciated.
Try this:
var groupMembershipFilter = (dynamic)CsQ.Groups.Where(x => x.Members["AgentsByUri"].Any(x => x.AbsoluteUri == "url")).ToList();
I am using an Html.Grid of a set of objects which are being pulled from a database (via LINQ to SQL). Here is the Grid code (I have changed the data to something contrived). The page is strongly-typed to People.
<%=Html.Grid<People>(Model)
// Display chose solution information.
.Columns(column =>
{
column.For(person => person.Name);
column.For(person => person.Address.City);
}
)
%>
Within the database, I would have a People table and an Address table linked by a foreign key. (Again, this is contrived - just trying to describe the problem.)
I want to allow the user to generically sort the columns. The previous developer had hard-coded in the sorting within the controller, like so...
//
// GET: Results/Grid
public ActionResult Grid(int id, GridSortOptions sort)
{
if (sort == null)
{
sort = new GridSortOptions();
}
Solution solution = repository.GetSolution(id);
List<People> people = solution.People.ToList();
if (!string.IsNullOrEmpty(sort.Column))
{
people = solution.People.ToList().OrderBy(sort.Column, sort.Direction).ToList();
}
return PartialView("Grid", people);
}
This works great when the object I am sorting is people (if I click on the person.Name column to sort it ascending or descending). However, when I want to sort the address column, I get an exception because People does not contain a City...Address does.
My question is...is there a way to do this sort generically without knowing what the type is ahead of time? Thanks.
How about a javascript table sorter? Of course that won't work if your list is big enough to require paging.
I posted a similar question a little while back. The answers I got on that post might help you get on your way:
Looking for a better way to sort my List<T>
Basically the answers suggest holding map of Func<> (or similar) and calling the appropriate one in the code block.
Well, I got an object called Mamamia and inside of it has some string properties. I created a list of this object and populated it with 150 items.
I'm trying to use List.FindAll but I reaaally don't know how to do it. I've tried this way:
produto = products.FindAll(delegate(Mamamia cv) {return cv.LocalPackage.Remove(1,21) == cmbPackage.SelectedValue};
I don't know why the delegate is there, I just tried to copy from some other code on the internet.
Thanks in advance!
The delegate is there to see whether the value that you're testing is what you're looking for. The call to Remove looks worryingly like it's mutating the value though - that's rarely a good thing when you're looking through the list. I guess if it's a string then it's not too bad, although it may not be what you're after...
What are the types involved, and what are you looking for? Oh, and are you using C# 3 and/or .NET 3.5? That would make it easier (even C# 3 against .NET 2.0 means you could use a lambda expression instead of an anonymous method).
What's happening when you run the code at the moment? If it's just not finding anything, it may just be because you're testing for reference equality (if SelectedValue returns object).
Try this:
produto = products.FindAll(delegate(Mamamia cv) {
return cv.LocalPackage.Remove(1,21).Equals(cmbPackage.SelectedValue);
});
EDIT:
It sounds like you only want a single value, and if you're using .NET 3.5 it would be more idiomatic to use LINQ in the first place. I would use:
string selectedText = (string) cmbPackage.SelectedValue;
Mamamia item = products.FirstOrDefault
(cv => cv.LocalPackage.Remove(1,21) == selectedText);
if (item != null)
{
// Found it; otherwise item will be null
}
What would be the best approach to allow users to define a WHERE-like constraints on objects which are defined like this:
Collection<object[]> data
Collection<string> columnNames
where object[] is a single row.
I was thinking about dynamically creating a strong-typed wrapper and just using Dynamic LINQ but maybe there is a simpler solution?
DataSet's are not really an option since the collections are rather huge (40,000+ records) and I don't want to create DataTable and populate it every time I run a query.
What kind of queries do you need to run? If it's just equality, that's relatively easy:
public static IEnumerable<object[]> WhereEqual(
this IEnumerable<object[]> source,
Collection<string> columnNames,
string column,
object value)
{
int columnIndex = columnNames.IndexOf(column);
if (columnIndex == -1)
{
throw new ArgumentException();
}
return source.Where(row => Object.Equals(row[columnIndex], value);
}
If you need something more complicated, please give us an example of what you'd like to be able to write.
If I get your point : you'd like to support users writting the where clause externally - I mean users are real users and not developers so you seek solution for the uicontrol, code where condition bridge. I just though this because you mentioned dlinq.
So if I'm correct what you want to do is really :
give the user the ability to use column names
give the ability to describe a bool function (which will serve as where criteria)
compose the query dynamically and run
For this task let me propose : Rules from the System.Workflow.Activities.Rules namespace. For rules there're several designers available not to mention the ones shipped with Visual Studio (for the web that's another question, but there're several ones for that too).I'd start with Rules without workflow then examine examples from msdn. It's a very flexible and customizable engine.
One other thing: LINQ has connection to this problem as a function returning IQueryable can defer query execution, you can previously define a query and in another part of the code one can extend the returned queryable based on the user's condition (which then can be sticked with extension methods).
When just using object, LINQ isn't really going to help you very much... is it worth the pain? And Dynamic LINQ is certainly overkill. What is the expected way of using this? I can think of a few ways of adding basic Where operations.... but I'm not sure how helpful it would be.
How about embedding something like IronPython in your project? We use that to allow users to define their own expressions (filters and otherwise) inside a sandbox.
I'm thinking about something like this:
((col1 = "abc") or (col2 = "xyz")) and (col3 = "123")
Ultimately it would be nice to have support for LIKE operator with % wildcard.
Thank you all guys - I've finally found it. It's called NQuery and it's available from CodePlex. In its documentation there is even an example which contains a binding to my very structure - list of column names + list of object[]. Plus fully functional SQL query engine.
Just perfect.