score:0
After spending almost a full 2 days on trying to get this solution to work without making big changes elsewhere I finally decided to make a slight design change to the model to make this easier.
How I resolved this was to add each field to the model directly, instead of using a List of custom objects. Due to this I removed the XML Deserialization, and instead looped through all of the elements within the Node using XDocument - then comparing the value of the "Name" attribute to determine which field it was, and assigning the value of the "Value" attribute to the corresponding Property in the model.
A little extra code, and a nice little switch statement with 22 cases to handle the assignments...
Not how I wanted to do it, but at least I can still deliver the service on time and then try to see if there is a better way to do this later...
For the sake of completeness here is what I changed:
public class Bug
{
public int ID { get; set; }
public DateTime ChangedDate { get; set; }
public string State { get; set; }
public string AreaPath { get; set; }
public string Title { get; set; }
public string Status { get; set; }
public string SubStatus { get; set; }
public string OpenedBy { get; set; }
public string ChangedBy { get; set; }
public DateTime OpenedDate { get; set; }
public DateTime ResolvedDate { get; set; }
public string AssignedTo { get; set; }
public string IssueType { get; set; }
public string IssueSubtype { get; set; }
public string Priority { get; set; }
public string Severity { get; set; }
public string Keywords { get; set; }
public string ScreenID { get; set; }
public string ResolvedBy { get; set; }
public string Resolution { get; set; }
public string ReproSteps { get; set; }
public string HowFound { get; set; }
public string FullHistory { get; set; }
public string EverChangedBy { get; set; }
}
and what used to be the deserialization method:
internal static Bug Deserialize(string filePath)
{
XDocument doc = XDocument.Load(filePath);
Bug bug = new Bug();
bug.ID = int.Parse(doc.Root.Element("ID").Value);
bug.ChangedDate = DateTime.Parse(doc.Root.Element("ChangedDate").Value);
foreach (var el in doc.Root.Element("Fields").Elements())
{
XAttribute fieldName = el.Attributes("Name").Single();
XAttribute fieldValue = el.Attributes("Value").Single();
switch (fieldName.Value.ToString())
{
case "State":
bug.State = fieldValue.Value.ToString();
break;
case "Area Path":
bug.AreaPath = fieldValue.Value.ToString();
break;
case "Title":
bug.Title = fieldValue.Value.ToString();
break;
case "Status":
bug.Status = fieldValue.Value.ToString();
break;
case "SubStatus":
bug.SubStatus = fieldValue.Value.ToString();
break;
case "Opened By":
bug.OpenedBy = fieldValue.Value.ToString();
break;
case "ChangedBy":
bug.ChangedBy = fieldValue.Value.ToString();
break;
case "Opened Date":
bug.OpenedDate = DateTime.Parse(fieldValue.Value.ToString());
break;
case "Resolved Date":
bug.ResolvedDate = DateTime.Parse(fieldValue.Value.ToString());
break;
case "Assigned To":
bug.AssignedTo = fieldValue.Value.ToString();
break;
case "Issue Type":
bug.IssueType = fieldValue.Value.ToString();
break;
case "Issue Subtype":
bug.IssueSubtype = fieldValue.Value.ToString();
break;
case "Priority":
bug.Priority = fieldValue.Value.ToString();
break;
case "Severity":
bug.Severity = fieldValue.Value.ToString();
break;
case "Keywords":
bug.Keywords = fieldValue.Value.ToString();
break;
case "ScreenID":
bug.ScreenID = fieldValue.Value.ToString();
break;
case "ResolvedBy":
bug.ResolvedBy = fieldValue.Value.ToString();
break;
case "Resolution":
bug.Resolution = fieldValue.Value.ToString();
break;
case "ReproSteps":
bug.State = fieldValue.Value.ToString();
break;
case "HowFound":
bug.State = fieldValue.Value.ToString();
break;
case "FullHistory":
bug.State = fieldValue.Value.ToString();
break;
case "EverChangedBy":
bug.State = fieldValue.Value.ToString();
break;
}
}
return bug;
}
Which allowed me to use the following call to build out the LINQ query:
foreach (KeyValuePair<string, object> field in queryFields)
{
itemList = itemList.Where<Bug>(field.Key, field.Value, (FilterOperation)StringEnum.Parse(typeof(FilterOperation), "eq"));
}
Cheers!
score:0
I see a couple of approaches to solve that:
1) Have a list of all properties that are available for filtering, match the properties and add them dynamically to your linq query.
The way that would work is that you will accept your json object and iterate through the properties passed with their values. So, say that you get in your controller:
{
Prop[0].Name = "Name"
Prop[0].Value = "Justin"
Prop[1].Name = "Email"
Prop[1].Value = "j@g.com"
}
In your controller, for example, you will iterate through each key value and dynamicall chain it. Some pseudocode:
foreach(var keyValue in Filters){
var prop = keyValue.Name;
var val = keyValue.Value;
myLinqQuery.AddWhere(c => c.GetType().GetProperty().Name == prop && c.GetValue() == Value);
}
This apprach will work good, but one big disadvantage is that it will try and filter each property in the request with reflection. You would loose some control in that case.
2) Allow only a number of filters and have a filter condition for each of those. In that option you would allow only a list of filters and have some sort of an where action for each of them. Here is a simple example: Say that your controller takes in:
public ActionResult Filter(string name, string email){
//name and email are the filter values
var query = ( from c in (List<Bug>)itemList select c);
if(!string.IsNullOrEmpty(name))
query.AddWhere(c => c.Name == name);
if(!string.IsNullOrEmpty(email))
query.AddWhere(c => c.Email == email);
}
You can further design that to be more OOP and SOLID by creating a separate class for each filter allowed and exposing its filter action.
Let me know if it helps!
score:1
I know this is old, but here is how I did it without having to type in every possible name:
1) Anonymous IEnumerable coming in, cast as IEnumerable or IList as required
2) On the first element,
var property = element.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
3) Foreach object in property, add columns to your datatable:
table.Columns.Add(property.Name, typeof(property.GetValue(element, null));
4) Iterate your collection from the beginning: foreach:
var prop = item.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
dynamic i = new ExpandoObject();
IDictionary<string, object> d = (IDictionary<string, object>)i;
foreach (var p in prop)
{
d.Add(p.Name, p.GetValue(item));
}
list.Rows.Add(((IDictionary<string, object>)d).Values.ToArray());
This gets the anonymous types, extracts them into an ExpandoObject (which is completely interchangeable with a IDictionary), then adds the row by casting the IDictionary dynamic object as an array.
Scales with any type you can throw at it and allows you to manipulate the data as it is constructed.
Source: stackoverflow.com
Related Articles
- Dynamic LINQ expression where field name is not known
- How do I write a linq to sql query to find records where field name can match one of dynamic number of strings?
- How to specify dynamic field names in a Linq where clause?
- I need a LINQ expression to find an XElement where the element name and attributes match an input node
- Linq expression to get all items in a IList where one field is in another List
- Setting a dynamic sort name field in a linq query
- MVC Linq Query with dynamic column name in WHERE clause
- Where clause on dynamic column name in LINQ query?
- LINQ Expression - Dynamic From & Where Clause
- Where clause on field outside Lambda expression class in Linq
- Creating a Dynamic Where Clause as a Linq Expression
- dynamic linq where expression
- Dynamic Linq Where with Lambda Expression generates Error
- Get resulting LINQ expression from LINQ Dynamic WHERE
- Dynamic where condition in linq query expression
- Convert a filter expression based on property name and value to Linq Where clause Func<T, bool>?
- Build Dynamic Expression For Linq Where using PropertyInfo and a value
- Entity Framework dynamic linq where from generic source with dynamic where clause
- How to specify a literal field name in a LINQ Query where clause?
- Dynamic WHERE clause in LINQ
- LINQ - Does the Where expression return new instance or reference to object instance
- Creating a LINQ Expression where parameter equals object
- LINQ query to perform a projection, skipping or wrapping exceptions where source throws on IEnumerable.GetNext()
- Dynamic where clause (OR) in Linq to Entities
- How to build a dynamic AND OR linq expression tree in a loop
- passing dynamic expression to order by in code first EF repository
- Null check String.ToLower in Linq Where expression
- Dynamic where condition in LINQ
- Is there a ODATA query to linq where expression (ODATA to Linq )
- Dynamic where clause in Linq to Entities
- XElement remove node found using LINQ
- IQueryable, converting Anonymous Type to Strong Type
- SQL to LINQ semi-complex query projecting to ViewModel
- How to find an subset from the start of a collection with LINQ?
- get indexes of different elements from arrays with equal length using Linq
- How to put one or many LINQ Lambda return records to List ?! C#
- Retrieving non-duplicates from 2 Collections using LINQ
- How can generate a sequence number inside a LINQ select?
- Can I convert those two LINQ Statement in a OneLiner (C#)
- Order list by average of child elements?
- Group By and To Dictionary in EF Core 3.1
- Removing the WHERE clause in a Linq query
- Linq To Entities Indirect Contextual Table Join in Fluent API
- How IEnumerable.Except() works?
- Error in my query in Entity Framework in C#
- Compare two complex list objects
- Linq - check if the any of the values present in an array exists in the database table
- problem using foreach in linq query result
- Doing a Count in a Linq to SQL Query (created as IQueryable) gets all the rows
- How to find modal value accross List<List<double>> for each inner value?