Save class properties in a list - c#

I have a class property called Instructions that I use to save my instructions data. I then save my Instruction class with the property to a list called _instructionList. Is this the best way to save my data which I can retrieve later or should I rather use another data structure like Tuple, ArrayList etc?
internal class Instructions
{
public string StreetName { get; set; }
public double Latitude { get; set; }
public double Longitude { get; set; }
public string Kind { get; set; }
public double LengthInMeters { get; set; }
}
Put the data coming from ArrayList to the class property and save the class with his properties in the list.
public void SaveInstructions(ArrayList value)
{
_instructionList.Add(new Instructions {
StreetName = (string)value[0],
Latitude = (double)value[1],
Longitude = (double)value[2],
Kind = (string)value[3],
LengthInMeters = (double)value[4]
});
}

ArrayList is mostly used for compatibility with .NET 1.1. There's no reason to use it in new development.
Your question indicates that you're looking for a structure to contain the data for an instance of Instructions so that you can pass it to a method that will 1) create an instance of Instructions and 2) add it to a list.
But the structure you're looking for already exists - it's your Instructions class. Your method might as well look like this:
public void SaveInstructions(Instructions saved)
{
_instructionList.Add(saved);
}
That would be my first choice - create the instance separately and just call the method to add it to the class. That way SaveInstructions has a simpler purpose - to add to the list.
If you did want to pass the parameters (as in your question) then you might do this:
public void SaveInstructions(string streetName, double latitude,
double longitude, string kind, string lengthInMeters)
But that gets a little messy because the method has so many parameters. The first option is better because it doesn't require the method to "know" what all the properties of Instructions are.

Related

Avoiding transposed parameters without creating coupling

I'm currently building a test application that manages parts for engineers, and I've hit a snag. I have a few different classes, including PartsModel and EngineerModel, and I want to update a list of parts that an engineer has, but I'm mindful of issues from either transposed parameters or from structuring the code in a way that unnecessarily couples to a particular class.
The two classes, with some relevant properties:
public class PartModel
{
public int PartId { get; private set; }
public string PartTitle { get; set; }
public string PartDescription { get; set; }
public int Quantity { get; set; }
public int MinimumStock { get; set; }
public void AddToStock (int quantityToAdd) {
Quantity += quantityToAdd;
}
public void RemoveFromStock (int quantityToRemove) {
Quantity -= quantityToRemove;
CheckMinimumStock();
}
}
public class EngineerModel
{
public int EngineerId { get; private set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public List<PartModel> PartsInStock { get; set; } = Factory.CreatePartsList();
}
As you can see, each engineer has a list of parts they have in stock via a List<PartModel>. I want to pass another list to this one so that I can update it respectively (incrementing or decrementing quantities, and then adding or removing parts to the list as necessary).
The first warning bell is that it takes two inputs of the same type, and is going to fill one from the other one (which isn't needed afterwards), so you're essentially modifying one input and destroying the other. To me, this presents a danger of the inputs getting transposed and the wrong list being either returned or updated (depending on whether it returns or just acts on the list). Because it removes items that have no quantity, it can't check the list length and just update the longer one, because there are possible cases where the engineer's list is shorter (maybe they're a new engineer, or maybe they just had a large shipment of parts sent when they were running low on stock). If it did just keep parts with quantity zero, then you're threatening scalability of both engineers and parts (not to mention any other objects that use the same operation).
So, put it as a method in the EngineerModel class and operate on PartsInStock, right? But what about when I want to use the same operation on other classes (e.g. if I have a list of parts associated to a work task)? Then I extract the method out to another class and... I'm passing the two lists as parameters in the method, so I'm back to where I was.
Am I being reasonable in not wanting to have two parameters of the same type, and how do I structure the code to deal with this, but without creating unnecessary coupling? If I'm not being reasonable, what am I overlooking?
Use an extension method
Thanks to #DavidBrowne-Microsoft for clarifying this. By defining an extension method for List<PartModel>, it only needs the one parameter - the list containing the updates (foreach below based on #Valentin's answer to this question).
public static class PartsHandler
{
public static List<PartModel> UpdateStockQuantitiesWith(this List<PartModel> stockToBeUpdated, List<PartModel> stockUpdates) {
foreach ( var part in stockUpdates )
{
var partToBeUpdated = stockToBeUpdated.FirstOrDefault(x => x.PartId == part.PartId);
if ( partToBeUpdated != null )
{ partToBeUpdated.Quantity += part.Quantity; }
else
{ stockToBeUpdated.Add(part); }
}
stockToBeUpdated.RemoveAll(x => x.Quantity <= 0);
return stockToBeUpdated;
}
}
Now any class that needs to implement this can simply call it in a method on the respective property. For example, in the EngineerModel class, it can operate on the PartsInStock property:
public void AddPartsToStock(List<PartModel> partsSent) {
PartsInStock.UpdateStockQuantitiesWith(partsSent);
}

Using reflection to display data from different custom classes

I want to create a method that displays the information contained in an object, that will work dynamically, with any object. I'm having trouble handling properties that are other custom classes. In the example below the Person has Phones and Occupations which both are other classes. When the data is displayed, the value on the screen currently is:
TestReflection.Person
Name: Mary
Phones: TestReflection.Phones
Occupations: TestReflection.Occupations
It just displays the name of class, like TestReflection.Phones, rather than the data inside that object.
How can I change this code to show information like this instead?
TestReflection.Person
Name: Mary
Phones:
TestReflection.Phones
Type: 1
Number: 555XYZ
Occupations:
TestReflection.Occupations
Type: 5
Description: Secretary
Here is my code:
class Program
{
static void Main(string[] args)
{
List<Person> listPeson = new List<Person>();
var person1 = new Person();
person1.Name = "Mary";
person1.Phones = new Phones { new Phone { Type = 1, Number = "555XYZ" } };
person1.Occupations = new Occupations {new Occupation { Type = 5, Description = "Secretary" }};
listPeson.Add(person1);
DynamicExport(listPeson);
Console.ReadLine();
}
public static void DynamicExport<T>(List<T> listReg)
{
for (int i = 0; i < listReg.Count; i++)
{
Console.WriteLine(listReg[i].GetType());
foreach (var item in listReg[i].GetType().GetProperties())
{
Console.WriteLine($"{item.Name}: {item.GetValue(listReg[i], null)}");
}
}
}
}
class Person
{
public string Name { get; set; }
public Phones Phones { get; set; }
public Occupations Occupations { get; set; }
}
class Phones : List<Phone> { }
class Phone
{
public int Type { get; set; }
public string Number { get; set; }
}
class Occupations : List<Occupation> { }
class Occupation
{
public int Type { get; set; }
public string Description { get; set; }
}
I made some edits to your question - I hope I understood you correctly.
If you want to export data
If your question is really about displaying data, then there are better ways to do it than creating your own export method. The format you are trying to display looks similar to YAML. There's also JSON and XML. Using one of these libraries is probably better than writing your own method:
YamlDotNet NuGet package
Json.NET NuGet Package
System.Xml.Serialization.XmlSerializer class
If you want to learn more about reflection
Maybe you're interested in learning more about reflection, and the export is just an example to play around with it. In that case, let's look at this line:
Console.WriteLine($"{item.Name}: {item.GetValue(listReg[i], null)}");
$"{item.GetValue(listReg[i], null)}" ends up calling person1.Phones.ToString(). The default behavior of ToString just displays the type name. You could override that behavior, like this:
class Phones : List<Phone>
{
public override string ToString()
{
return Program.DynamicExportToString(this);
// ... where DynamicExportToString is a modified version of DynamicExport that
// builds and returns a string rather than sending it directly to the Console.
}
}
Maybe you want to be able to handle any class, even when you cannot override ToString in all of the classes you might export. Then you will need to put some additional logic in the DynamicExport method, because...
$"{item.Name}: {item.GetValue(listReg[i], null)}"
... doesn't work for every situation. We need to display different things depending on the type of the property.
Consider how you want to handle null values. Maybe something like $"{item.Name}: <null>"
Use your existing $"..." code if the type is...
a primitive type.
DateTime
String
... or a Nullable<> of one of those types.
If the type implements IEnumerable, loop over the contents of the collection and recursively call your export code for each element.
It's important to check for this interface after you've checked if the type is a String, because String implements IEnumerable.
Otherwise, recursively call your export code on this value.
When you call your export code recursively, it would be wise to guard against infinite loops. If the object you're trying to export contains a circular reference - you could quickly wind up with a StackOverflowException. To avoid this, maintain a stack of objects that have already been visited.
I think the above advice is generally applicable whenever you're using reflection to traverse an object graph - whether it's for serialization or any other purpose.
I hope this helps!

FileHelpers error: The field: 'k__BackingField' has the type: XXX that is not a system type, so this field need a CustomConverter

I need to read a CSV file with FileHelpers based on type, automatically generated by my MVC model. The model looks like this:
public partial class Merchant
{
public long Id { get; set; }
public string Name { get; set; }
public Nullable<int> Category { get; set; }
public virtual MerchantCategory MerchantCategory { get; set; }
}
The last field is obviously generated by a foreign key in database, referring to table MerchantCategories.
Then I attempt to create an instance of FileHelperEngine with this type:
var engine = new FileHelperEngine<Merchant>();
And get the following exception:
The field: 'k__BackingField' has the type: MerchantCategory that is not a system type, so this field need a CustomConverter ( Please Check the docs for more Info).
Actually I don't need this field at all for my import, so I tried to ignore it in derived class:
[DelimitedRecord(",")]
public class MerchantForImport : Merchant {
[FieldHidden]
new public MerchantCategory MerchantCategory;
}
var engine = new FileHelperEngine<MerchantForImport>();
And still the same error. I don't need this field at all, I don't want to implement any FieldConverter for it, I never asked for this k__BackingField and it's nowhere to be found in my code!
I can't call FileHelperEngine.Options.RemoveField() because the exception is thrown by the constructor.
Where does that come from? How do I get rid of it?
From a design perspective, I think you are going about it the wrong way. You are trying to use the Merchant class for two incompatible uses. Instead you should have two separate classes.
FileHelpers is a library for describing csv files so that you can import them easily. You should have a MerchantFileSpec for describing your file. It's really not a proper C# class - it may have: dummy fields to represent unused columns; lots of attributes [FieldNullValue], [FieldQuoted], [FieldConverter]; etc. It works best with public fields (a FileHelpers limitation which is not C# best practice), etc. It is a convenience syntax for describing the import file. It should not include any business logic or special constructors, or backing fields. Keep it as simple as possible.
Then you can have your MVC-generated Merchant class which is separate. Its purpose is to describe the merchant as required by the MVC framework, with foreign keys, ids, whatever.
Then you use a FileHelperEngine<MerchantFileSpec> to read the records into an array and map it to an enumerable of Merchant (via Linq or a library like AutoMapper).
Something like:
/// Your MVC-generated class. Add methods, getters, setters, whatever.
/// FileHelpers doesn't use this class.
class Merchant
{
public long Id { get; set; }
public string Name { get; set; }
public Nullable<int> Category { get; set; }
public virtual MerchantCategory MerchantCategory { get; set; }
}
/// This is the class FileHelpers will use
/// This class describes the CSV file only. Stick to whatever
/// syntax conventions are required by FileHelpers.
[DelimitedRecord(";")]
class ProductMerchantFileSpec
{
[FieldQuoted(QuoteMode.OptionalForRead)]
public long Id;
[FieldQuoted(QuoteMode.OptionalForRead)]
public string Name;
[FieldQuoted(QuoteMode.OptionalForRead)]
// Handle non-US formats such as , decimal points
// convert from inches to centimetres?
// you get the idea...
[FieldConverter(MyCustomizedCategoryConverter)] // you get the idea
public int Category;
}
class Program
{
static void Main(string[] args)
{
var engine = new FileHelperEngine<ProductMerchantFileSpec>();
var productMerchantRecords = engine.ReadFile(filePath);
var productMerchants = productMerchantRecords
.Select(x => new Merchant() { Id = x.Id, Name = x.Name, Category = x.Category });
}
}
I received this error specifically because my object (i.e. Merchant) was missing a column that existed in the source file. I was able to work around the issue prior to realizing the missing column by adding a new property to my object class public string[] MyProperty { get; set; }. This work-around help me realize a column was missing.
i.e..
public partial class Merchant
{
public long id { get; set; }
..
..
..
public string[] MyProperty { get; set; }
}

How to hydrate an object with values taken from struct in C#

Is there in C# any hydrating technique allowing to transfer values from one struct/object to another struct/object if they have similar fields or based on certain strategy. I came from Zend Framework 2 world, and it provides the feature "Hydrator" which allows do exactly what I said above. So, I am wondering whether Asp.Net or C# provides something similar.
To make it clear, I want something like this:
struct UserInfo {
public string FirstName { get; set; };
public string LastName { get; set; };
public int Age { get; set; };
}
class UserUpdateModel {
public string FirstName { get; set; };
public string LastName { get; set; };
public int Age { get; set; };
}
...
//supposed UserUpdateModel model I is gotten from the action param
UserInfo info = new UserInfo();
Hydrator hydrator = new Hydrator(Hydrator.Properties);
hydrator.hydrate(info, model);
Now, "info" should be populated with values from "model"
Any help is appreciated.
Yes. AutoMapper. It is designed specifically for this. I personally prefer writing ViewModel constructor that takes an entity and copies the properties. I like the control and familiarity of good old C# code even if it takes a bit more effort.
Automapper should do the trick. You can use it as a nuget package.
Once you have your types and a reference to AutoMapper, you can create a map for the two types.
Mapper.CreateMap<UserUpdateModel, UserInfo>();
The type on the left is the source type, and the type on the right is the destination type. To perform a mapping, use the Map method.
UserInfo info = Mapper.Map<UserInfo>(userUpdateModel);

Serializing Name/Value Pairs in a Custom Object via Web Service

This is a very complicated question concerning how to serialize data via a web service call, when the data is not-strongly typed. I'll try to lay it out as best possible.
Sample Storage Object:
[Serializable]
public class StorageObject {
public string Name { get; set; }
public string Birthday { get; set; }
public List<NameValuePairs> OtherInfo { get; set; }
}
[Serializable]
public class NameValuePairs {
public string Name { get; set; }
public string Value { get; set; }
}
Sample Use:
[WebMethod]
public List<StorageObject> GetStorageObjects() {
List<StorageObject> o = new List<StorageObject>() {
new StorageObject() {
Name = "Matthew",
Birthday = "Jan 1st, 2008",
OtherInfo = new List<NameValuePairs>() {
new NameValuePairs() { Name = "Hobbies", Value = "Programming" },
new NameValuePairs() { Name = "Website", Value = "Stackoverflow.com" }
}
},
new StorageObject() {
Name = "Joe",
Birthday = "Jan 10th, 2008",
OtherInfo = new List<NameValuePairs>() {
new NameValuePairs() { Name = "Hobbies", Value = "Programming" },
new NameValuePairs() { Name = "Website", Value = "Stackoverflow.com" }
}
}
};
return o;
}
Return Value from Web Service:
<StorageObject>
<Name>Matthew</Name>
<Birthday>Jan 1st, 2008</Birthday>
<OtherInfo>
<NameValuePairs>
<Name>Hobbies</Name>
<Value>Programming</Value>
</NameValuePairs>
<NameValuePairs>
<Name>Website</Name>
<Value>Stackoverflow.com</Value>
</NameValuePairs>
</OtherInfo>
</StorageObject>
What I want:
<OtherInfo>
<Hobbies>Programming</Hobbies>
<Website>Stackoverflow.com</Website>
</OtherInfo>
The Reason & Other Stuff:
First, I'm sorry for the length of the post, but I wanted to give reproducible code as well.
I want it in this format, because I'm consuming the web services from PHP. I want to easily go:
// THIS IS IMPORANT
In PHP => "$Result["StorageObject"]["OtherInfo"]["Hobbies"]".
If it's in the other format, then there would be no way for me to accomplish that, at all. Additionally, in C# if I am consuming the service, I would also like to be able to do the following:
// THIS IS IMPORANT
In C# => var m = ServiceResult[0].OtherInfo["Hobbies"];
Unfortunately, I'm not sure how to accomplish this. I was able to get it this way, by building a custom Dictionary that implemented IXmlSerializer (see StackOverflow: IXmlSerializer Dictionary), however, it blew the WSDL schema out of the water. It's also much too complicated, and produced horrible results in my WinFormsTester application!
Is there any way to accomplish this ? What type of objects do I need to create ? Is there any way to do this /other than by making a strongly typed collection/ ? Obviously, if I make it strongly typed like this:
public class OtherInfo {
public string Hobbies { get; set; }
public string FavoriteWebsite { get; set; }
}
Then it would work perfectly, I would have no WSDL issues, I would be able to easily access it from PHP, and C# (.OtherInfo.Hobbies).
However, I would completely lose the point of NVP's, in that I would have to know in advance what the list is, and it would be unchangeable.. say, from a Database.
Thanks everyone!! I hope we're able to come up with some sort of solution to this. Here's are the requirements again:
WSDL schema should not break
Name value pairs (NVP's) should be serialized into attribute format
Should be easy to access NVP's in PHP by name ["Hobbies"]
Should be easy to access in C# (and be compatible with it's Proxy generator)
Be easily serializable
Not require me to strongly type the data
Now, I am /completely/ open to input on a better/different way to do this. I'm storing some relatively "static" information (like Name), and a bunch of pieces of data. If there's a better way, I'd love to hear it.
This is like dynamic properties for a object.
C# is not quite a dynamic language unlike javascript or maybe PHP can parse the object properties on the fly. The following two methods are what I can think of. The second one might fit into your requirements.
The KISS Way
The Keep It Simple Stupid way
public class StorageObject {
public string Name { get; set; }
public string Birthday { get; set; }
public List<string> OtherInfo { get; set; }
}
You can have name value pairs which is separated by '|'
OtherInfo = {"Hobbies|Programming", "Website|Stackoverflow.com"}
Serialized forms
<StorageObject>
<Name>Matthew</Name>
<Birthday>Jan 1st, 2008</Birthday>
<OtherInfo>
<string>Hobbies|Programming</string>
<string>Website|Stackoverflow.com</string>
</OtherInfo>
</StorageObject>
The Dynamic Way in C#
Make the name value pair part become an XML element so that you can build it dynamically.
public class StorageObject {
public string Name { get; set; }
public string Birthday { get; set; }
public XElement OtherInfo { get; set; } // XmlElement for dot net 2
}
You can easily build up OtherInfo object as element centric
e.g.
XElement OtherInfo = new XElement("OtherInfo");
OtherInfo.Add( ..Hobbies xelement & text value..);
OtherInfo.Add( ..WebSite xelement & text value..);
The serialized form will be
<OtherInfo>
<Hobbies>Programming</Hobbies>
<Website>Stackoverflow.com</Website>
</OtherInfo>
or build it as attribute centric
XElement OtherInfo = new XElement("OtherInfo");
OtherInfo.Add( ..nvp xattribute Hobbies & value..);
OtherInfo.Add( ..nvp xattribute WebSite & value..);
<OtherInfo>
<nvp n="Hobbies" v="Programming" />
<nvp n="Website" v="Stackoverflow.com" />
</OtherInfo>
For any dynamic language, it can access to the properties directly.
For the rest, they can access the value by read the XML. Reading XML is well supported by most of framework.
This is what I've settled on.
Class Structure:
public class StorageObject {
public string Name { get; set; }
public string Birthday { get; set; }
[XmlAnyElement("Info")] // this prevents double-nodes in the XML
public XElement OtherInfo { get; set; }
}
Usage:
StorageObject o = new StorageObject();
o.OtherInfo.Add(new XElement("Hobbies","Programming");
o.OtherInfo.Add(new XElement("Website","Stackoverflow.com");
Output:
<Info>
<Hobbies>Programming</Hobbies>
<Website>Stackoverflow.com</Website>
</Info>
I would like to thank everyone for their assistance, I really appreciate the help and ideas.
As a completely different take on this, why not think about doing it completely differently. Have one web service method to return the serialized storage object, minus the OtherInfo and another method to return the list of properties (keys) for OtherInfo, and a third to return the list of values for any key. Granted, it will take more round trips to the web service if you want all of the data, but the solution will be much simpler and more flexible.
[Serializable]
public class StorageObject {
public string Name { get; set; }
public string Birthday { get; set; }
[Nonserializable]
public Dictionary<string,List<string>> OtherInfo { get; set; }
}
[WebMethod]
public List<StorageObject> GetStorageObjects() {
// returns list of storage objects from persistent storage or cache
}
[WebMethod]
public List<string> GetStorageObjectAttributes( string name )
{
// find storage object, sObj
return sObj.Keys.ToList();
}
[WebMethod]
public List<string> GetStorageObjectAtributeValues( sting name, string attribute )
{
// find storage object, sObj
return sObj[attribute];
}
Have a look into the System.Xml.Serialization.XmlSerializerAssemblyAttribute attribute. This lets you specify a custom class-level serializer. You'll be able to spit out whatever XML you like.
A quick way to get up to speed on these is to use sgen.exe to generate one and have a peek at it with Reflector.
-Oisin
I'm not sure this would solve your problem (it would in C#, but maybe not in PHP), but try using Dictionary<string,List<string>> OtherInfo instead of List<NameValuePairs>. Then "Hobbies" and "Websites" would be your keys and the values would be the list of hobbies or web sites. I'm not sure how it would serialize, though.
You would be able to reference the lists of hobbies as:
List<string> hobbies = storageObject.OtherInfo["Hobbies"];
[EDIT] See here for a generic XML serializable dictionary. This derived class is the one you would need to use instead of generic Dictionary.

Categories