CSV to Cascading Model using Entity Framework - c#

I need to turn an automatic CSV file into multiple database columns using Entity Framework. It is set up so that each model has children. So that Animal contains a list of Types which contain a list of Classification. In this way Classification is a grandchild of Animal
Right now I have these three models that need to be filled by the CSV file. The file is formatted in the following way:
They are then pulled from the API into a Windows Desktop App as a cascading dropdown box. So far I've tried adding them to separate lists however that did not upload when using Entity Framework. The current way is to try to cascade down the list however I get an error
Sequence contains no events
Here is the portion of the code that I am having a problem with (had to edit due to work rules so classes are different):
var Animal = new List<AnimalModel>();
var lines = await ReadStreamAsync(new StreamReader(uploadModel.File.OpenReadStream()));
foreach(string l in lines)
{
Animal.Add(new AnimalModel
{
AnimalName = cells[0],
});
Animal.Last().Type.Add(new TypeModel
{
TypeName = cells[1],
});
Animal.Last().Type.Last().Classification.Add(new ClassificationModel
{
Type = Type.Last(),
ClassificationName = cells[2],
Color = cells[3],
Age = cells[4]
});
}

I resolved the issue. I needed to initialize the list within the code as I am not doing so within the model. The following worked:
var Animal = new List<AnimalModel>();
var lines = await ReadStreamAsync(new StreamReader(uploadModel.File.OpenReadStream()));
foreach(string l in lines)
{
Animal.Add(new AnimalModel
{
AnimalName = cells[0],
Type = new List<TypeModel>()
{
new TypeModel()
{
TypeName = cells[1]
}
}
});
And so on for the grandchild. I will have to clean this up as it is quite messy but this works for now.

Related

Reading files from a txt file and placing contents in a List<T>

I have a file in my project folder containing the data for properties in a Vehicle class. If the class was hardcoded into the XAML it would look like this:
Vehicle v1 = new Car() { Make = "Ford", Model = "Fiesta", Price = 10000, Year = "1999", Colour = "Red", Mileage = 40000, Description = "Lovely red car, 4 wheel, optional steering wheel.", VehicleType = "Car" };
VehicleList.Add(v1);
I don't want to hardcode this class object in, Instead I want to read it from a .txt file, each part separated by a comma ',' and place these into each property and add that vehicle into the Vehicle List<> 'VehicleList' and then display this new read list into a listbox.
Both the hardcode and the .txt follow the same structure with the hardcode containing the variable names and the .txt file containing just the data.
Here is what I have so far, as you can see I tried using System.IO however I am open to alternative methods.
private void Button_Click(object sender, RoutedEventArgs e)
{
string location = #"PATH";
VehicleList = File.ReadAllLines(location).ToList();
var logFile = File.ReadAllLines(location);
foreach (var v[i] in logFile) VehicleList.Add(s);
}
}
public static List<Car> FromTextFile(string fileName)
{
var lines = File.ReadAllLines(fileName);
var cars = new List<Car>();
foreach (var line in lines)
{
var car = FromLine(line);
if (car == null)
continue;
cars.Add(car);
}
return cars;
}
private static Car FromLine(string line)
{
var values = line.Split(',');
if (values.Length != 8) // There is no specification on what to do, if the amount of items is not sufficient.
return null;
// There is also no specification on what order to use, or how to map the ordering.
return new Car
{
Make = values[0],
Model = values[1],
// There is also no specification on how to handle in-correct formats.
// This assumes the Price property to be type of int.
Price = int.Parse(values[2]),
Year = values[3],
Colour = values[4],
// Again; how to handle in-correct formats?
Mileage = int.Parse(values[5]),
Description = values[6],
VehicleType = values[7]
};
}
This code should do what you described. Please be aware of the limitations of this code;
It expects a file that contains some lines, in comma seperated format.
It expects lines to contain 8 non-empty items, otherwise those lines will not be mapped.
It assumes the comma-seperated items to be exactly in this order; Make, Model, Price, Year, Colour, Mileage, Description, VehicleType.
It will not handle in-correct formats for converted types (e.g int). Because you did not ask so.
Keep in mind that this is not a desired code, it is not well written.
Now, as to your code;
Firstly, you've two different values for lines. I don't know why. One of them should be ok.
Second, you try to loop through one of them using foreach, but it doesn't work like that. You don't give or take an index from the foreach loop. The correct what is just foreach (TYPE VARIABLE_NAME in COLLECTION). So your code would be foreach (var v /* not v[i] */ in logFile) // ...
And then you add this to a list which contains those seperated lines again. At the end of the day, you have a list that contains the same lines twice. What you should have done in the adding part is, you should have converted the line to a Car object. Now how you do that, is absolutely up to you. Here are some question that comes to my mind, when the task is something such as mapping a string collection to an object.
What do I do if some members do not exist? (i.e when there is 5 items in the line, even though we have 8 properties)
How do I know which property to map the i'th item? What is the ordering? Do I get it from a "Columns" line? Do I just assume some ordering of my desire? How do I handle, if the user does not follow those rules?
What do I do, when there is something wrong about formatting of some of the members?
Now, this is not really an un-common problem. Everyone somehow needs to transport the data from place A to place B. For your specific instance, it is from disk storage, to memory, in the form of an object.
How do people solve these sorts of problem? They invent generic ways of "serialization" and "deserialization". What are some examples? The most known ones and the most used ones that comes to my mind are XML (eXtensible Markup Language) and JSON (JavaScript Object Notation). What do these actually do? They serialize programming objects to non-binary string formats. Here is an example XML output for your Car class.
<?xml version=\"1.0\" encoding=\"utf-16\"?>
<Car xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">
<Make>Ford</Make>
<Model>Fiesta</Model>
<Price>10000</Price>
<Year>1999</Year>
<Colour>Red</Colour>
<Mileage>40000</Mileage>
<Description>Lovely red car, 4 wheel, optional steering wheel.</Description>
<VehicleType>Car</VehicleType>
</Car>
Generated with this simple code;
var car = new Car
{
Make = "Ford",
Model = "Fiesta",
Price = 10000,
Year = "1999",
Colour = "Red",
Mileage = 40000,
Description = "Lovely red car, 4 wheel, optional steering wheel.",
VehicleType = "Car"
};
var serializer = new XmlSerializer(typeof(Car));
// https://stackoverflow.com/questions/2434534/serialize-an-object-to-string
using (StringWriter textWriter = new StringWriter())
{
serializer.Serialize(textWriter, car);
var output = textWriter.ToString();
}
And this is, how you would easily read "Car"s from a text file, if it was formatted correctly;
var serializer = new XmlSerializer(typeof(List<Car>));
// If your file should contain one car and one car only, you should change the above line to;
// var serilizer = new XmlSerializer(typeof(Car));
using (var reader = File.OpenRead(#"PATH"))
{
var cars = (List<Car>)serializer.Deserialize(reader);
reader.Close();
}
Similar code can be written for JSON. But I think you get the idea.
In short, most of the times, XML, JSON or a similar generic format is sufficient. When they are not, you can develop your very own format. But comma-seperated line is a very simplistic way of doing serialization. The reasons are exactly my question (see above). And XML, JSON answers those questions easily. Implementations do a very good job as well, most of the time.
Edit
Here is an answer to a very similar question. The answer to that question would high likely also answer your question. How to split csv whose columns may contain ,

EF Code First renaming of properties in generated POCO classes

I'm using Entity Data Model Wizard(Code First From Database) to generate the dbcontext and POCO Classes. Unfortunately I'm running on a very old database and all the database columns have lowercase names, frequently with underscores and look like garbage in C#. It'd be really nice with the ~100 tables we have if the code generator would put the attribute [Column("column_name")] above everything that wasn't capitalized in the database or if there was an easy way to tell visual studio to look at a file and add that attribute for all lowercase properties that don't already have it(or even just all properties period).
I've looked at some T4 stuff, reverse poco generator, etc. But it seemed nearly as time consuming to get it up and running as manually renaming the properties. Is the source for the (Entity Data Model Wizard) code that runs when you select "ADO.NET Entity Data Model" in the VS Add New Item window available anywhere so I can start with something that is already working?
Or does someone know of an epic find/replace that will take
public string n_addr1 { get; set; }
and give
[Column("n_addr1")]
public string N_addr1 { get; set; }
without knowing what n_addr1 is called, meaning it would have to match on public string and/or {get; set;}
I did something similar and I'm going to post the code I used to find the "name" of the class. I edited so that it works with a fileName you pass. Tested on one of my classes and this is working.
var fileName = #"YOUR FILE NAME";
StringBuilder builder = new StringBuilder();
using (StreamReader sr = new StreamReader(fileName))
{
while (!sr.EndOfStream)
{
var line = sr.ReadLine();
var match = Regex.Match(line, #"{\s?get;\s?set;\s?}");
if (match.Success)
{
var split = Regex.Split(line, #"{\s?get;\s?set;\s?}");
var declaration = split[0].Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
var last = declaration.Count();
var name = declaration[last - 1];
builder.AppendLine(string.Format("[Column(\"{0}\")]", name));
}
builder.AppendLine(line);
}
}

Project on multiple fields in C# MongoDB 2.0

How do you project on fields in the new MongoDB C# drivers when the fields are given in the form of a String array ?.
I could find ways to project on a single field by doing
collection.find(filter).Project(Builders<Category>.Projection.Include(fieldName)
How do I extend this to take an array of fields ?.
There is also extension method Include
var projection = Builders<Category>.Projection.Include(fieldList.First());
foreach (var field in fieldList.Skip(1))
{
projection = projection.Include(field);
}
var result = await collection.Find(filter).Project(projection).ToListAsync();
another way, assuming fieldList is a string enumerable is:
var project = Builders<BsonDocument>.Projection.Combine(fieldList.Select(x => Builders<BsonDocument>.Projection.Include(x)).ToList());
A better way than mofenko's, you don't have to include the first column :
ProjectionDefinition<BsonDocument> project = null;
foreach (string columnName in columnsToInclude)
{
if (project == null)
{
project = Builders<BsonDocument>.Projection.Include(columnName);
}
else
{
project = project.Include(columnName);
}
}
This is for loosely typed data. If you're using classes replace BsonDocument with your class

XML to List converter? Is XML the right tool anyway?

i'm coming from PHP to C#, so please excuse some of my terminology.
Currently i'm working on a small project that requires multiple profiles to be stored in one single file, so i decided to use XML, because INI files (usually my go to guy for text based stuff) are not really supported by C#. At least not in a satisfying way.
Here the basic structure of my XML file:
<profile name="FooBar">
<btnA pressed="actionA" released="actionB" />
<btnB pressed="actionX" released="actionY" />
...
</profile>
<profile name="XYZ">
<btnA pressed="actionA" released="actionB" />
<btnB pressed="actionX" released="actionY" />
...
</profile>
In PHP i would generate an associative array with the following structure:
<?php
foo = array(
'FooBar' => array(
'btnA_pressed' => 'actionA',
'btnA_released' => 'actionB'
// ...
),
'XYZ' => array(
// ...
)
);
EDIT START
Application Class Structure:
Settings (Contains all profiles and a reference to the current profile)
Profile (See below)
The Profile class:
public class Profile
{
private string _name;
public string Name
{
get { return this._name; }
set { this._name = value;}
}
private string _btnA_pressed;
public string BtnA_Pressed { get; set; }
// and so on...
public Profile(var data) { // arg is a placeholder
// associate the data with the properties
}
}
In short, the Settings class holds all profiles and a reference to the selected profile. Access to the profile goes over Settings.CurrentProfile.propertie_Name()
EDIT END
The question is now, how do i achieve the same or a similar thing in C#? Or are there better methods of achieving the same thing?
Thanks for your help in advance!
XML structures can be manipulated very easily with LINQ2XML without the need of typed models (classes).
Reading XML file containing many profile nodes (and i assume your XML file is correct and has one root node), can look like this:
// read existing XML structure
var xml = XDocument.Load("d:\\temp\\xml\\profile.xml");
// take all profile elements
var profiles = xml.Root.Elements("profile").ToList();
foreach (var profile in profiles)
{
Console.WriteLine(profile.Attribute("name").Value);
// find all button elements
var buttons = profile
.Elements()
.Where (e => e.Name.ToString().StartsWith("btn"));
// list elements
foreach (var button in buttons)
{
// tag name
var name = button.Name.ToString();
// attributes
var pressed = button.Attribute("pressed").Value;
var released = button.Attribute("released").Value;
Console.WriteLine(String.Format("{0} - P'{1}' - R'{2}'", name, pressed, released));
}
}
The output is:
FooBar
btnA - P'actionA' - R'actionB'
btnB - P'actionX' - R'actionY'
XYZ
btnA - P'actionA' - R'actionB'
btnB - P'actionX' - R'actionY'
Reading a single profile XML structure from a string and then creating a new one can look like this:
var xmlCode = #"<profile name=""FooBar""><btnA pressed=""actionA"" released=""actionB"" /><btnB pressed=""actionX"" released=""actionY"" /></profile>";
try
{
// read existing XML structure
var xml = XDocument.Parse(xmlCode); // XDocument.Load("c:\\path\\to\\file.xml");
// find all button elements
var buttons = xml.Root
.Elements()
.Where (e => e.Name.ToString().StartsWith("btn"));
// list elements
foreach (var button in buttons)
{
// tag name
var name = button.Name.ToString();
// attributes
var pressed = button.Attribute("pressed").Value;
var released = button.Attribute("released").Value;
Console.WriteLine(String.Format("{0} - P'{1}' - R'{2}'", name, pressed, released));
}
// create xml
// root element
var newXml = new XElement("profile", new XAttribute("name", "FooBaz"),
new XElement("btnA",
new XAttribute("pressed", "actionX"),
new XAttribute("released", "actionXR")),
new XElement("btnB",
new XAttribute("pressed", "actionZ"),
new XAttribute("released", "actionZR")));
Console.WriteLine(newXml.ToString());
}
catch (Exception exception)
{
Console.WriteLine(exception.Message);
}
The output is:
btnA - P'actionA' - R'actionB'
btnB - P'actionX' - R'actionY'
<profile name="FooBaz">
<btnA pressed="actionX" released="actionXR" />
<btnB pressed="actionZ" released="actionZR" />
</profile>
You can use LINQ2XML to read the data and fill a list of objects of your Profile type like this:
// read existing XML structure
var xml = XDocument.Load("d:\\temp\\xml\\profile.xml");
// take all profile elements
var profiles = xml.Root.Elements("profile").ToList();
var listOfProfiles = new List<Profile>();
foreach (var profile in profiles)
{
var profileObject = new Profile("");
profileObject.Name = profile.Attribute("name").Value;
// find all button elements
var buttons = profile
.Elements()
.Where (e => e.Name.ToString().StartsWith("btn"));
// list elements
foreach (var button in buttons)
{
// attributes
var pressed = button.Attribute("pressed").Value;
var released = button.Attribute("released").Value;
profileObject.BtnA_Pressed = pressed;
}
listOfProfiles.Add(profileObject);
}
You can also use XML serialization - you need to describe your XML structure as a class (typed model) and deserialize (read XML file into your class) resp. serialize (write your XML structure to a file). A generic implementation of the methods for serialization resp. deserialization can look like this:
public void SerializeModel<T>(string fqfn, T entity)
{
var xmls = new XmlSerializer(entity.GetType());
var writer = new StreamWriter(fqfn);
xmls.Serialize(writer, entity);
writer.Close();
}
public T DeserializeModel<T>(string fqfn)
{
var fs = new FileStream(fqfn, FileMode.Open);
var xmls = new XmlSerializer(typeof(T));
var r = (T) xmls.Deserialize(fs);
fs.Close();
return r;
}
The typed model that describes your Profile class and the lists contained within, looks like this (note the usage of the different XML serialization attributes):
public class Profiles
{
[XmlElement(ElementName="Profile")]
public List<Profile> Profile { get; set; }
}
public class Profile
{
[XmlArray(ElementName="Buttons")]
public List<Button> Buttons { get; set; }
[XmlAttribute]
public String Name;
}
public class Button
{
[XmlAttribute]
public String Pressed { get; set; }
[XmlAttribute]
public String Released;
}
Creation of an XML file:
var profiles = new Profiles();
var profileA = new Profile();
var profileB = new Profile();
var buttonA = new Button();
var buttonB = new Button();
profileA.Buttons = new List<Button>();
profileB.Buttons = new List<Button>();
profiles.Profile = new List<Profile>();
profileA.Name = "Profile A";
profileB.Name = "Profile B";
buttonA.Pressed = "Pressed A";
buttonA.Released = "Release A";
buttonB.Pressed = "Pressed B";
buttonB.Released = "Release B";
profileA.Buttons.Add(buttonA);
profileB.Buttons.Add(buttonB);
profiles.Profile.Add(profileA);
profiles.Profile.Add(profileB);
var xmlFile = "d:\\temp\\xml\\profile_model.xml";
SerializeModel<Profiles>(xmlFile, profiles);
The new XML file looks like this (note, the structure was slightly modified because of the way XML serialization in .NET handles arrays/lists):
<?xml version="1.0" encoding="utf-8"?>
<Profiles xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Profile Name="Profile A">
<Buttons>
<Button Released="Release A" Pressed="Pressed A" />
</Buttons>
</Profile>
<Profile Name="Profile B">
<Buttons>
<Button Released="Release B" Pressed="Pressed B" />
</Buttons>
</Profile>
</Profiles>
The file can be then read like this:
var input = DeserializeModel<Profiles>(xmlFile);
foreach (var profile in input.Profile)
{
var b = profile.Buttons.First();
Console.WriteLine(String.Format("{0} - {1} - {2}", profile.Name, b.Pressed, b.Released));
}
The output is as expected:
Profile A - Pressed A - Release A
Profile B - Pressed B - Release B
Both approaches have advantages and disadvantages.
IMO the answer to your question (changed a bit) Is XML the correct approach for saving structured data to a file? is - definitely yes! Nowadays XML is one of the standards for representing / manipulating / exchanging structured data and data generally (data kept in a string). As someone already mentioned INI files were not really meant to represent complex nested structures.
In contrast to PHP where you might be used to doing a lot of arrays and magic-string based stuff, C# is a Statically Typed language where it's usually recommended to create a proper, formally defined, structured Data Model which allows you to manipulate the Data you're dealing with in a strongly typed manner.
This means, for example, that if you're dealing with personal information related data, and you need to deal with the concepts of last name, first name, and age you will want to create a class containing these pieces of data in the form of Properties, like so:
public class Person
{
public string LastName {get;set;}
public string FirstName {get;set;}
public int Age {get;set;}
//And so on...
}
Notice how each property has an adequate Data Type that allows you to constrain what values it can contain. For example, the fact that the Age property is of type int (integer numbers) automatically means you can never have something like "abc" inside it, and code like this:
var person = new Person();
person.Age = "abc";
will also produce a compile-time error, rather than blowing up at run time, or producing any sorts of inconsistencies in stored data.
Likewise, if your Person objects (in the real world data you're trying to model) have a relation to, say an Address, you're also going to create the Address class:
public class Address
{
public string Line1 {get;set;}
public string Line2 {get;set;}
public string City {get;set;}
//And so on...
}
And then model this Person -> Address relationship by creating an additional property in the Person class:
public class Person
{
//.. All of the above.
public Address Address {get;set;}
}
Which can be illustrated with a diagram like this:
This approach has the following advantages:
It provides Compile Time checking for correctness of the code. Compile-Time errors can be trapped (and need to be corrected) very early in the development cycle. For example, you can't do something like:
person.LatsName where the property name LastName is mispelled LatsName because the compiler "knows" there is no such property in the object model and thus you recieve a compile-time error rather than having the application crash at run-time.
It provides IDE support for features such as AutoComplete because the IDE "knows" the object model you're dealing with and thus it knows what properties/methods/events (Members) every class has, and what are the expected parameters and data types for each of these.
So, to answer the overarching question, once you created a proper object model, you can use .Net's built-in Serialization/Deserialization mechanisms to transform instances of classes in your object model (with actual values and properties) to a data format that can be stored in a file on disk, such as XML.
Assuming you have a class for profile with all the relevant fields/properties, you can do
Profile profile = new Profile();
profile.actionA = "actionA"; // or whatever you have
var file = new System.IO.StreamWriter(#"c:\temp\Profile_as_xml.xml");
var writer = new System.Xml.Serialization.XmlSerializer(typeof(Profile));
writer.Serialize(file, profile);
file.Close();
See http://msdn.microsoft.com/en-us/library/ms172873.aspx

C# Linq to CSV Dynamic Object runtime column name

I'm new to using Dynamic Objects in C#. I am reading a CSV file very similarly to the code found here: http://my.safaribooksonline.com/book/programming/csharp/9780321637208/csharp-4dot0-features/ch08lev1sec3
I can reference the data I need with a static name, however I can not find the correct syntax to reference using a dynamic name at run time.
For example I have:
var records = from r in myDynamicClass.Records select r;
foreach(dynamic rec in records)
{
Console.WriteLine(rec.SomeColumn);
}
And this works fine if you know the "SomeColumn" name. I would prefer to have a column name a a string and be able to make the same type refrence at run time.
Since one has to create the class which inherits from DynamicObject, simply add an indexer to the class to achieve one's result via strings.
The following example uses the same properties found in the book example, the properties which holds the individual line data that has the column names. Below is the indexer on that class to achieve the result:
public class myDynamicClassDataLine : System.Dynamic.DynamicObject
{
string[] _lineContent; // Actual line data
List<string> _headers; // Associated headers (properties)
public string this[string indexer]
{
get
{
string result = string.Empty;
int index = _headers.IndexOf(indexer);
if (index >= 0 && index < _lineContent.Length)
result = _lineContent[index];
return result;
}
}
}
Then access the data such as
var csv =
#",,SomeColumn,,,
ab,cd,ef,,,"; // Ef is the "SomeColumn"
var data = new myDynamicClass(csv); // This holds multiple myDynamicClassDataLine items
Console.WriteLine (data.OfType<dynamic>().First()["SomeColumn"]); // "ef" is the output.
You will need to use reflection. To get the names you would use:
List<string> columnNames = new List<string>(records.GetType().GetProperties().Select(i => i.Name));
You can then loop through your results and output the values for each column like so:
foreach(dynamic rec in records)
{
foreach (string prop in columnNames)
Console.Write(rec.GetType().GetProperty (prop).GetValue (rec, null));
}
Try this
string column = "SomeColumn";
var result = rec.GetType().GetProperty (column).GetValue (rec, null);

Categories