How to make a class that holds classes? - c#

I want to make a program that lists recipes, their ingredients and only ingredients on a separate page.
I was thinking of creating classes "Ingredient" and "Recipe". The "Recipe" class will have a name and description properties and a Dictionary<Ingredient, int> property. I was thinking of filling the dictionary with "ingredient" classes and the amount they are needed in, for example let's say I create an instance of the "Recipe" class and name it "Pancakes". I will add a new Ingredient instance, named "Milk" to the dictionary with the amount 100 (for example) and do the same for "eggs" and so forth.
Is this the correct way to go about such a thing, as I will want to create references or links to the ingredients themselves on the "ingredients" page?

The way you have described will work correctly. However, a common solution for what you have described is the decorator pattern. The decorator pattern is used for creating dynamic objects.
https://www.youtube.com/watch?v=j40kRwSm4VE
This example is using pizza and toppings but it's essentially the same concept.

Something like this would work:
public enum UnitOfMeasurement
{
Grams,
Milliliters
}
public class Ingredient
{
public UnitOfMeasurement UnitOfMeasurement { get; set; }
public decimal Unit { get; set; }
public string Name { get; set; }
}
public class Recipe
{
public string Description { get; set; }
public List<Ingredient> Ingredients { get; set; }
public List<string> RecipeSteps { get; set; }
}
I might be tempted to create a class or some more complex object for recipe steps as well but that largely depends on what you want to do with this.

I was asked to do something like this recently in an interview, had to put in a recipe and then print it out by overriding the ToString() method. Obviously it can be cleaned up a lot, but the general idea is there.
came up with this code:
using System;
namespace cSHARP
{
class Program
{
static void Main(string[] args)
{
Recipe myRecipe = new Recipe();
Ingredient myIngredient = new Ingredient();
Ingredient myIngredient2 = new Ingredient();
myRecipe.Name = "Fish and Chips";
myIngredient.Name = "Fish";
myIngredient.Amount = 3;
myIngredient.AmountType = "pieces";
myIngredient2.Name = "Chips";
myIngredient2.Amount = 1;
myIngredient2.AmountType = "regular serving";
myRecipe.Ingredients = new Ingredient[2];
myRecipe.Ingredients[0] = myIngredient;
myRecipe.Ingredients[1] = myIngredient2;
Console.WriteLine(myRecipe.ToString());
}
}
public class Recipe
{
public string Name { get; set; }
public Ingredient[] Ingredients { get; set; }
public override string ToString()
{
string retVal = "";
retVal = retVal + "\n Recipe Name is " + this.Name;
retVal = retVal + "\n Ingredient one is "
+ this.Ingredients[0].Amount.ToString() + " "
+ this.Ingredients[0].AmountType.ToString() + " of "
+ this.Ingredients[0].Name;
retVal = retVal + "\n Ingredient two is "
+ this.Ingredients[1].Amount.ToString() + " "
+ this.Ingredients[1].AmountType.ToString() + " of "
+ this.Ingredients[1].Name;
return (retVal);
}
}
public class Ingredient
{
public string Name { get; set; }
public int Amount { get; set; }
public string AmountType { get; set; }
}
}

Related

C# Searching MongoDB string that starts with "xyz"

I try to code a Storagesystem and i got one Problem and dont know how to solve it...
public static List<string> GetAllItemsFromDB(string Searchbar)
{
List<string> retList = new List<string>();
retList.Clear();
var filter = Builders<DB_Package_Item>.Filter.Eq(Item => Item.Item_Name, Searchbar);
var ItemsMatch = Item_DB.Find(filter).ToList();
foreach (var Item in ItemsMatch.ToList())
{
retList.Add(Item.Item_Name);
}
return retList;
}
This Works. But when i change the filter to:
var filter = Builders<DB_Package_Item>.Filter.ElemMatch(Item => Item.Item_Name, Searchbar);
It crasches as soon i type any char in the searchbar with Error code : "System.InvalidOperationException: "The serializer for field 'Item_Name' must implement IBsonArraySerializer and provide item serialization info."
"
I just dont get it why...
This is the Data_Package for MongoDB
public class DB_Package_Item
{
public Guid Id { get; set; }
public int Item_ID { get; set; }
public int Box_ID { get; set; }
public string Item_Name { get; set; }
public int Quantity { get; set; }
public string Partnumber { get; set; }
public string Supplier { get; set; }
}
Thx for every help!
FilterDefinitionBuilder<TDocument>.ElemMatch<TItem> or $elemMatch operator is for searching the item in the array field, which is not appropriate for your scenario to search (term) for a string field.
You need the $regex operator which is FilterDefinitionBuilder<TDocument>.Regex in C# syntax.
using System.Text.RegularExpressions;
var filter = Builders<DB_Package_Item>.Filter.Regex(Item => Item.Item_Name
, new Regex("^" + Searchbar));
For case-insensitive:
new Regex("^" + Searchbar, RegexOptions.IgnoreCase)
Or
var filter = Builders<DB_Package_Item>.Filter.Regex(Item => Item.Item_Name
, new BsonRegularExpression("^" + Searchbar));
For case-insensitive,
new BsonRegularExpression("^" + Searchbar, "i")

Loading instance of entity takes more than 1 second

I ran into one interesting thing in EF. If we get child entity using base entity, loading entities takes more time. My model looks like this:
public abstract class BaseDocument
{
public Guid Id { get; set; }
public string Name { get; set; }
}
public abstract class ComplexDocument : BaseDocument
{
public string AuthorName { get; set; }
}
public abstract class SimpleDocument : BaseDocument
{
public int Level { get; set; }
}
public abstract class OfficeDocument : ComplexDocument
{
public string OfficeName { get; set; }
}
public abstract class ClassDocument : SimpleDocument
{
public string HeadName { get; set; }
}
public class WordDocument : OfficeDocument
{
public int PagesCount { get; set; }
}
public class ExcelDocument : OfficeDocument
{
public int SheetsCount { get; set; }
}
public class TextDocument : ClassDocument
{
public int LinesCount { get; set; }
}
I am using the TPT approach. Here is the inheritance tree
Here is my context class:
public class Context : DbContext
{
public Context() : base(#"Server=(localdb)\MSSQLLocalDB;Database=EFSIX;Trusted_Connection=True;")
{
Database.CreateIfNotExists();
}
public DbSet<BaseDocument> BaseDocuments { get; set; }
public DbSet<ComplexDocument> ComplexDocuments { get; set; }
public DbSet<SimpleDocument> SimpleDocuments { get; set; }
public DbSet<OfficeDocument> OfficeDocuments { get; set; }
public DbSet<ClassDocument> ClassDocuments { get; set; }
public DbSet<ExcelDocument> ExcelDocuments { get; set; }
public DbSet<WordDocument> WordDocuments { get; set; }
public DbSet<TextDocument> TextDocuments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BaseDocument>().ToTable("BaseDocuments");
modelBuilder.Entity<ComplexDocument>().ToTable("ComplexDocuments");
modelBuilder.Entity<SimpleDocument>().ToTable("SimpleDocuments");
modelBuilder.Entity<OfficeDocument>().ToTable("OfficeDocuments");
modelBuilder.Entity<ExcelDocument>().ToTable("ExcelDocuments");
modelBuilder.Entity<WordDocument>().ToTable("WordDocuments");
modelBuilder.Entity<ClassDocument>().ToTable("ClassDocuments");
modelBuilder.Entity<TextDocument>().ToTable("TextDocuments");
}
public IQueryable<T> GetEntities<T>() where T : class
{
return Set<T>();
}
}
I'm creating some data:
static void CreateTestData()
{
using (Context context = new Context())
{
for (int i = 0; i < 20; i++)
{
ExcelDocument excel = new ExcelDocument()
{
Id = Guid.NewGuid(),
AuthorName = $"ExcelAuthor{i}",
Name = $"Excel{i}",
OfficeName = $"ExcelOffice{i}",
SheetsCount = (i + 1) * 10
};
context.ExcelDocuments.Add(excel);
WordDocument word = new WordDocument()
{
Id = Guid.NewGuid(),
AuthorName = $"WordAuthor{i}",
Name = $"Word{i}",
OfficeName = $"WordOffice{i}",
PagesCount = (i + 2) * 10
};
context.WordDocuments.Add(word);
TextDocument text = new TextDocument()
{
Id = Guid.NewGuid(),
Name = $"Text{i}",
LinesCount = (i + 3) * 10,
HeadName = $"Head{i}",
Level = i + 5
};
context.TextDocuments.Add(text);
}
context.SaveChanges();
}
}
I made some two methods for getting WordDocument from db. One of them using BaseDocument and another one using WordDocument. Both returns 20 instances of WordDocument:
static long ReadBaseDoc()
{
using (Context context = new Context())
{
var words= context.GetEntities<BaseDocument>().Where(e => e.Name.StartsWith("Word"));
Stopwatch stopwatch = Stopwatch.StartNew();
var instacnes = excel.ToList();
stopwatch.Stop();
return stopwatch.ElapsedMilliseconds;
}
}
static long ReadWordDoc()
{
using (Context context = new Context())
{
var words = context.GetEntities<WordDocument>().Where(e => e.Name.StartsWith("Word"));
Stopwatch stopwatch = Stopwatch.StartNew();
var instacnes = words.ToList();
stopwatch.Stop();
return stopwatch.ElapsedMilliseconds;
}
}
I tested moth method separately, several times, in average method ReadWordDoc takes 25ms and method ReadBaseDoc takes 52ms (instances are the same ).
It's not too big problem now, but when we have complex inheritance it takes more than 1 second. I created 10 classes and inherited from BaseDocument. After that I executed ReadBaseDoc and ReadWordDoc methods. ReadWordDoc took 25ms and ReadBaseDoc took 1023ms. Instances are the same, why ReadBaseDoc takes more time? What is the better way to avoid this kind of problems in EF?
Take a look here. There are ways to make EF faster, but in those complex scenarios ORM just creates more problems than it solves.
One way in your case would be to try to change the inheritance to TablePerType, MAYBE it will be a little bit faster.
Other way would be to locate the slow request and use Dapper for them - it will be much faster.
Last way would be to create a Repository with live cache that loads the full database into memory and keeps it up to date - this should be a singleton in an app. If you have more than one app using the same database, you need to hookup data change triggers.
In general, I would say for slow (and relatively simple) queries like yours, use Dapper + AutoMapper. Keep EF so that your database stays synchronized with your classes, but do not rely on it for queries.
If you really want to stick to ORM, I think you need to switch nHibernate. Haven't try it myself, but form what I read, it is superior in almost every possible way, that includes performance and startup time.

Deserialize JSON Object into Class

I'm having a little trouble deserializing a JSON object to a class (using JSON.NET), and hoping someone can point me in the right direction. Below is a snippet of code I'm trying, and been testing at dotnetfiddle
Here's a sample of the JSON:
{
"`LCA0001": {
"23225007190002": "1",
"23249206670003": "1",
"01365100070018": "5"
},
"`LCA0003": {
"23331406670018": "1",
"24942506670004": "1"
},
"`LCA0005": {
"01365100070018": "19"
}
}
I'm trying to use this code:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
public class Program
{
public static void Main()
{
string json = "{\"`LCA0001\": {\"23225007190002\": \"1\",\"23249206670003\": \"1\",\"01365100070018\": \"5\"},\"`LCA0003\": {\"23331406670018\": \"1\",\"24942506670004\": \"1\"},\"`LCA0005\": {\"01365100070018\": \"19\"}}";
Console.WriteLine(json);
Console.WriteLine();
//This works
Console.Write("Deserialize without class");
var root = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, int>>>(json);
foreach (var locationKvp in root)
{
foreach (var skuKvp in locationKvp.Value)
{
Console.WriteLine("location: " + locationKvp.Key + ", sku: " + skuKvp.Key + ", qty: " + skuKvp.Value);
}
}
//Why doesn't this work?
Console.Write("\nDeserialize with class");
var root2 = JsonConvert.DeserializeObject<InventoryLocations>(json);
foreach (var locationKvp in root2.InventoryLocation)
{
foreach (var skuKvp in locationKvp.Value)
{
Console.WriteLine("location: " + locationKvp.Key + ", sku: " + skuKvp.Key + ", qty: " + skuKvp.Value);
}
}
}
}
class InventoryLocations
{
public Dictionary<Location, Dictionary<Sku, Qty>> InventoryLocation { get; set; }
}
public class Location
{
public string location { get; set; }
}
public class Sku
{
public string sku { get; set; }
}
public class Qty
{
public int qty { get; set; }
}
Is there a reason why deserializing into a Class doesn't work? Am I just defining the classes incorrectly?
I see two problems here: one is using classes as the dictionary keys - the JSON has simple strings there (and cannot have anything else really), so that won't work.
The second problem is that deserialization of JSON to classes works by matching keys to properties - so it converts something like
{
"prop1": "value1",
"prop2": "value2"
}
to an instance of:
public class MyClass {
public string prop1 { get; set; }
public string prop2 { get; set; }
}
In your case this cannot work because in your JSON all keys are not valid property names. You have to stick with the deserialization to a dictionary
One of the ways to generate the classes from JSON is using Visual Studio.
Navigate to Edit -> Paste Special -> Paste JSON As Classes. For posted JSON in question, following classes are generated.
public class Rootobject
{
public LCA0001 LCA0001 { get; set; }
public LCA0003 LCA0003 { get; set; }
public LCA0005 LCA0005 { get; set; }
}
public class LCA0001
{
public string _23225007190002 { get; set; }
public string _23249206670003 { get; set; }
public string _01365100070018 { get; set; }
}
public class LCA0003
{
public string _23331406670018 { get; set; }
public string _24942506670004 { get; set; }
}
public class LCA0005
{
public string _01365100070018 { get; set; }
}
In addition to MiMo's answer, you can use a ContractResolver to serialize/deserialize Dictionaries within classes.
Here's a working example of your code in dotnetfiddle.
Note the serialized Json with the contract resolver is different than the original json. It must be serialized using this contract resolver in order to deserialize with it as well.
I pulled the contract resolver from this StackOverflow question, if you need any more clarification.

How to iterate through Observable collection?

Hi guys i want to ask that i have a Class with properties like following:
public class VLANSPropertyClass
{
public string vname { get; set; }
public int S_No { get; set; }
public string vid { get; set; }
public string ip { get; set; }
public string vports { get; set; }
}
I created an ObservableCollection as follows:
public ObservableCollection<VLANSPropertyClass> vlan { get; set; }
vlan = new ObservableCollection<VLANSPropertyClass>();
I am adding all these values in a datagrid:
void AddVlans()
{
var serial = new VLANSPropertyClass();
serial.S_No = vlan.Count + 1;
Console.WriteLine(serial.S_No);
serial.vname = VlanName;
Console.WriteLine(serial.vname);
serial.vid = VlanID;
Console.WriteLine(serial.vid);
serial.ip = VlanIP1 + "." + VlanIP2 + "." + VlanIP3 + "." + VlanIP4;
Console.WriteLine(serial.ip);
serial.vports = SelectedVlanPort;
vlan.Add(serial);
}
The display looks like following image:
Now i want go through each row and read its values.I tried following but didnt work
foreach(VLANSPropertyClass v in vlan)
{
Console.WriteLine(v);
Console.WriteLine();
}
Kindly tell me the possible way of reading values from ObservableCollection/Datagrid.Any help would be highly appreciable.
You can change your class to this...
public class VLANSPropertyClass
{
public string vname { get; set; }
public int S_No { get; set; }
public string vid { get; set; }
public string ip { get; set; }
public string vports { get; set; }
public override string ToString()
{
return String.Format("Name: {0}, Serial {1}", vname, S_No);
}
}
This change includes an override to the ToString method. It will be called whenever the framework needs a string representation of your class.
ToString is the major formatting method in the .NET Framework. It
converts an object to its string representation so that it is suitable
for display. (For information about formatting support in the .NET
Framework, see Formatting Types.)
source: http://msdn.microsoft.com/en-us/library/system.object.tostring.aspx
Using an override to ToString will let you do this Console.WriteLine(v); with expected results.
When you loop through an ObservableCollection<>, accessing it through index returns a complete instance/item of the collection in form of the string, you now need to reference this string using index or property(in case its a user defined type(class)) in order to access the real content, in my case i am doing this to access temp property of an item of the collection mess.
mess[mj_key][mn_key][0].Temp.ToString()
here i have 3 dimensions, avoid it for now, just understand that 0 index returns string object, i then access temperature using .Temp
Hope it Helps!

C# WinForms populating TreeView from List<myObj>

I have this structure of classes:
public class L3Message
{
public int Number { get; set; }
public string MessageName { get; set; }
public string Device { get; set; }
public string Time { get; set; }
public string ScramblingCode { get; set; }
public List<Parameter> Parameters { get; set; }
public L3Message()
{
Parameters = new List<Parameter>();
}
}
public class Parameter
{
public int numOfWhitespaces { get; set; }
public string ParameterName { get; set; }
public string ParameterValue { get; set; }
public Parameter Parent { get; set; }
public List<Parameter> SubParameters { get; set; }
public Parameter()
{
SubParameters = new List<Parameter>();
}
}
So, as return type from one of my Methods I have List of L3Messages (List<L3Message>), and I need to map that to TreeView in WinForms (populate TreeView from that List).
EDIT:
Please notice that tree of my objects can be deeper than one level (becouse class Parameter have prop List < Parmaeter > (List of Parameter object, same structure as root parameter object)), so that means recursion or something like.
EDIT2:
Here is pic of tree, but this tree is created from text file base on whitespaces, so here is all Parameters, in my tree I need only one from List of L3Message objects.
http://imageshack.us/photo/my-images/803/treeviewmessage.png/
EDIT3:
I'm sure that my TreeView need to be something like this:
L3Message.Number: L3Message.MessageName
+L3Message.Time
+L3Message.Device
+L3Message.ScramblingCode
+L3Message.Parameters[0]
++Parameter.ParamaeterName: Parameter.ParameterValue
++ (same as above)
L3Message.Number: L3Message.MessageName
+L3Message.Time
+L3Message.Device
+L3Message.ScramblingCode
+L3Message.Parameters[0]
++Parameter.ParamaeterName: Parameter.ParameterValue (in this occasion Value is null )
+++SubParameter.ParameterName: SubParameter.ParameterValue
Something like that
If possible, I would like to that in separate thread.
How can I achieve that?
Of course it is possible. Now it depends how you want your TreeView to be structured. Then you just need to create TreeNode objects and add them to the TreeView. See this small tutorial: http://www.dotnetperls.com/treeview
If you are going to do this on a different thread, you will need to update the GUI by forwarding the updates to the GUI thread using BeginInvoke:
TreeNode node = new TreeNode("node");
L3Message msg = new L3Message();
node.Tag = msg;
treeView.BeginInvoke(
(Action)(() =>
{
treeView.Nodes.Add(node);
}));
Notice that the TreeNode needs to be created with a string representing the name and then you can add the object it points to using the Tag property.
I managed to solve this, but I think that is not optimize and there is no separate thread.
If anyone can modify my code to perform better and add separate thread?
SOLUTION:
foreach (L3Message message in por)
{
treeViewMessages.Nodes.Add(message.Number + ": " + message.MessageName);
treeViewMessages.Nodes.Add("Time: " + message.Time);
treeViewMessages.Nodes.Add("MS: " + message.Device);
treeViewMessages.Nodes.Add("Scrambling Code: " + message.ScramblingCode);
foreach (Parameter param in message.Parameters)
{
TreeNode tnRootParam = new TreeNode();
//tnRootParam.Nodes.Add(param.ParameterName + ": " + param.ParameterValue);
if (param.SubParameters.Count != 0)
{
CreateTreeNodes(param, tnRootParam);
tnRootParam.Text = param.ParameterName;
treeViewMessages.Nodes.Add(tnRootParam);
}
else
{
tnRootParam.Text = param.ParameterName + ": " + param.ParameterValue;
treeViewMessages.Nodes.Add(tnRootParam);
}
}
treeViewMessages.Nodes.Add("---------------------------------------------------------------------");
}
private void CreateTreeNodes(Parameter parameter, TreeNode tnRootParam)
{
if (parameter.SubParameters.Count == 0)
{
tnRootParam.Nodes.Add(parameter.ParameterName + ": " + parameter.ParameterValue);
}
else
{
foreach (Parameter subparam in parameter.SubParameters)
{
CreateTreeNodes(subparam, tnRootParam);
}
}
}

Categories