I have a list of StudentViewModel object. I am binding this list with a DataGridView, and the column generatation is set to automatic according to the bound model's properties.
public async Task LoadGridView()
{
Tuple<List<StudentViewModel>, int> result = await App.StudentService.SearchAsync(studentRequestModel);
dataGridView1.DataSource = null;
dataGridView1.DataSource = result.Item1;
}
In the StudentViewModel, I have decorated some of the properties with a custom attribute IsViewable.
[AttributeUsage(AttributeTargets.Property)]
public class IsViewable: Attribute
{
public bool Value { get; set; }
}
usage:
[IsViewable(Value = true)]
public string Name { get; set; }
Idea is, just before binding with the UI Control, I want to filter the list and make a new list of anonymous object so that my grid will be populated with only selected properties.
Note: I don't want to create separate view models specific to Grids. I will refactor it if it creates performance issues.
The catch is, I serialized the dynamic list and then deserialized. Then I bind that dynamic list with the datagridview and it worked like a charm.
The whole project can be found here foyzulkarim/GenericComponents
Caller / Usage:
Type type = typeof(StudentViewModel);
PropertyInfo[] properties = type.GetProperties();
var infos = properties.Where(x => x.CustomAttributes.Any(y => y.AttributeType == typeof(IsViewable))).ToList();
List<StudentViewModel> models = result.Item1;
List<dynamic> list = models.Select(x => GetValue(x, infos)).ToList();
string serializeObject = JsonConvert.SerializeObject(list);
var deserializeObject = JsonConvert.DeserializeObject<List<dynamic>>(serializeObject);
dataGridView1.DataSource = deserializeObject;
Related
In the table column (of type nvarchar) I store an array of string values (List <string>). To get a data model I use Entity Framework.
Model:
namespace Project.Models.Atm
{
public class MyObject
{
[JsonIgnore]
public string _Parameters { get; set; }
[NotMapped]
[JsonProperty("parameters")]
public List<string> Parameters
{
get
{
return JsonConvert.DeserializeObject<List<string>>(string.IsNullOrEmpty(_Parameters) ? "" : _Parameters);
}
set
{
_Parameters = JsonConvert.SerializeObject(value);
}
}
}
}
If a new list of string values is assigned to the field of the myObject.parameters object, then I will do everything well.
MyObject myObject = new MyObject();
List<string> parameters = new List<string>();
parameters.Add("value");
myObject.parameters = parameters;
But if you try to add a value to the list of objects one by one, they are not added.
MyObject myObject = new MyObject();
myObject.parameters.Add("value"); <-- The value will not be added and there will be no errors.
What could be the problem?
While executing:
myObject.parameters.Add("value");
you call:
get
{
return JsonConvert.DeserializeObject<List<string>>(string.IsNullOrEmpty(_Parameters) ? "" : _Parameters);
}
and this code is creating new list for you and you are adding to it not to the string that is actually stored in the database.
To avoid temptation of adding to this list change your property to return IEnumerable<string> instead and always assign new list to the property.
Scenario:
I have to export an excel file which will contain list of Parts. We have enabled the user to select the columns and get only selected columns' data in the exported file. Since this is a dynamic report, I am not using any concrete class to map the report as this will result in exporting empty column headers in the report, which is unnecessary. I am using Dynamic Linq to deal with this scenario.
I have a list of dynamic objects fetched from dynamic linq.
[
{"CleanPartNo":"Test","Description":"test","AliasPartNo":["258","145","2313","12322"]},
{"CleanPartNo":"Test1","Description":"test1","AliasPartNo":[]}
]
How can I get 4 rows out of this json like
Please note that I cannot use a strongly typed object to deserialize/ Map it using JSON.Net
Update
Following is the code:
public class Part
{
public int Id { get; set; }
public string CleanPartNo { get; set; }
public string Description { get; set; }
public List<PartAlias> AliasPartNo { get; set; }
}
public class PartAlias
{
public int PartId { get; set; }
public int PartAliasId { get; set; }
public string AliasPartNo { get; set; }
}
var aliases = new List<PartAlias> {
new PartAlias{AliasPartNo="258" },
new PartAlias{AliasPartNo="145" },
new PartAlias{AliasPartNo="2313" },
new PartAlias{AliasPartNo="12322" }
};
List<Part> results = new List<Part> {
new Part{CleanPartNo="Test", Description= "test", PartAlias=aliases },
new Part{CleanPartNo="Test1", Description= "test1" }
};
var filters = "CleanPartNo,Description, PartAlias.Select(AliasPartNo) as AliasPartNo";
var dynamicObject = JsonConvert.SerializeObject(results.AsQueryable().Select($"new ({filters})"));
in the dynamicObject variable I get the json mentioned above
Disclaimer: The following relies on anonymous classes, which is not exactly the same as dynamic LINQ (not at all), but I figured that it may help anyway, depending on your needs, hence I decided to post it.
To flatten your list, you could go with a nested Select, followed by a SelectMany (Disclaimer: This assumes that every part has at least one alias, see below for the full code)
var flattenedResult = result.Select(part => part.AliasPartNumber.Select(alias => new
{
CleanPartNo = part.CleanPartNo,
Description = part.Description,
AliasPartNo = alias.AliasPartNo
})
.SelectMany(part => part);
You are first projecting your items from result (outer Select). The projection projects each item to an IEnumerable of an anonymous type in which each item corresponds to an alias part number. Since the outer Select will yield an IEnumerable<IEnumerable> (or omething alike), we are using SelectMany to get a single IEnumerable of all the items from your nested IEnumerables. You can now serialize this IEnumerable of instances of an anonymous class with JsonConvert
var json = sonConvert.SerializeObject(flatResults);
Handling parts without aliases
If there are no aliases, the inner select will yield an empty IEnumerable, hence we will have to introduce a special case
var selector = (Part part) => part.AliasPartNumber?.Any() == true
? part.AliasPartNumber.Select(alias => new
{
CleanPartNo = part.CleanPartNo,
Description = part.Description,
AliasPartNo = alias.AliasPartNo
})
: new[]
{
new
{
CleanPartNo = part.CleanPartNo,
Description = part.Description,
AliasPartNo = alias.AliasPartNo
}
};
var flattenedResult = result.Select(selector).SelectMany(item => item);
From json you provided you can get values grouped by their name in this way:
var array = JArray.Parse(json);
var lookup = array.SelectMany(x => x.Children<JProperty>()).ToLookup(x => x.Name, x => x.Value);
then this is just a manner of simple loop over the lookup to fill the excel columns.
However, I would suggest to do the flatenning before JSON. I tried for some time to make it happen even without knowing the names of the columns that are arrays, but I failed, and since it's your job, I won't try anymore :P
I think the best way here would be to implement custom converter that would just multiply objects for properties that are arrays. If you do it well, you would get infinite levels completely for free.
I am using generic method to fill my dropdown for all types
below is my code.
the entity type are as follow
public class Role
{
public string Id { get; set; }
public string Name { get; set; }
}
public class DropDown
{
public string Id { get; set; }
public string Name { get; set; }
}
i am able to fetch data successfully at
var data = DataFetcher.FetchData<T>();
private static void Main( string[] args )
{
List<DropDown> cities = BLL.GetDataList<City>();
List<DropDown> states = BLL.GetDataList<State>();
List<DropDown> roles = BLL.GetDataList<Role>();
}
public static class BLL
{
public static List<DropDown> GetDataList<T>() where T : class ,new()
{
var data = DataFetcher.FetchData<T>();
return data as List<DropDown>;
}
}
I knew this cast data as List<DropDown> will fail,thats why its returning null back to calling method,
How can i cast Generic list to List of Known Type?
You have to ask yourself: how do I want to convert T to DropDown? If you can't answer this, the answer is: you can't.
I guess your DropDown class has an object Value property, that holds the dropdown value, and you wish to assign the data entity to that property.
Then you can project the list of data entities to DropDowns as such:
var data = DataFetcher.FetchData<T>();
return data.Select(d => new DropDown { Value = d }).ToList();
As for your edit: so you have at least one type, the displayed Role, that has an Id and Name property. But type T doesn't guarantee this, so you'd need to introduce an interface:
public interface INamedIdentifyableEntity
{
string Id { get; set; }
string Name { get; set; }
}
And apply this to your entities. Then introduce it as a generic constraint and do the mapping:
return data.Select(d => new DropDown
{
Id = d.Id,
Name = d.Name,
}).ToList();
But you don't want this, as here you are tying these two properties to dropdowns. Tomorrow you'll want an entity with Code instead of Id and Text instead of Name, so you'll have to add more interfaces, more overloads, and so on.
Instead you might want to use reflection, where you can specify the member names in the call:
List<DropDown> cities = BLL.GetDataList<City>(valueMember: c => c.CityCode, displayMember: c => c.FullCityname);
And use these member expressions to look up data's values and fill those into the DropDown.
However, you're then reinventing the wheel. Leave out your DropDown class entirely, and leave the dropdown generation to the front end, in this case MVC:
var cities = DataFetcher.FetchData<City>();
var selectList = new SelectList(cities.Select(c => new SelectListItem
{
Selected = (c.Id == selectedCityId),
Text = c.FullCityName,
Value = c.CityCode,
});
Or:
var selectList = new SelectList(cities, "CityCode" , "FullCityName", selectedCityId);
One solution is to use AutoMapper.
First create a map between your models like this:
AutoMapper.Mapper.CreateMap<Role, DropDown>();
Do the same thing for City and State classes if you need to.
Then you can use AutpMapper to convert your objects to DropDown like this:
public static List<DropDown> GetDataList<T>() where T : class ,new()
{
var data = DataFetcher.FetchData<T>();
return data.Select(x => AutoMapper.Mapper.Map<DropDown>(x)).ToList();
}
If I understood the question correctly, you could use Linq as follows.
return data.Cast<DropDown>().ToList();
I am working on serializing a viewmodel with MVC5 with JsonConvert.SerializeObject and the result is coming up with is an array for each record (e.g. [{PropertyName: Value,...},{PropertyName: Value,...}]. I am trying to fill a slick grid to get the property names dynamically and I have seen others have arrays that have the Property names in one array and then the data rows in another. How can I create [{Column Names},{DataRowValues}] so i can get the property names from this array. I will be deserializing this data as well. I was using this link as a reference, but I am using a List and had trouble making it work Formatting output of Newtonsoft.Json.JsonConvert.SerializeObject(dataSet). Thanks.
[HttpGet]
public JsonResult GeneratePlans()
{
//code here to create and populate view model
return Json(JsonConvert.SerializeObject(viewModel, Formatting.Indented, new JsonSerializerSettings { }), JsonRequestBehavior.AllowGet);
}
On the server, if you need dynamic property resolution, you can use reflection to enumerate the properties and values of your view model.
Since you wish to have your JSON split into an array for property names and an array for property values, we need a class to hold that result. I've left this class deliberately simple since it will just be used for holding values for JSON serialization.
class ObjectPropertyNamesAndValues
{
public List<string> Names { get; set; }
public List<object> Values { get; set; }
}
Here is a function that will populate an instance of ObjectPropertyNamesAndValues given a viewModel instance of any type.
ObjectPropertyNamesAndValues GetObjectPropertyNamesAndValues(object viewModel)
{
if (viewModel == null)
return null;
var result = new JsonObjectPropertyValues();
result.Names = new List<string>();
result.Values = new List<object>();
var propertyInfo = viewModel.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
foreach (var property in propertyInfo)
{
if (!propertyInfo.CanRead)
continue;
// you can add other checks here, too, such as whether or not
// the property has a certain custom attribute or not
result.Names.Add(propertyInfo.Name);
result.Values.Add(property.GetValue(viewModel));
}
return result;
}
Now you can implement your function like this:
[HttpGet]
public JsonResult GeneratePlans()
{
//code here to create and populate view model
// get the object properties and values for transport as JSON
Debug.Assert(viewModel != null);
var objectPropertyNamesAndValues = GetObjectPropertyNamesAndValues(viewModel);
return Json(JsonConvert.SerializeObject(objectPropertyNamesAndValues, Formatting.Indented, new JsonSerializerSettings { }), JsonRequestBehavior.AllowGet);
}
I'm trying to convert a List<Topic> to an anonymous or dynamic type via linq projection... I'm am using the following code, but it doesn't seem to work properly. It returns the dynamic type without error, however, if I try to enumerate the children field (list<object/topic>) then it says
Results View = The type '<>f__AnonymousType6<id,title,children>' exists in both 'MyWebCore.dll' and 'MvcExtensions.dll'
Strange.
Here is the code I am using:
protected dynamic FlattenTopics()
{
Func<List<Topic>, object> _Flatten = null; // satisfy recursion re-use
_Flatten = (topList) =>
{
if (topList == null) return null;
var projection = from tops in topList
select new
{
id = tops.Id,
title = tops.Name,
children = _Flatten(childs.Children.ToList<Topic>())
};
dynamic transformed = projection;
return transformed;
};
var topics = from tops in Repository.Query<Topic>().ToList()
select new
{
id = tops.Id,
title = tops.Name,
children = _Flatten(tops.Children.ToList<Topic>())
};
return topics;
}
All i'm doing is flattening a list of self containing objects - basically it's a list of POCOs that will be stuffed into a tree view (jstree).
The Topic class is defined as:
public class Topic
{
public Guid Id {get;set;}
public string Name {get;set;}
public List<Topic> Children {get;set;}
}
And here is an example of what the first member of the returned dynamic object looks like:
[0] = {
id = {566697be-b336-42bc-9549-9feb0022f348},
title = "AUTO",
children = {System.Linq.Enumerable.SelectManyIterator
<MyWeb.Models.Topic,
MyWeb.Models.Topic,
<>f__AnonymousType6<System.Guid,string,object>
>}
}
Why do you have the same LINQ code twice? After you define your _Flatten func, you can just call it immediately - var topics = _Flatten(Repository.Query<Topic>().ToList().
It looks like you're creating two identical anonymous types, one inside the _Flatten func and one outside it. I would think the compiler is smart enough to handle that, but try changing your call to explicitly use _Flatten, see if it solves the problem.
Here is the proper way - have to load into the a DTO / POCO and return that:
_Flatten = (topList) =>
{
if (topList == null) return null;
var projection = from tops in topList
//from childs in tops.Children
select new JsTreeJsonNode
{
//id = tops.Id.ToString(),
data = tops.Name,
attr = setAttributes(tops.Id.ToString(), tops.URI),
state = "closed",
children = _Flatten(tops.Children)
};
return projection.ToList();
};