I have the following situation.
I have a bunch of simple classes, for example this one
public class Student
{
public int Id { get; set; }
public int Age { get; set; }
public decimal AverageMark { get; set; }
public string Name { get; set; }
public string University { get; set; }
}
There is web page for every of them where user can create, edit and delete. When we create Student of update it, we need to validate it.
The problem is that we do not know validation rules during compilation !!!
We have separate web page for administrator where he set up validation criterias,
for example, that Student Age cannot be less then 15 or University have to be equal "SomeUniversity".
As result i have some list of criterias stored in my database
public class Criteria
{
public string PropertyName { get; set; }
public string OperationName { get; set; }
public string OperationValue { get; set; }
}
I have created simple console application for investigation purposes. Here is code
namespace DynamicValidation
{
class Program
{
static void Main(string[] args)
{
//set up students
var student1 = new Student() { Age = 20, AverageMark = 4, Name = "Ihor", University = "Lviv National University" };
var student2 = new Student() { Age = 20, AverageMark = 4, Name = "Taras", University = "Lviv National University" };
var student3 = new Student() { Age = 20, AverageMark = 5, Name = "Marko", University = "" };
var student4 = new Student() { Age = 20, AverageMark = 3, Name = "Tanya", University = "" };
var student5 = new Student() { Age = 22, AverageMark = 4, Name = "Ira", University = "" };
var students = new List<Student>() { student1, student2, student3, student4, student5 };
//set up validation rules
var criteria1 = new Criteria("Age", "Equal", "20");
var criteria2 = new Criteria("AverageMark", "NotLessThan", "4");
var criteria3 = new Criteria("University", "Contains", "Lviv");
var criterias = new List<Criteria>() { criteria1, criteria2, criteria3 };
var result = new List<Student>();
foreach (var currentStudent in students)
{
foreach (var currentCriteria in criterias)
{
object currentPropertyValue = typeof(Student).GetProperty(currentCriteria.PropertyName).GetValue(currentStudent);
//what is next ???!!!
}
}
}
}
public class Student
{
public int Id { get; set; }
public int Age { get; set; }
public decimal AverageMark { get; set; }
public string Name { get; set; }
public string University { get; set; }
}
public class Criteria
{
public string PropertyName { get; set; }
public string OperationName { get; set; }
public string OperationValue { get; set; }
}
}
How can i implement this piece of code ? (expression trees, dynamic ?)
I do not want that you do work for me but maybe there are some articles about this ? (i tried to found but without success)
Maybe some advices about approach ?
Maybe there is some similar open code ?
Or maybe it is already implemented in some libraries ?
Will be thankful for any help :)
You could write a student validator function, see IsValidStudent(Criteria criteria):
public class Student
{
public int Id { get; set; }
public int Age { get; set; }
public decimal AverageMark { get; set; }
public string Name { get; set; }
public string University { get; set; }
public bool IsValidStudent(Criteria criteria)
{
return IsValidByAge(criteria)
&& IsValidByMarks(criteria)
&& IsValidByUniversity(criteria);
}
private bool IsValidByAge(Criteria criteria)
{
switch (criteria.OperationType)
{
case Criteria.Operation.GreaterThan:
return Convert.ToInt32(criteria.OperationValue) > this.Age;
case Criteria.Operation.LessThan:
return Convert.ToInt32(criteria.OperationValue) < this.Age;
case Criteria.Operation.EqualTo:
return Convert.ToInt32(criteria.OperationValue) == this.Age;
default:
return false;
}
}
private bool IsValidByMarks(Criteria criteria)
{
// etc...
}
private bool IsValidByUniversity(Criteria criteria)
{
// etc...
}
}
Usage:
var result = new List<Student>();
foreach (var currentStudent in students)
{
foreach (var currentCriteria in criterias)
{
if (currentStudent.IsValidStudent(currentCriteria))
{
result.Add(currentStudent);
}
}
}
I also extended your Criteria class:
public class Criteria
{
public string PropertyName { get; set; }
public Operation OperationType { get; set; }
public string OperationValue { get; set; }
public enum Operation
{
EqualTo,
GreaterThan,
LessThan,
Contains
}
public Criteria(string propertyName, Operation operationType, string operationValue)
{
this.PropertyName = propertyName;
this.OperationType = operationType;
this.OperationValue = operationValue;
}
}
IMHO, I have found a little better solution then proposed
Now we do not need to change validation logic inside Student class if new property will be added. Also this code can be applied to any other class (not only for Student class as before)
Interface for validation
public interface IValidator
{
bool Validate(object value, object validateWith);
}
Set of implementations
public class ContainsValidator : IValidator
{
public bool Validate(object value, object validateWith)
{
string valueString = Convert.ToString(value);
string validateWithString = Convert.ToString(validateWith);
return valueString.Contains(validateWithString);
}
}
public class StartWithValidator : IValidator
{
public bool Validate(object value, object validateWith)
{
string valueString = Convert.ToString(value);
string validateWithString = Convert.ToString(validateWith);
return valueString.StartsWith(validateWithString);
}
}
public class LengthValidator : IValidator
{
public bool Validate(object value, object validateWith)
{
string valueString = Convert.ToString(value);
int valueLength = Convert.ToInt32(validateWith);
return (valueString.Length == valueLength);
}
}
public class LessThanValidator : IValidator
{
public bool Validate(object value, object validateWith)
{
decimal valueDecimal = Convert.ToDecimal(value);
decimal validateWithDecimal = Convert.ToDecimal(validateWith);
return (valueDecimal < validateWithDecimal);
}
}
public class MoreThanValidator : IValidator
{
public bool Validate(object value, object validateWith)
{
decimal valueDecimal = Convert.ToDecimal(value);
decimal validateWithDecimal = Convert.ToDecimal(validateWith);
return (valueDecimal > validateWithDecimal);
}
}
public class EqualValidator : IValidator
{
public bool Validate(object value, object validateWith)
{
string valueString = Convert.ToString(value);
string validateWithString = Convert.ToString(validateWith);
return (valueString == validateWithString);
}
}
And usages
class Program
{
static void Main(string[] args)
{
//set up students
var student1 = new Student() { Age = 20, AverageMark = 5, Name = "Ihor", University = "Lviv National University" };
var student2 = new Student() { Age = 20, AverageMark = 5, Name = "SomeLongName", University = "Lviv National University" };
var student3 = new Student() { Age = 20, AverageMark = 5, Name = "Taras", University = "Kyiv National University" };
var student4 = new Student() { Age = 20, AverageMark = 5, Name = "Marko", University = "Some University" };
var student5 = new Student() { Age = 20, AverageMark = 4, Name = "Tanya", University = "Lviv National University" };
var student6 = new Student() { Age = 22, AverageMark = 4, Name = "Ira", University = "" };
var students = new List<Student>() { student1, student2, student3, student4, student5, student6 };
//set up validation rules
var criteria1 = new Criteria("Age", "Equal", "20");
var criteria2 = new Criteria("AverageMark", "MoreThen", "4");
var criteria3 = new Criteria("University", "Contains", "National");
var criteria4 = new Criteria("University", "StartWith", "Lviv");
var criteria5 = new Criteria("Name", "Length", "4");
var criterias = new List<Criteria>() { criteria1, criteria2, criteria3, criteria4, criteria5 };
var result = new List<Student>();
foreach (var currentStudent in students)
{
var isValid = true;
foreach (var currentCriteria in criterias)
{
object currentPropertyValue = typeof(Student).GetProperty(currentCriteria.PropertyName).GetValue(currentStudent);
IValidator currentValidator = ValidatorFactory.GetValidator(currentCriteria.OperationName);
bool validationResult = currentValidator.Validate(currentPropertyValue, currentCriteria.OperationValue);
if (!validationResult)
{
isValid = false;
break;
}
}
if (isValid)
result.Add(currentStudent);
}
}
}
In the end the the code of ValidatorFactory
public class ValidatorFactory
{
public static IValidator GetValidator(string validatorName)
{
validatorName = validatorName.ToUpper();
switch (validatorName)
{
case "CONTAINS": return new ContainsValidator();
case "STARTWITH": return new StartWithValidator();
case "EQUAL": return new EqualValidator();
case "MORETHEN": return new MoreThanValidator();
case "LENGTH": return new LengthValidator();
default: throw new Exception("There are not appropriate validator.");
}
}
}
Maybe this will help someone in the future :)
Related
I have following class.
public class Unit
{
public string Name { get; set; }
public double cConvertFromSI { get; set; }
}
public class UnitList
{
public Unit m = new Unit() { Name = "meter", cConvertFromSI = 1 };
public Unit mm = new Unit() { Name = "millimeter", cConvertFromSI = 1000 };
public Unit in = new Unit() { Name = "inch", cConvertFromSI = 39.3701 };
}
And I want to get all 'Unit' from 'UnitList'.
// I want to do something like
UnitList MyUnitList = new UnitList();
foreach (Unit Unit in MyUnitList)
{
// do something with each 'Unit'
}
How can I do it?
You can implement the IEnumerable<Unit> interface.
public class UnitList : IEnumerable<Unit>
{
public Unit m = new Unit() { Name = "meter", cConvertFromSI = 1 };
public Unit mm = new Unit() { Name = "millimeter", cConvertFromSI = 1000 };
public Unit in_ = new Unit() { Name = "inch", cConvertFromSI = 39.3701 };
public IEnumerator<Unit> GetEnumerator()
{
yield return m;
yield return mm;
yield return in_;
//...
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
=> GetEnumerator();
}
That way you can iterate through a UnitList instance with foreach.
foreach ( Unit u in new UnitList() )
{
}
However, it would probably be more reasonable to just use a List or Array property instead.
I hope you have a good reason to not just use a List<Unit>, but this would solve getting the properties dynamically.
public class Unit
{
public string Name { get; set; }
public double cConvertFromSI { get; set; }
public override string ToString()
{
return $"{Name} {cConvertFromSI}";
}
}
public class UnitList
{
public Unit m { get; set; } = new Unit() {Name = "meter", cConvertFromSI = 1};
public Unit mm { get; set; } = new Unit() {Name = "millimeter", cConvertFromSI = 1000};
public Unit iN { get; set; } = new Unit() {Name = "inch", cConvertFromSI = 39.3701}; // in is a reserved keyword btw
}
class Program
{
static void Main(string[] args)
{
var unitList = new UnitList();
var propertyInfos = typeof(UnitList).GetProperties().Where(p => p.PropertyType == typeof(Unit));
var units = propertyInfos.Select(propertyInfo => (Unit) propertyInfo.GetValue(unitList)).ToList();
units.ForEach(u => { Console.WriteLine(u.ToString()); });
}
}
Note that I added {get; set;} at the end of UnitList fields to make them properties.
If you want to keep them as fields then you would need to get the units like this
var fields = typeof(UnitList).GetFields().Where(p => p.FieldType == typeof(Unit));
var units = fields.Select(propertyInfo => (Unit) propertyInfo.GetValue(unitList)).ToList();
Is there any scope for improvement in my program which converts flat db entity to a tree data structure.
I don't want to loose the Generic flexibility as i should be able to use the same method for any other DBEntity class
Interface for db entity class
public interface IDbEntityNode
{
int Id { get; set; }
int ParentId { get; set; }
}
Example of db Entity class
public class ExceptionCategory :IDbEntityNode
{
public int Id { get; set; }
public int ParentId { get; set; }
public string Data { get; set; }
public ExceptionCategory(string data, int id, int parentId)
{
Id = id;
ParentId = parentId;
Data = data;
}
}
Generic class which holds the structure of tree node
public class GenericNode<T>
{
public T NodeInformation { get; set; }
public GenericNode<T> Parent { get; set; }
public List<GenericNode<T>> Children { get; set; } = new List<GenericNode<T>>();
}
Method which coverts flat list to tree
public static List<GenericNode<T>> CreateGenericTree<T>(List<T> flatDataObject,Func<T,bool> IsRootNode) where T : IDbEntityNode
{
var lookup = new Dictionary<int, GenericNode<T>>();
var rootNodes = new List<GenericNode<T>>();
var noOfElements = flatDataObject.Count;
for (int element = 0; element < noOfElements; element++)
{
GenericNode<T> currentNode;
if (lookup.TryGetValue(flatDataObject[element].Id, out currentNode))
{
currentNode.NodeInformation = flatDataObject[element];
}
else
{
currentNode = new GenericNode<T>() { NodeInformation = flatDataObject[element] };
lookup.Add(flatDataObject[element].Id, currentNode);
}
if (IsRootNode(flatDataObject[element]))
{
rootNodes.Add(currentNode);
}
else
{
GenericNode<T> parentNode;
if (!lookup.TryGetValue(flatDataObject[element].ParentId, out parentNode))
{
parentNode = new GenericNode<T>();
lookup.Add(flatDataObject[element].ParentId, parentNode);
}
parentNode.Children.Add(currentNode);
currentNode.Parent = parentNode;
}
}
return rootNodes;
}
Execution:
private static void Main(string[] args)
{
List<IDbEntityNode> flatDataStructure = new List<IDbEntityNode>
{
new ExceptionCategory("System Exception",1,0),
new ExceptionCategory("Index out of range",2,1),
new ExceptionCategory("Null Reference",3,1),
new ExceptionCategory("Invalid Cast",4,1),
new ExceptionCategory("OOM",5,1),
new ExceptionCategory("Argument Exception",6,1),
new ExceptionCategory("Argument Out Of Range",7,6),
new ExceptionCategory("Argument Null",8,6),
new ExceptionCategory("External Exception",9,1),
new ExceptionCategory("Com",10,9),
new ExceptionCategory("SEH",11,9),
new ExceptionCategory("Arithmatic Exception",12,1),
new ExceptionCategory("DivideBy0",13,12),
new ExceptionCategory("Overflow",14,12),
};
var tree = CreateGenericTree(flatDataStructure, IsRootNode);
}
private static bool IsRootNode(IDbEntityNode dbEntity)
{
bool isRootNode = false;
if (dbEntity.ParentId == 0 )
isRootNode = true;
return isRootNode;
}
Created a generic approach, table objects need to follow the dbSet interface and TreeNode objects need to follow the ITreeNode. I used binarySerach to make this as fast as possible. No recursion needed. The logic ensures that you do not need to have the items in a particular order. I did not throw an error when out of the loop when there are still unassigned objects, this can be easy added.
using System.Collections.Generic;
public interface ITreeNode
{
string ParentId { get; set; }
string Id { get; set; }
dbItem item { get; set; }
List<ITreeNode> Nodes { get; set; }
}
public class TreeNode : ITreeNode
{
public TreeNode()
{ }
public string ParentId { get; set; }
public string Id { get; set; }
public dbItem item { get; set; }
public List<ITreeNode> Nodes { get; set; }
}
public class dbItem
{
public string ParentId { get; set; }
public string Id { get; set; }
public string Name { get; set; }
}
class app
{
static void Main()
{
List<dbItem> dbSet = new List<dbItem>();
dbSet.Add(new dbItem() { Id = "5", ParentId = "1", Name = "Jan" });
dbSet.Add(new dbItem() { Id = "25", ParentId = "1", Name = "Maria" });
dbSet.Add(new dbItem() { Id = "1", Name = "John" });
dbSet.Add(new dbItem() { Id = "8", ParentId = "2", Name = "Cornelis" });
dbSet.Add(new dbItem() { Id = "2", Name = "Ilse" });
dbSet.Add(new dbItem() { Id = "3", Name = "Nick" });
dbSet.Add(new dbItem() { Id = "87", ParentId = "5", Name = "Rianne" });
dbSet.Add(new dbItem() { Id = "67", ParentId = "3000", Name = "Rianne" });
dbSet.Add(new dbItem() { Id = "3000", Name = "Max" });
List<TreeNode> result = BuildTree<TreeNode>(dbSet);
}
private class ParentComparer<T> : IComparer<ITreeNode> where T: ITreeNode
{
public int Compare(ITreeNode x, ITreeNode y)
{
if (x.ParentId == null) return -1; //have the parents first
return x.ParentId.CompareTo(y.ParentId);
}
}
private class IdComparer<T> : IComparer<ITreeNode> where T : ITreeNode
{
public int Compare(ITreeNode x, ITreeNode y)
{
return x.Id.CompareTo(y.Id);
}
}
static private List<T> BuildTree<T> (List<dbItem> table) where T: ITreeNode, new()
{
//temporary list of tree nodes to build the tree
List<T> tmpNotAssignedNodes = new List<T>();
List<T> tmpIdNodes = new List<T>();
List<T> roots = new List<T>();
IComparer<T> pc = (IComparer<T>) new ParentComparer<T>();
IComparer<T> ic = (IComparer<T>) new IdComparer<T>();
foreach (dbItem item in table)
{
T newNode = new T() { Id = item.Id, ParentId = item.ParentId, item = item };
newNode.Nodes = new List<ITreeNode>();
T dummySearchNode = new T() { Id = item.ParentId, ParentId = item.ParentId };
if (string.IsNullOrEmpty(item.ParentId))
roots.Add(newNode);
else
{
int parentIndex = tmpIdNodes.BinarySearch(dummySearchNode, ic);//Get the parent
if (parentIndex >=0)
{
T parent = tmpIdNodes[parentIndex];
parent.Nodes.Add(newNode);
}
else
{
parentIndex = tmpNotAssignedNodes.BinarySearch(dummySearchNode, pc);
if (parentIndex < 0) parentIndex = ~parentIndex;
tmpNotAssignedNodes.Insert(parentIndex, newNode);
}
}
dummySearchNode.ParentId = newNode.Id;
//Cleanup Unassigned
int unAssignedChildIndex = tmpNotAssignedNodes.BinarySearch(dummySearchNode, pc);
while (unAssignedChildIndex >= 0 && unAssignedChildIndex < tmpNotAssignedNodes.Count)
{
if (dummySearchNode.ParentId == tmpNotAssignedNodes[unAssignedChildIndex].ParentId)
{
T child = tmpNotAssignedNodes[unAssignedChildIndex];
newNode.Nodes.Add(child);
tmpNotAssignedNodes.RemoveAt(unAssignedChildIndex);
}
else unAssignedChildIndex--;
}
int index = tmpIdNodes.BinarySearch(newNode, ic);
tmpIdNodes.Insert(~index, newNode);
}
return roots;
}
}
Try following solution using recursive code :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
public static List<IDbEntityNode> flatDataStructure = null;
public static Dictionary<int?, List<IDbEntityNode>> dict = null;
static void Main(string[] args)
{
flatDataStructure = new List<IDbEntityNode>
{
new ExceptionCategory("System Exception",1,0),
new ExceptionCategory("Index out of range",2,1),
new ExceptionCategory("Null Reference",3,1),
new ExceptionCategory("Invalid Cast",4,1),
new ExceptionCategory("OOM",5,1),
new ExceptionCategory("Argument Exception",6,1),
new ExceptionCategory("Argument Out Of Range",7,6),
new ExceptionCategory("Argument Null",8,6),
new ExceptionCategory("External Exception",9,1),
new ExceptionCategory("Com",10,9),
new ExceptionCategory("SEH",11,9),
new ExceptionCategory("Arithmatic Exception",12,1),
new ExceptionCategory("DivideBy0",13,12),
new ExceptionCategory("Overflow",14,12),
};
dict = flatDataStructure.GroupBy(x => x.ParentId, y => y)
.ToDictionary(x => x.Key, y => y.ToList());
GenericNode<IDbEntityNode> root = new GenericNode<IDbEntityNode>();
root.Parent = null;
int rootId = 0;
root.NodeInformation.Id = rootId;
root.NodeInformation.name = "root";
root.NodeInformation.ParentId = null;
CreateGenericTree(root);
}
public static void CreateGenericTree<T>(GenericNode<T> parent) where T : IDbEntityNode, new()
{
if (dict.ContainsKey(parent.NodeInformation.Id))
{
List<IDbEntityNode> children = dict[parent.NodeInformation.Id];
foreach (IDbEntityNode child in children)
{
GenericNode<T> newChild = new GenericNode<T>();
if (parent.Children == null) parent.Children = new List<GenericNode<T>>();
parent.Children.Add(newChild);
newChild.NodeInformation.Id = child.Id;
newChild.NodeInformation.ParentId = parent.NodeInformation.Id;
newChild.NodeInformation.name = child.name;
newChild.Parent = parent;
CreateGenericTree(newChild);
}
}
}
}
public class GenericNode<T> where T : new()
{
public T NodeInformation = new T();
public GenericNode<T> Parent { get; set; }
public List<GenericNode<T>> Children { get; set; }
}
public class IDbEntityNode
{
public int Id { get; set; }
public int? ParentId { get; set; }
public string name { get; set; }
}
public class ExceptionCategory : IDbEntityNode
{
public string Data { get; set; }
public ExceptionCategory(string data, int id, int parentId)
{
Id = id;
ParentId = parentId;
Data = data;
}
}
}
How can I add the following data on the table into a list called Vehicles?
public class criterias
{
public double values { get; set; }
public double time { get; set; }
}
public class movChannels
{
public string name { get; set; }
public IList<criterias> criteria = new List<criterias>();
}
public class stepsList
{
public string steps { get; set; }
public IList<movChannels> stepChannelsCriteria = new List<movChannels>();
}
public class vehicles
{
public int vehID { get; set; }
public string vehDescription { get; set; }
public IList<stepsList> vehValCriteria = new List<stepsList>();
}
Now, how can I add the data that I have in the table shown into a list called Vehicles? I will create other vehicles later...
You had several bad decisions, some were design flaws and some were minor C# naming convention violations.
Couple of worth mentions flaws:
vehID should have been a string and not int (Example "XPT")
Movment has Name, Value and Time. It doesn't have a list of Values and Times.
Creation:
List<Vehicle> vehicles = new List<Vehicle>();
Vehicle vehicle = new Vehicle()
{
Id = "XPT",
Description = "Average Car",
Steps = new List<Step>()
{
new Step() {
Name = "move car",
Movements = new List<Movement>()
{
new Movement("engage 1st gear", 1, 1),
new Movement("reach 10kph", 10, 5),
new Movement("maintain 10kph", 10, 12),
}
},
new Step() {
Name = "stop car",
Movements = new List<Movement>()
{
new Movement("reach 0kph", 10, 4),
new Movement("put in neutral", 0, 1),
new Movement("turn off vehicle", 0, 0),
}
}
}
};
vehicles.Add(vehicle);
Entities:
public class Movement
{
public string Name { get; set; }
public double Values { get; private set; }
public double Time { get; private set; }
public Movement(string name, double values, double time)
{
Name = name;
Values = values;
Time = time;
}
}
public class Step
{
public string Name { get; set; }
public IList<Movement> Movements { get; set; }
}
public class Vehicle
{
public string Id { get; set; } // Should be changed to string
public string Description { get; set; }
public IList<Step> Steps { get; set; }
}
You should create your classes like the following:
public class criterias
{
public double values { get; set; }
public double time { get; set; }
}
public class movChannels
{
public movChannels
{
criteria = new List<criterias>();
}
public string name { get; set; }
public IList<criterias> criteria { get; set; }
}
public class stepsList
{
public stepsList
{
stepChannelsCriteria = new List<movChannels>();
}
public string steps { get; set; }
public IList<movChannels> stepChannelsCriteria { get; set; }
}
public class vehicles
{
public vehicles
{
vehValCriteria = new List<stepsList>();
}
public int vehID { get; set; }
public string vehDescription { get; set; }
public IList<stepsList> vehValCriteria { get; set; }
public movChannels movments { get; set; }
}
What about that?
public class VehiclesViewModel
{
public List<vehicles> Vehicles { get; private set; }
public void Initalize()
{
this.Vehicles = new List<vehicles>();
var vehicle = new vehicles
{
vehID = 1,
vehDescription = "firstDescription",
};
var stepsList = new stepsList
{
steps = "firstStep",
};
var movChannel = new movChannels
{
name = "firstChannel",
};
var criteria = new criterias
{
values = 0.5,
time = 0.5
};
movChannel.criteria.Add(criteria);
stepsList.stepChannelsCriteria.Add(movChannel);
vehicle.vehValCriteria.Add(stepsList);
this.Vehicles.Add(vehicle);
}
}
it seems in your table the VehicleId is of type string. Make sure your VehicleId property in Vehicle class also matches the same.
You can use the collection initializers to set the values of child objects like this way:
var data = new vehicles()
{
vehID = 1,
vehDescription = "Average Car",
vehValCriteria = new List<stepsList>()
{
new stepsList()
{
steps = "Move car",
stepChannelsCriteria = new List<movChannels>()
{
new movChannels()
{
name = "engage firstgear",
criteria = new List<criterias>()
{
new criterias()
{
values = 1,
time = 1
},
}
},
new movChannels()
{
name = "reach 10kph",
criteria = new List<criterias>()
{
new criterias()
{
values = 10,
time = 5
},
}
},
new movChannels()
{
name = "maintain 10kph",
criteria = new List<criterias>()
{
new criterias()
{
values = 10,
time = 12
},
}
}
}
},
new stepsList()
{
steps = "stop car",
stepChannelsCriteria = new List<movChannels>()
{
new movChannels()
{
name = "reach okph",
criteria = new List<criterias>()
{
new criterias()
{
values = 10,
time = 4
},
}
},
new movChannels()
{
name = "put in neutral",
criteria = new List<criterias>()
{
new criterias()
{
values = 0,
time = 1
},
}
},
new movChannels()
{
name = "turn off vehicle",
criteria = new List<criterias>()
{
new criterias()
{
values = 0,
time = 0
},
}
}
}
}
}
};
You can fill your list by moving from top to bottom, like
Create Criterias List then Create movChannel object and add that list
to Criterias object and so on
However if you want to avoid this way, there is another way. If you are using Linq To List then follow this
Get a simple flat object to a list object
var TableData = db.Tablename.Tolist();
Then fill your own object like this
Vehicles finalList = TableData.Select(a => new Vehicles()
{
vehID = a.Id,
vehDescription = a.des,
vehValCriteria = TableData.Where(b => b.StepslistId == a.StepslistId)
.Select(c => new StepsList()
{
steps = c.Steps,
stepChannelsCriteria = TableData.Where(d => d.channelId == c.channelId)
.select(e => new MovChannels()
{
name = e.name,
criteria = TableData.Where(f => f.criteriasId = e.criteriasId)
.Select(g => new Criterias()
{
values = g.Values,
time = g.Time
}).ToList()
}).ToList()
}).ToList()
}).ToList();
This is standard way to fill list within list
Using C# with LINQ, how can I merge two lists of different objects, say, Seminar and Conference?
They have some common and some different fields/properties and do not share unique id.
class Seminar
{
int id,
DateTime joinDate,
string name
}
class Conference
{
Guid confNumber,
DateTime joinDate
Type type
}
I have a list of:
List<Seminar>
List<Conference>
I need to merge them into a super List:
List<Object>
A code snippet would be great help.
If you just want a single List<object> containing all objects from both lists, that's fairly simple:
List<object> objectList = seminarList.Cast<object>()
.Concat(conferenceList)
.ToList();
If that's not what you want, then you'll need to define what you mean by "merge".
Following code works fine for me, if this is your definition of Merge
One solution
List<A> someAs = new List<A>() { new A(), new A() };
List<B> someBs = new List<B>() { new B(), new B { something = new A() } };
List<Object> allS = (from x in someAs select (Object)x).ToList();
allS.AddRange((from x in someBs select (Object)x).ToList());
Where A and B are some classes as follows
class A
{
public string someAnotherThing { get; set; }
}
class B
{
public A something { get; set; }
}
Another Solution
List<A> someAs = new List<A>() { new A(), new A() };
List<B> someBs = new List<B>() { new B(), new B { something = string.Empty } };
List<Object> allS = (from x in someAs select (Object)new { someAnotherThing = x.someAnotherThing, something = string.Empty }).ToList();
allS.AddRange((from x in someBs select (Object)new { someAnotherThing = string.Empty, something = x.something}).ToList());
Where A and B are having class definition as
class A
{
public string someAnotherThing { get; set; }
}
class B
{
public string something { get; set; }
}
Simple method of pure code
internal class Person
{
public int Id { get; set; }
public string UserName { get; set; }
}
internal class User
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
internal class UserPerson
{
public int Id { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}
private static void Main(string[] args)
{
Person[] people = new Person[3] { new Person { Id = 1, UserName = "AliUserName" }, new Person { Id = 2, UserName = "MortezaUserName" }, new Person { Id = 3, UserName = "SalarUserName" } };
User[] users = new User[4] { new User { FirstName = "ali", LastName = "Barzegari" }, new User { FirstName = "Morteza", LastName = "Sefidi" }, new User { FirstName = "Salar", LastName = "Pirzadeh" }, new User { FirstName = "Babak", LastName = "Hasani" } };
UserPerson[] userPeople = new UserPerson[people.Length > users.Length ? people.Length : users.Length];
if (people.Length > users.Length)
for (int i = 0; i < people.Length; i++)
{
userPeople[i] = new UserPerson
{
Id = people[i].Id,
UserName = people[i].UserName,
FirstName = users.Length <= i ? "" : users[i].FirstName,
LastName = users.Length <= i ? "" : users[i].LastName
};
}
else
for (int i = 0; i < users.Length; i++)
{
userPeople[i] = new UserPerson
{
Id = people.Length <= i ? 0 : people[i].Id,
UserName = people.Length <= i ? "" : people[i].UserName,
FirstName = users[i].FirstName,
LastName = users[i].LastName
};
}
Console.ReadLine();
}
I'm working with KnockoutMVC and it requires strongly type models to use inside the VIEW. I have tried multiple variations of the examples on KnockoutMVC's site including using ENUMS and still could not get it to work. Perhaps this is a problem with the setup of my models.
MODELS
public class PhoneNumber
{
public List<NumberTypeClass> Types { get; set; }
//public NumberType enumType { get; set; }
//public enum NumberType
//{
// Work,
// Home,
// Mobile,
// Fax
//}
private string _number;
[StringLength(14, MinimumLength = 10, ErrorMessage = "Please use (123) 456-7890 format"), Required]
public string Number
{
get
{
this._number = BeautifyPhoneNumber(this._number);
return this._number;
}
set
{
this._number = value;
}
}
public string Extension { get; set; }
public static String BeautifyPhoneNumber(string numberToBeautify)
{
//beautifyNumberCode
}
}
public class NumberTypeClass
{
public int Id { get; set; }
public string NumberType { get; set; }
}
public class VendorsEditorVendorModel
{
public string FirstName {Get;set;}
public string LastName {get;set;}
public List<Address> Address {get;set;}
public List<PhoneNumber> Phones {get;set;}
}
public class VendorsEditorModel
{
public List<VendorsEditorVendorModel> Vendors {get;set;}
}
CONTROLLER
public class VendorsEditorController : BaseController
{
public ActionResult CreateVendors()
{// VendorsEditor/CreateVendors
var vendor = new VendorsEditorModel();
vendor.Vendors = new List<VendorsEditorVendorModel>();
vendor.Vendors[0].Phones[0].Types = new List<NumberTypeClass>
{
new NumberTypeClass{Id = 0, TypeName = "Mobile"},
new NumberTypeClass{Id = 0, TypeName = "Work"},
new NumberTypeClass{Id = 0, TypeName = "Home"}
};//this throws an error because there is no Vendors[0] ...but how would i populate this list for every Vendor?
return View(vendor);
}
}
You cannot call an empty collection by index [x]. You need to fill your collection from a database or what not before you can access items in it. If you are just trying to add items to a collection, this is how you do it:
var vendor = new VendorsEditorModel
{
Vendors = new List<VendorsEditorVendorModel>
{
new VendorsEditorVendorModel
{
Phones = new List<PhoneNumber>
{
new PhoneNumber
{
Types = new List<NumberTypeClass>
{
new NumberTypeClass {Id = 0, NumberType = "Mobile"}
}
}
}
}
}
};
If you just want to add the types to an already populated collection, you can do the following:
foreach (var phone in vendor.Vendors.SelectMany(item => item.Phones))
{
phone.Types = new List<NumberTypeClass>
{
new NumberTypeClass{Id = 0, NumberType = "Mobile"},
new NumberTypeClass{Id = 0, NumberType = "Work"},
new NumberTypeClass{Id = 0, NumberType = "Home"}
};
}