I want to perform a Dynamic Lambda in a collection with an array of strings[] on it:
public class ClassStudentsViewModel
{
public string[] Disciplines { get; set; }
public TeacherName { get; set; }
}
This is what I'm trying:
The source is a collection of ClassStudentsViewModel and the values is an array of strings with one string. When executed, it throws me this exception:
No property or field 'y' exists in type 'String'
After some searching, I have found this question which is almost the same problem and the OP ended changing the source code of Dynamic.cs, that isn't a nice option for me. I wonder what I'm trying isn't supported or might be a bug. The problem is that the above mantioned question was asked almost 4 years ago.
The following snippet works nice:
classStudents.AsQueryable().Where(x => x.Disciplines.Any(y => y == "Turma 2")).ToList();
How can I get rid of that error?
UPDATE:
A little context of what I'm trying: My controller receives a viewModel with a collection of filters sent by the 3rd party grid, which contains basically a value and a operator, like eq, gt etc... A method loops all those filters and transforms on lambda operators, like eq to == or contains to .Contains(). In a simple string property like TeacherName(updated viewModel above), the dynamic filters works, e.g. if the predicate in the screenshot is: "TeacherName.Contains(#0)" it works well.
UPDATE 2:
This code generates the predicate:
public static string ToLambdaOperator(string field, string oper, int index, string sufix = null)
{
var result = String.Empty;
switch (oper)
{
case "eq":
case "neq":
case "gte":
case "gt":
case "lte":
case "lt":
result = string.Format(field + ToLinqOperator(oper) + "#" + index);
break;
case "startswith":
result = field + ".StartsWith(" + "#" + index + ")";
break;
case "endswith":
result = field + ".EndsWith(" + "#" + index + ")";
break;
case "contains":
result = field + ".Contains(" + "#" + index + ")";
break;
case "doesnotcontain":
result = "!" + field + ".Contains(" + "#" + index + ") || " + field + ".Equals(String.Empty)";
break;
}
if (!String.IsNullOrEmpty(sufix))
{
result += sufix;
}
return result;
}
// Use example
var operator = "eq";
var paramCounter = -1;
var predicate = ToLambdaOperator("Disciplines.Any(y => y", operator, ++paramCounter, ")");
The predicate above will result: Disciplines.Any(y => y == #0). With the operator contains will result in this: Disciplines.Any(y => y.Contains(#0)).
I think what you are trying to do is to generate an expression tree based on the arguments provided. Here are some examples about how to do that.
https://gist.github.com/afreeland/6733381
How to: Use Expression Trees to Build Dynamic Queries (C# and Visual Basic)
Related
I got this error when trying to sort any columns that are not in the Order table, if I use OrderBy("Customer.CompanyName" + " " + sortDir) the error will gone but all the columns will become unable to sort. The OrderBy method used below come from here.
What is the cause of the problem ?
public ActionResult WebGrid(int page = 1, int rowsPerPage = 10, string sortCol = "OrderID", string sortDir = "ASC")
{
List<Order> res;
using (var nwd = new NorthwindEntities())
{
var _res = nwd.Orders
.OrderBy(sortCol + " " + sortDir)
.Select(o => new Order
{
OrderID = o.OrderID,
OrderDate = o.OrderDate,
CompanyName = o.Customer.CompanyName,
FirstName = o.Employee.FirstName,
//......
//......
//......
});
The class you provided the link to is marked as internal and it can't be used outside the assembly it was defined in, so you can't use it in your code.
This API supports the product infrastructure and is not intended to be
used directly from your code. Provides functionality to create new
classes from values in a LinqDataSource control.
So What you're actually trying to use is OrderBy inside Queryable class which is part of System.Linq which can be used as following:
.OrderBy(x=> x.sortCol + " " + x.sortDir)
If you're trying to order by two columns, then you can use:
.OrderBy(x=> x.sortCol).ThenBy(x=> x.sortDir)
If you want to dynamically specify the OrderBy expression, you can either do a switch statement for each possible parameter, or follow this SO Answer to build a dynamic Expression Tree.
How do I iterate through the List<Galaxy> and print out the value(s) of every property without having to write the property name(s) explicitly?
For example, I use this code to write property values of all properties of galaxy
private static void IterateThroughList()
{
var theGalaxies = new List<Galaxy>
{
new Galaxy() { Name = "Tadpole", MegaLightYears = 400},
new Galaxy() { Name = "Pinwheel", MegaLightYears = 25}
};
foreach (Galaxy theGalaxy in theGalaxies)
{
// this part is of concern
Console.WriteLine(theGalaxy.Name + " " + theGalaxy.MegaLightYears);
}
}
I'm trying to avoid the explicit property names in this line
Console.WriteLine(theGalaxy.Name + " " + theGalaxy.MegaLightYears);
So that, if my Galaxy class had more properties than Name and MegaLightYears, it would automatically print them too.
If you want to
Write all properties without naming them
Use it for any Type in a generic way
You can write a quick Reflection utility like this
public static string GetAllProperties(object obj)
{
return string.Join(" ", obj.GetType()
.GetProperties()
.Select(prop => prop.GetValue(obj)));
}
And use it like
foreach (Galaxy theGalaxy in theGalaxies)
{
Console.WriteLine(GetAllProperties(theGalaxy));
}
If I understand you correctly you want to avoid having to write the individual properties of the galaxy within the loop?
In that case you might overload ToString on Galaxy appropriately:
class Galaxy {
public override string ToString() {
return Name + " " + MegaLightYearsl;
}
}
Then you can just do
foreach (var galaxy in theGalaxies) {
Console.WriteLine(galaxy);
}
However, since you only have one ToString to override, you cannot do this for wildly differing string representations of your object that may be needed in different contexts.
Your question is a little unclear, but I assume you're asking for how you iterate through a list by index instead of as a foreach loop.
Try a standard for loop, like so:
for(int i = 0; i < theGalaxies.Count; i++) {
Console.WriteLine(theGalaxies[i].Name + " " + theGalaxies[i].MegaLightYears);
}
I have an object which looks something like this
Object
Name
ID
PropertyGroupList[]
PropertyGroupListItem
PropertyList[]
PropertyListItem
Tag
Type
PropertyListItem
Tag
Type
PropertyGroupListItem
PropertyList[]
PropertyListItem
Tag
Type
PropertyListItem
Tag
Type
Each of those objects has a PropertyListItem where the Tag is "Revision". I need to compare the value of "Revision" with all of the other objects stored in the list and return the item where "Revision" has the highest value.
I think I can build a way with nested for loops but I thought it would be a better approach to get the Object by using a Linq or Lambda expression.
I've been trying to find a way to do this by myself but I feel like everything I did is completely wrong. I'd be more than happy if someone could help me and give a little explanation about it. Thanks a lot!
EDIT:
Sample code:
public MdsObject GetSoftwareObjectByName(string sPackageName)
{
GetObjectListRequest getObjectListReq = new GetObjectListRequest();
InitializeRequest(getObjectListReq);
//TODO: Are there more characters which need to be escaped in an LDAP query to DSM?
sPackageName = sPackageName.Replace("(", "\\(");
sPackageName = sPackageName.Replace(")", "\\)");
getObjectListReq.LdapQuery = "<LDAP://rootDSE>;(Name:IgnoreCase=" + sPackageName + ");;subtree";
getObjectListReq.MaxResults = -1;
GetObjectListReply getObjectListReply = AdministrationService.GetObjectList(getObjectListReq);
switch (getObjectListReply.ObjectList.Length)
{
case 0:
{ throw new ApplicationException("GetSoftwareObjectByName failed. Could not find '" + sPackageName + "'"); }
case 1:
{
MdsObject incompleteObjectFromLdap = getObjectListReply.ObjectList[0];
return GetSoftwareObjectById(incompleteObjectFromLdap.ID);
}
//more than one object was returned -> check revisions
default:
{
List<MdsObject> ListReturnedObjects = new List<MdsObject>();
for (int i = 1; i <= getObjectListReply.ObjectList.Length; i++)
{
MdsObject incompleteObjectFromLdap = getObjectListReply.ObjectList[i-1];
ListReturnedObjects.Add(GetSoftwareObjectById(incompleteObjectFromLdap.ID));
}
**Here I need to filter the objects**
throw new ApplicationException("GetSoftwareObjectByName failed. Software name '" + sPackageName + "' is not unique!");
}
}
}
First Start off by flatening your nested hierarchy down to an IEnumerable
var q = MyObject.PropertyGroupList
.SelectMany(item=>item.PropertyList); // For every Item in the GroupList, Flatten it and return the individual PropertyListItem
Then find the one with the highest revision
var q2 = q.OrderbyDescending(item=>item.Tag) // Order them by tag starting with the largest
.First(); // And get the first and thus biggest one.
I've seen many posts about creating ExpandoObject objects and such, but it does not work in my case. I need to create an object like
var someObj = new {
term1 = "someValue",
term2 = "other",
...
};
Basically, we are using Dapper and we need to create a query dynamically, where the WHERE clause is fabricated from a given array of arguments. We are not generalizing queries! It's a single method receiving a variable number of arguments and we need to check OR each value on a single column.
Right now, the only viable solution is to revert and directly use System.Data.SqlClient.SqlConnection, or is there any way to make this work?
Update:
This is what most likely should work, but doesn't :
string inWhere = null;
dynamic inTerms = new ExpandoObject();
IDictionary<string, object> inTermsDict = inTerms;
if (!(string.IsNullOrEmpty(filter.Term) || string.IsNullOrWhiteSpace(filter.Term))) {
inWhere = "(" + string.Join(" OR ", filter.Terms.Select((t, i) => "{0} LIKE #p" + i)) + ")";
int termIndex = 0;
foreach (string term in filter.Terms) {
inTermsDict.Add("p" + (termIndex++), term);
}
}
// ...
var rows = db.Query("SELECT * FROM {table} WHERE {baseCondition}" +
(string.IsNullOrEmpty(inWhere) ? "" : string.Format(" AND " + inWhere, "columnName")),
inTerms as object);
Just to answer my own question, as we found the proper solution earlier today.
Simply put, we found the IDynamicParameters And this class simply solves everything, acting as a Dictionary.
var inTerms = new Dapper.DynamicParameters();
inTerms.Add("#p" + (termIndex++), somveValue);
Everyone's happy!
I can not get the right result from the following code block:
object ob = ds_bug.Tables[0].Compute("Count(id)",str_vertical +"= '"+"' and "+str_horizontal+" = '"+first_row.Cells[j].Text.ToString()+"'");// ds_bug has been filled some data.
str_vertical and str_horizontal are two fields from the ds_bug.Tables[0], and I can see their values are right too when I debug the code.
Is there any problem I set the filter with a string like " a = '##' and b = '##' " ?
First you should split the method call and the initialization of the expression and the filter, that makes it clearer:
I assume that first_row.Cells[j].Text.ToString() is a static value that you've queried previously, so you can declare it like:
String firstRowCellsJText = first_row.Cells[j].Text.ToString();
String expression="Count(id)";
String filter = "str_vertical=str_horizontal + " + firstRowCellsJText;
object ob = s_bug.Tables[0].Compute(expression,filter);