Initialize SELECT2 dropdown with OPTGROUP in C# Webform - c#

I've searched high and low for an exact example for how to populate a <select> control with <optgroup>, but I get various results. The documentation on select2.org states that the format should be like this:
{
"results": [
{
"text": "Group 1",
"children" : [
{
"id": 1,
"text": "Option 1.1"
},
{
"id": 2,
"text": "Option 1.2"
}
]
},
{
"text": "Group 2",
"children" : [
{
"id": 3,
"text": "Option 2.1"
},
{
"id": 4,
"text": "Option 2.2"
}
]
}
],
"pagination": {
"more": true
}
}
But my results throw an error stating that the 'ID' field is not present. I think it's due to the way I'm initializing the <select>, but I don't see another way.
First, my data is being pulled from a MS SQL db using a stored procedure. I don't have easy control over the data format, so I'm using Linq to create an object I then initialize the <select> with using .DataBind(). Here is the relevant code:
HTML:
<select id="MySelect" name="myselect" runat="server"></select>
Definitions for Parent and Child:
public class Parent : Child
{
public List<Child> children { get; set; }
}
public class Child
{
public string id { get; set; }
public string text { get; set; }
}
Linq statement:
var parents = myData
.Select(c => new Parent()
{
text = string.Format("{0} {1}", c.first_name, c.last_name),
id = c.ID.ToString(),
children = GetChildren(locations, c)
})
.ToArray();
// Convert to JSON
var json = JsonConvert.SerializeObject(new
{
results = parents
});
Linq result converted to JSON:
{
"results":[
{
"id":"2",
"text":"Parent 1 ",
"children":[
]
},
{
"id":"39",
"text":"Parent 2",
"children":[
{
"text":"Child 2.1",
"id":"62",
"Custom1":"196.00"
},
{
"text":"Child 2.2",
"id":"130",
"Custom1":"642.00"
},
{
"text":"Child 2.2",
"id":"61",
"Custom1":"0.00"
}
]
},
{
"id":"15",
"text":"Parent 3",
"children":[
{
"text":"Child 3.1",
"id":"13",
"Custom1":"40.00"
}
]
}
]
}
DataBind:
MySelect.DataSource = json;
MySelect.DataValueField = "id";
MySelect.DataTextField = "text";
MySelect.DataBind();
After all of this, the MySelect <select> renders with a dropdown containing only the parents and no <optgroup> elements in the actual HTML.
I have tried excluding the 'id' member from the <Parent> class, but then there is an error thrown from the .DataBind() that there is no 'id' even though the children all have ids.
I suspect that using .DataBind() isn't the way to handle this. I want to keep the code clean, short and quick.
How can I accomplish this?

Related

C# MongoDB Count with Lookup or Match

So Suppose I have two Collections
User {
string Id;
string name;
}
Category {
string Id;
string userId;
string name;
}
// For Look Up
UserJoined : User {
public ICOllection<Category> Categories {get;set;}
}
I am trying to count using this kind of Query (SQL)
Expression<Func<User,bool>> query = p => p.User.Category.Name == "Apple";
Now the above works great in SQL using EF Core.
But I want to do something similar in Mongo DB Driver C#.
The Current situation fetches all the documents and then it Takes count, which is very very bad performance wise in the long run. I know there might be something.
I was looking at AddFields operator but I couldn't find a way to use it in my case.
This should get the count of users which contain a category with the name "Apple":
MongoClient mongoClient = new MongoClient("mongodb://localhost:27017");
IMongoDatabase db = mongoClient.GetDatabase("your_db_name");
IMongoCollection<BsonDocument> usersCollection = db.GetCollection<BsonDocument>("users");
FilterDefinition<BsonDocument> matchFilter = Builders<BsonDocument>.Filter.ElemMatch<BsonDocument>("categories", Builders<BsonDocument>.Filter.Eq(c => c["name"], "Apple"));
AggregateCountResult result = usersCollection.Aggregate()
.Match(matchFilter)
.Count()
.Single();
long count = result.Count;
Which is a C# representation of the following aggregation:
db.users.aggregate(
[{
$match: {
categories: {
$elemMatch: {
name: "Apple"
}
}
}
}, {
$count: "count"
}]
)
Considering you have users collection with the following data:
[
{
"_id": 1,
"name": "User 1",
"categories": [
{
"_id": 1,
"name": "Orange"
},
{
"_id": 2,
"name": "Apple"
},
{
"_id": 3,
"name": "Peach"
}
]
},
{
"_id": 2,
"name": "User 2",
"categories": [
{
"_id": 1,
"name": "Orange"
},
{
"_id": 3,
"name": "Peach"
}
]
},
{
"_id": 3,
"name": "User 3",
"categories": [
{
"_id": 4,
"name": "Banana"
}
]
},
{
"_id": 4,
"name": "User 4",
"categories": [
{
"_id": 4,
"name": "Banana"
},
{
"_id": 2,
"name": "Apple"
}
]
},
{
"_id": 5,
"name": "User 5",
"categories": [
{
"_id": 2,
"name": "Apple"
}
]
}
]
Value of count variable will be 3.
if the goal is to get a count of users who has "Apple" as a category, you can simply do it like so:
var appleUserCount = await categoryCollection.CountDocumentsAsync(c=>c.Name == "Apple");
however, if there are duplicate categories (same userId and categoryName) then getting an accurate count gets a bit complicated and you'll have to do some grouping for getting a distinct user count.
test program:
using MongoDB.Entities;
using System.Threading.Tasks;
namespace TestApplication
{
public class User : Entity
{
public string Name { get; set; }
}
public class Category : Entity
{
public string UserID { get; set; }
public string Name { get; set; }
}
public static class Program
{
private static async Task Main()
{
await DB.InitAsync("test");
await DB.Index<Category>()
.Key(c => c.Name, KeyType.Ascending)
.CreateAsync();
var user1 = new User { Name = "Fanboy" };
var user2 = new User { Name = "Poolboy" };
await new[] { user1, user2 }.SaveAsync();
await new[] {
new Category { Name = "Apple", UserID = user1.ID},
new Category { Name = "Apple", UserID = user2.ID},
new Category { Name = "Google", UserID = user1.ID}
}.SaveAsync();
var count = await DB.CountAsync<Category>(c => c.Name == "Apple");
}
}
}

Unable to produce enum from c# code to json string

I am using JsonForm to produce dynamic forms in my MVC core web application. I am using System.Text.Json.JsonSerializer.Serialize in the following code in my view model to produce a simple one field form. My aim is to store this json in the database eventually and retrieve it from there.
public TestsViewModel GetFormControls1()
{
var myJsonModel = new
{
schema = new
{
client = new
{
type = "object",
title = "Client",
properties = new
{
forename = new
{
type = "string",
title = "Forename",
minLength = 3,
maxLength = 10,
}
}
}
},
form = new List<Object>
{
new {
key = "client.forename",
title = "Forename"
}
},
value = new
{
client = new
{
forename = "",
}
}
};
TestsViewModel homeVm = new TestsViewModel();
homeVm.FormControls = System.Text.Json.JsonSerializer.Serialize(myJsonModel);
return homeVm;
}
Above code works fine and produces following json schema which is then used to create a form.
{
"schema": {
"client": {
"type": "object",
"title": "Client",
"properties": {
"forename": {
"type": "string",
"title": "Forename",
"minLength": 3,
"maxLength": 10
}
}
}
},
"form": [
{
"key": "client.forename",
"title": "Forename"
}
],
"value": {
"client": {
"forename": ""
}
}
}
I now need to produce an enum for my json so that a drop down for selecting a gender can appear in the form. However, I am unable to do that via c# code. Can someone help? I would like my c# code to produce following json (note two entries for gender in schema and form).
{
"schema": {
"client": {
"type": "object",
"title": "Client",
"properties": {
"forename": {
"type": "string",
"title": "Forename",
"minLength": 3,
"maxLength": 10
},
"gender": {
"type": "string",
"title": "Gender",
"enum": [
"male",
"female",
"alien"
]
}
}
}
},
"form": [
{
"key": "client.forename",
"title": "Forename"
},
{
"key": "client.gender",
"titleMap": {
"male": "Dude",
"female": "Dudette",
"alien": "I'm from outer space!"
}
}
],
"value": {
"client": {
"forename": ""
}
}
}
I have tried using following code but enum is a keyword in c# so I get error.
gender = new
{
type = "string",
title = "Gender",
enum = "[male, female, alien]"
}
Also Enum = "[male, female, alien]" produces "Enum": "[male, female, alien]" instead of "enum": [ "male", "female", "alien" ]
I have a gender lookup table which I will be eventually using to produce above enum somehow so any idea regarding that would be helpful as well.
UPDATE
#dbc's comment provides solution to most of my problem. However, I am still struglling with producing titleMap json if I try to map string to an int.
var gender3 = new
{
type = "string",
title = "Gender",
titleMap = new List<string> { new string("1" + ":" + "Male"), new string("2" + ":" + "Female")}
};
Above code produces
{
"type": "string",
"title": "Gender",
"titleMap": [
"1:Male",
"2:Female"
]
}
However, I need both 1 and Male in their own double quotations inside { } instead of [ ] as shown below.
{
"type": "string",
"title": "Gender",
"titleMap": {
"1": "Male",
"2": "Female"
}
}
enum is a keyword in C# so you need to prepend # to enum
gender = new
{
type = "string",
title = "Gender",
#enum = new string[3]{"male", "female", "alien"}
}
From what I understand, you are trying to serialize an array of enum values as their text names into JSON. The simplest solution for that is to add an array or list of enums property to your TestViewModel and use the JsonStringEnumConverter converter to serialize them as strings rather than numbers.
Here's an example of how you could achieve what you are looking for.
Let's say you have some enum with a set of values:
//This is an arbitrary enum, this could be 'Gender', in your case
public enum TestEnum
{
value1,
value2,
value3,
value4,
}
And you want to write down an array of those enum values. Add a list of enums property (or array of enums) to your model. If you want the property name within the JSON to be one of the reserved words (like enum) either override the name using the JsonPropertyName attribute (and keep the name whatever makes most sense programmatically):
public class TestsViewModel_Option1
{
// In your case, this property could be called 'Genders' to be self-documenting
[JsonPropertyName("enum")]
public List<TestEnum> ListOfEnums { get; set; }
}
OR, use the # symbol if you really want the property name to be the reserved keyword and don't want to/can't use the attribute for some reason.
public class TestsViewModel_Option2
{
// Or use fixed-size array, TestEnum[], if needed
public List<TestEnum> #enum { get; set; }
}
And now this is what your code looks like with the JsonSerializer:
private static void SerializeListOfEnums()
{
var model1 = new TestsViewModel_Option1
{
ListOfEnums = { TestEnum.value1, TestEnum.value3 }
};
var options = new JsonSerializerOptions
{
Converters = { new JsonStringEnumConverter() }
};
// {"enum":["value1","value3"]}
Console.WriteLine(JsonSerializer.Serialize(model1, options));
var model2 = new TestsViewModel_Option2
{
#enum = { TestEnum.value1, TestEnum.value3 }
};
// {"enum":["value1","value3"]}
Console.WriteLine(JsonSerializer.Serialize(model2, options));
}

Compare two Jsons

For example, I have two Jsons: the first one I gave from Test Constructor, the second I gave such a user result of a test (I'm using the JS libriry - survey.js) :
The First :
{
"pages": [
{
"name": "page 1",
"elements": [
{
"type": "checkbox",
"name": "question 1",
"correctAnswer": [
"item1",
"item3"
],
"choices": [
"item1",
"item2",
"item3"
]
},
{
"type": "radiogroup",
"name": "question 2",
"correctAnswer": "item2",
"choices": [
"item1",
"item2",
"item3"
]
}
]
}
]
}
The Second:
{
"question 1":["item3","item1"],
"question 2":"item2"
}
How should I compare those two Jsons by correctAnswer?
I require result such:
question 1 - wrong,
question 2 - right.
as user gsharp mentioned you can use json.NET to achieve this.
Just do the following: create your classes that will hold the survey form:
public class SurveyForm
{
[JsonProperty("pages")]
public IEnumerable<SurveyPage> Pages { get; set; }
}
public class SurveyPage
{
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("elements")]
public IEnumerable<SurveyPageElement> Elements { get; set; }
}
public class SurveyPageElement
{
// I think you can do the rest
}
public class SurveyResult
{
// same as the other classes
}
Do you notice the JsonProperty Attribute? Use that to tell json.NET where to find the related properties in your json. You actually don't need to do that because json.NET does some magic to find the correct properties by it's own but I think it is useful to do it by my own.
Then deserialize the two Jsons:
var surveyForm = JsonConvert.DeserializeObject<SurveyForm>(surveyFormJson);
var surveyResult = JsonConvert.DeserializeObject<Dictionary<string,IEnumerable<string>>>(surveyResultJson);
EDIT: To compare the both do something like this:
foreach(var page in surveyForm.Pages)
{
foreach(var element in page.Elements)
{
if(element.CorrectAnswers.All(surveyResult[element.Name].Contains))
{
// right
}
else
{
// wrong
}
}
}

C# EF Random Order causes related list disappear

I got a Problem with my API. I want to get a random amout of questions back.
The Model of my question has a list of answers and hints:
public class Question
{
public int Id { get; set; }
public string Text { get; set; }
public string Explanation { get; set; }
public Category Category { get; set; }
public ICollection<Answer> Answers { get; set; }
public ICollection<Hint> Hints { get; set; }
}
normally if i call my get method i get a json with all the lists back
return _ctx.Questions.Include(x => x.Answers).Include(x => x.Hints).ToList();
{
"id": 1,
"text": "Test?",
"explanation": "Test",
"category": null,
"answers": [
{
"id": 1,
"text": "Test",
"isCorrect": true
},
{
"id": 2,
"text": "Test1",
"isCorrect": false
},
{
"id": 3,
"text": "Test2",
"isCorrect": false
},
{
"id": 4,
"text": "Test3",
"isCorrect": false
}
],
"hints": [
{
"id": 1,
"text": "..."
},
{
"id": 2,
"text": "..."
}
]
}
But if I want to get random picks with a orderby i only got empty lists
return _ctx.Questions.Include(x => x.Answers).Include(x => x.Hints).OrderBy(o => Guid.NewGuid()).Take(amount).ToList();
{
"id": 1,
"text": "test",
"explanation": "..-",
"category": null,
"answers": [],
"hints": []
}
Does someone have a Idea to fix this?
After sql need to be a list. I got a similar problem a long time ago.
Hope it s help, was with an older version of Ef.
So you have to add a ToList before the OrderBy.
return _ctx.Questions.Include(x => x.Answers).Include(x => x.Hints).ToList().OrderBy(o => Guid.NewGuid()).Take(amount).ToList();

Build JSON Hierarchy from Structured Data

C# | .NET 4.5 | Entity Framework 5
I have data coming back from a SQL Query in the form of ID,ParentID,Name. I'd like to take that data and parse it into a Hierarchical JSON string. So far it seems to be much more of a daunting task than it should be. Since I'm using Entity the data comes back nicely to me as an IEnumerable. Now I believe I just need some form of recursion, but I'm not quite sure where to start. Any help is appreciated.
Data Returns as
id parentId name
1 1 TopLoc
2 1 Loc1
3 1 Loc2
4 2 Loc1A
Code is
public static string GetJsonLocationHierarchy(long locationID)
{
using (EntitiesSettings context = new EntitiesSettings())
{
// IEnumerable of ID,ParentID,Name
context.GetLocationHierarchy(locationID);
}
}
The end result I'd hope would be something like this:
{
"id": "1",
"parentId": "1",
"name": "TopLoc",
"children": [
{
"id": "2",
"parentId": "1",
"name": "Loc1",
"children": [
{
"id": "4",
"parentId": "2",
"name": "Loc1A",
"children": [
{}
]
}
]
},
{
"id": "3",
"parentId": "1",
"name": "Loc2",
"children": [
{}
]
}
]
}
One way to turn a flat table into a hierarchy is to put all your nodes into a dictionary. Then iterate over the dictionary, and for each node, look up its parent and add it to the parent's children. From there, you just need to find the root and serialize it.
Here is an example program to demonstrate the approach:
class Program
{
static void Main(string[] args)
{
IEnumerable<Location> locations = new List<Location>
{
new Location { Id = 1, ParentId = 1, Name = "TopLoc" },
new Location { Id = 2, ParentId = 1, Name = "Loc1" },
new Location { Id = 3, ParentId = 1, Name = "Loc2" },
new Location { Id = 4, ParentId = 2, Name = "Loc1A" },
};
Dictionary<int, Location> dict = locations.ToDictionary(loc => loc.Id);
foreach (Location loc in dict.Values)
{
if (loc.ParentId != loc.Id)
{
Location parent = dict[loc.ParentId];
parent.Children.Add(loc);
}
}
Location root = dict.Values.First(loc => loc.ParentId == loc.Id);
JsonSerializerSettings settings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
Formatting = Formatting.Indented
};
string json = JsonConvert.SerializeObject(root, settings);
Console.WriteLine(json);
}
}
class Location
{
public Location()
{
Children = new List<Location>();
}
public int Id { get; set; }
public int ParentId { get; set; }
public string Name { get; set; }
public List<Location> Children { get; set; }
}
Here is the output:
{
"id": 1,
"parentId": 1,
"name": "TopLoc",
"children": [
{
"id": 2,
"parentId": 1,
"name": "Loc1",
"children": [
{
"id": 4,
"parentId": 2,
"name": "Loc1A",
"children": []
}
]
},
{
"id": 3,
"parentId": 1,
"name": "Loc2",
"children": []
}
]
}

Categories