So I want to write an interface, which should be able to be implemented with any data. This is interface i wrote till now. The reason I chose IEnumerable is because I need to give class Computer or struct Processor
public interface IData<T> where T : IEnumerable<object>
{
public T ReadData();
public void WriteData(T list);
}
And I have two different datas, one is Computer, which is a class. And the other one is Processor (struct)
public struct Processor
{
public string Name { get; set; }
public string AmazonLink { get; set; }
public string AmazonBin { get; set; }
public Processor(string name, string link)
{
Name = name;
try
{
//constructor parses elements which is needed to generate AmazonURL in URLGenerator project
AmazonLink = link.Substring(0, link.IndexOf("&dc"));
string binStart = link.Substring(link.IndexOf("bin%") + 4);
AmazonBin = "%7C" + binStart.Substring(2);
}
catch (Exception e)
{
throw new InnerCustomException("Erorr occured while trying to substring the link", e);
}
}
I tried to do that like this, but it seems like I am not allowed to do that because of boxing?
public class ProcessorServiceCSV : IData<IEnumerable<Processor>>
{ private string Path { get; set; }
private FileMode Filemode { get; set; }
public ProcessorServiceCSV(string path, FileMode fileMode)
{
Path = path;
Filemode = fileMode;
}
//reads Processor list from CSV file
public IEnumerable<Processor> ReadData()
{
try
{
using (var reader = new StreamReader(Path))
using (var csv = new CsvReader(reader))
{
csv.Configuration.CultureInfo = CultureInfo.InvariantCulture;
csv.Configuration.Delimiter = ",";
csv.Configuration.RegisterClassMap<ProcessorMap>();
var records = csv.GetRecords<Processor>().ToList();
return records.ToList();
}
}
catch (FileNotFoundException)
{
throw new DataCustomException("File not found", this);
}
catch (Exception e)
{
throw new DataCustomException("Something's wrong happened:" + e.Message, this);
}
} public void WriteData(IEnumerable<Processor> processors)
{
try
{
using (var stream = File.Open(Path, Filemode))
using (StreamWriter sw = new StreamWriter(stream))
using (CsvWriter cw = new CsvWriter(sw))
{
foreach (Processor processor in processors)
{
cw.Configuration.RegisterClassMap<ProcessorMap>();
cw.WriteRecord<Processor>(processor);
cw.NextRecord();
}
}
}
catch (FileNotFoundException)
{
throw new DataCustomException("File not found", this);
}
catch (FileLoadException)
{
throw new DataCustomException("File could not be opened", this);
}
catch (Exception e)
{
throw new DataCustomException("Something's wrong happened:" + e.Message, this);
}
}
}
}
I know I could change Processor from struct to class, but is it possible to keep struct? Thank you in advance
You have a lot of other problems, including not giving us a complete, working bit of code.
However, it looks like you should be able to do what you want to do if you use an Interface for the Processor struct instead of the actual struct type.
Also, notice how I changed the type for T in your classes. You don't need IEnumerable in your T constraint. I did delete some of your code to get it to somewhat work (the exception in the struct constructor, e.g.), so you will need to do some more work here.
public interface IData<T>
{
IEnumerable<T> ReadData();
void WriteData(IEnumerable<T> list);
}
public interface IProcessor {
string Name { get; set; }
string AmazonLink { get; set; }
string AmazonBin { get; set; }
}
public struct Processor : IProcessor
{
public string Name { get; set; }
public string AmazonLink { get; set; }
public string AmazonBin { get; set; }
public Processor(string name, string link)
{
Name = name;
//constructor parses elements which is needed to generate AmazonURL in URLGenerator project
AmazonLink = link.Substring(0, link.IndexOf("&dc"));
string binStart = link.Substring(link.IndexOf("bin%") + 4);
AmazonBin = "%7C" + binStart.Substring(2);
}
}
public class ProcessorServiceCSV<T> : IData<T> where T: IProcessor
{ private string Path { get; set; }
private FileMode Filemode { get; set; }
public ProcessorServiceCSV(string path, FileMode fileMode)
{
Path = path;
Filemode = fileMode;
}
//reads Processor list from CSV file
public IEnumerable<T> ReadData()
{
try
{
using (var reader = new StreamReader(Path))
using (var csv = new CsvReader(reader))
{
csv.Configuration.CultureInfo = CultureInfo.InvariantCulture;
csv.Configuration.Delimiter = ",";
csv.Configuration.RegisterClassMap<ProcessorMap>();
var records = csv.GetRecords<Processor>().ToList();
return records.ToList();
}
}
catch (FileNotFoundException)
{
throw new DataCustomException("File not found", this);
}
catch (Exception e)
{
throw new DataCustomException("Something's wrong happened:" + e.Message, this);
}
}
}
Is this the basic skeleton code of what you are trying to do? Note that the generic collection is IEnumerable<T> and not IEnumetable<object> and hence I updated your IData<T> definition
public class Computer
{
}
public struct Processor
{
}
public interface IData<T>
{
IEnumerable<T> ReadData();
void WriteData(IEnumerable<T> list);
}
public class ComputerData : IData<Computer>
{
public IEnumerable<Computer> ReadData()
{
throw new NotImplementedException();
}
public void WriteData(IEnumerable<Computer> list)
{
throw new NotImplementedException();
}
}
public class ProcessorData : IData<Processor>
{
public IEnumerable<Processor> ReadData()
{
throw new NotImplementedException();
}
public void WriteData(IEnumerable<Processor> list)
{
throw new NotImplementedException();
}
}
Please indicate if this code meets your requirements, and if not why.
Related
I need to iterate through list I created, but can't access objects inside. I tried a few different functions but nothing worked and I'm afraid I'm using the wrong tools for the job.
namespace WholesaleApp
{
internal class Program : Wholesale
{
static string filePath = "C:\\Wholesale.txt";
static void Main(string[] args)
{
List<Merchandise> productList = new List<Merchandise>();
productList = ReadFromFile(filePath);
foreach(Merchandise merchandise in productList)
{
//this is where I'm trying to access objects inside and display them
}
}
}
}
This is my abstract class:
namespace WholesaleApp
{
internal abstract class Merchandise
{
public string merchandiseName { get; set; }
public int merchandiseAmount { get; set; }
public Merchandise(string name, int amount)
{
merchandiseName = name;
merchandiseAmount = amount;
}
}
}
And this is one of the three classes deriving from Merchandise abstract class:
namespace WholesaleApp
{
internal class MerchandiseClothing : Merchandise
{
public string clothSize { get; set; }
public string clothType { get; set; }
public MerchandiseClothing(string _clothType, string _clothSize, string name, int amount) : base(name, amount)
{
clothType = _clothType;
clothSize = _clothSize;
}
public void ReturnAll()
{
Console.Write(merchandiseName+" of type: "+clothSize+" in amount: "+merchandiseAmount+ " and jeep status is: "+clothType);
}
}
}
Finally, my function where I add everything to the final list:
namespace WholesaleApp
{
internal class Wholesale
{
static public List<Merchandise> ReadFromFile(string filePath)
{
List<Merchandise> result = new List<Merchandise>();
string line;
StreamReader reader = null!;
try
{
reader = new StreamReader(filePath);
line = reader.ReadLine()!;
while (line != null)
{
string[] words = line.Split(';');
if (words[0] == "MerchandiseComputer")
{
result.Add(new MerchandiseComputer(words[1], words[2], Int32.Parse(words[3])));
}
else if (words[0] == "MerchandiseCarParts")
{
result.Add(new MerchandiseCarParts(bool.Parse(words[1]), words[3], words[2], Int32.Parse(words[4])));
}
else if (words[0] == "MerchandiseClothing")
{
result.Add(new MerchandiseClothing(words[1], words[2], words[3], Int32.Parse(words[4])));
}
line = reader.ReadLine()!;
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
finally
{
reader.Close();
}
return result;
}
}
}
It should be possible to iterate here iterate through objects already. If you want to use specific fields from each specific class, you can put here a check on type and do whatever you want. For example:
foreach (Merchandise merchandise in productList)
{
if (merchandise is MerchandiseClothing clothing)
{
Console.WriteLine(clothing.clothSize); //Can be use any field from Clothing class
Console.WriteLine(clothing.merchandiseAmount); //And also from parent
}
else if (merchandise is MerchandiseComputer computer)
{
//Do what you want
}
}
However, better to make abstract method like WriteToConsole in Merchandise class and override it in each implementation. Like ReturnAll method in your MerchandiseClothing class
I have two Serializable classes with very similar code. Actually, except for the part where specific constructor is called, serialization code is identical.
Is there a way to create a common class to contain the common parts, so that specific classes (subclasses?) can implement only the constructor part? I can think of generics, factory pattern, but could not figure out how to do it.
// Fictitious classes
[Serializable]
public class FlightParameters {
public double MaxHeight { get; set; }
pulbic double MaxSpeedKmPerHour { get; set; }
public static FlightParameters Load(String fname) {
FlightParameters result;
using (var fs = new FileStream(fname, FileMode.OpenOrCreate)) {
var serializer = new XmlSerializer(typeof(FlightParameters));
try {
result = (FlightParameters)serializer.Deserialize(fs);
}
// catch "file not found"
catch (InvalidOperationException) {
result = new FlightParameters() {
MaxHeight = 30000;
MaxSpeedKmPerHour = 1500;
}
serializer.Serialize(fs, result);
}
return result;
}
}
}
[Serializable]
public class SailingParameters {
public double MaxDepth { get; set; }
pulbic double MaxSpeedKnots { get; set; }
public static SailingParameters Load(String fname) {
SailingParameters result;
using (var fs = new FileStream(fname, FileMode.OpenOrCreate)) {
var serializer = new XmlSerializer(typeof(SailingParameters));
try {
result = (SailingParameters)serializer.Deserialize(fs);
}
// catch "file not found"
catch (InvalidOperationException) {
result = new SailingParameters() {
MaxDepth = 13000;
MaxSpeedKnots = 15;
}
serializer.Serialize(fs, result);
}
return result;
}
}
}
Usage:
FlightParameters _fparam = FlightParameters.Load(somePath);
SailingParameters _sparam = SailingParameters.Load(someOtherPath);
The easiest way I can see to do that would be something like:
static class XmlUtils {
public static T Load<T>(string filename, Func<T> onMissing = null)
where T : class, new()
{
using (var fs = File.OpenRead(filename)) {
var serializer = new XmlSerializer(typeof(T));
try {
return (T)serializer.Deserialize(fs);
} catch (InvalidOperationException) { // catch "file not found"
return onMissing == null ? new T() : onMissing();
}
}
}
}
allowing something like;
public static SailingParameters Load(string filename) {
return XmlUtils.Load<SailingParameters>(filename, () => new SailingParameters {
MaxDepth = 13000;
MaxSpeedKnots = 15;
});
}
I have huge problem with saveing and restore ObservableCollection to IsolatedData.
I'm trying with this code.
Helper class for Observable
public class ListItem {
public String Title { get; set; }
public bool Checked { get; set; }
public ListItem(String title, bool isChecked=false) {
Title = title;
Checked = isChecked;
}
private ListItem() { }
}
IsoHelper
public class IsoStoreHelper {
private static IsolatedStorageFile _isoStore;
public static IsolatedStorageFile IsoStore {
get { return _isoStore ?? (_isoStore = IsolatedStorageFile.GetUserStoreForApplication()); }
}
public static void SaveList<T>(string folderName, string dataName, ObservableCollection<T> dataList) where T : class {
if (!IsoStore.DirectoryExists(folderName)) {
IsoStore.CreateDirectory(folderName);
}
if (IsoStore.FileExists(folderName + "\\" + dataName+".dat")) {
IsoStore.DeleteFile(folderName + "\\" + dataName + ".dat");
}
string fileStreamName = string.Format("{0}\\{1}.dat", folderName, dataName);
try {
using (var stream = new IsolatedStorageFileStream(fileStreamName, FileMode.Create, IsoStore)) {
var dcs = new DataContractSerializer(typeof(ObservableCollection<T>));
dcs.WriteObject(stream, dataList);
}
} catch (Exception e) {
Debug.WriteLine(e.Message);
}
}
public static ObservableCollection<T> LoadList<T>(string folderName, string dataName) where T : class {
var retval = new ObservableCollection<T>();
if (!IsoStore.DirectoryExists(folderName) || !IsoStore.FileExists(folderName + "\\" + dataName + ".dat")) {
return retval;
}
string fileStreamName = string.Format("{0}\\{1}.dat", folderName, dataName);
var isf = IsoStore;
try {
var fileStream = IsoStore.OpenFile(fileStreamName, FileMode.OpenOrCreate);
if (fileStream.Length > 0) {
var dcs = new DataContractSerializer(typeof(ObservableCollection<T>));
retval = dcs.ReadObject(fileStream) as ObservableCollection<T>;
}
} catch {
retval = new ObservableCollection<T>();
}
return retval;
}
}
And I'm trying to use it this way
public partial class MainPage : PhoneApplicationPage{
public ObservableCollection<ListItem> ListItems = new ObservableCollection<ListItem>();
bool isListSaved;
private void Panorama_SelectionChanged(object sender, System.Windows.Controls.SelectionChangedEventArgs e) {
if (strTag.Equals("list") ) {
isListSave = false;
ListItems = IsoStoreHelper.LoadList<ListItem>("settings", "ListItems");
} else if (!isListSave) {
IsoStoreHelper.SaveList<ListItem>("settings", "ListItems", ListItems);
}
}
}
I keep getting A first chance exception of type 'System.Security.SecurityException' occurred in System.Runtime.Serialization.ni.dll when I try read saved file at line ReadObject(fileStream) but the FileAccess looks fine.
Any conclusion will be appreciated.
SOLVED:
Like Dmytro Tsiniavskyi said I totaly forgot about [DataContract] and [DataMember] in ListItem. Whats more I found better solution for saving and loading data. I end up with this code for ListItem
[DataContract]
public class ListItem {
[DataMember]
public String Title { get; set; }
[DataMember]
public bool Checked { get; set; }
public ListItem(String title, bool isChecked=false) {
Title = title;
Checked = isChecked;
}
private ListItem() { }
}
And this code for save/load collection which was originally founded here and modified a litte bit for better useage.
public partial class IsolatedRW {
public static void SaveData<T>(string fileName, T dataToSave) {
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication()) {
try {
if (store.FileExists(fileName)) {
store.DeleteFile(fileName);
}
if (!store.DirectoryExists("Settings")) store.CreateDirectory("Settings");
IsolatedStorageFileStream stream;
using (stream = store.OpenFile("Settings/"+fileName+".xml", System.IO.FileMode.Create, System.IO.FileAccess.Write)) {
var serializer = new DataContractSerializer(typeof(T));
serializer.WriteObject(stream, dataToSave);
}
stream.Close();
} catch (System.Security.SecurityException e) {
//MessageBox.Show(e.Message);
return;
}
Debug.WriteLine(store.FileExists("Settings/" + fileName + ".xml"));
}
}
public static T ReadData<T>(string fileName) {
using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication()) {
Debug.WriteLine(store.FileExists("Settings/" + fileName + ".xml"));
if (store.FileExists("Settings/" + fileName + ".xml")) {
IsolatedStorageFileStream stream;
using (stream = store.OpenFile("Settings/"+fileName+".xml", FileMode.OpenOrCreate, FileAccess.Read)) {
try {
var serializer = new DataContractSerializer(typeof(T));
return (T)serializer.ReadObject(stream);
} catch (Exception) {
return default(T);
}
}
stream.Close();
}
return default(T);
}
}
}
Try to add [DataContract] attribute for your ListItem class.
[DataContract]
public class ListItem {
[DataMember]
public String Title { get; set; }
[DataMember]
public bool Checked { get; set; }
public ListItem(String title, bool isChecked=false) {
Title = title;
Checked = isChecked;
}
private ListItem() { }
}
I'm using the Razor/Markdown engine from ServiceStack and am having a bit of difficulty applying my own custom Template/Layout to some rendered Markdown. The Markdown content renders perfectly, I just want to inject that into a Template/Layout file of my choice.
Currently I have this (which works perfectly) :
var rootPath = HttpContext.Current.Server.MapPath("~/");
var markdownPath = Path.Combile(rootPath, "NotFound.md");
var format = new MarkdownFormat();
var markdownContent = File.ReadAllText(markdownPath);
const string pageTitle = "Not Found";
var page = new MarkdownPage(format, rootPath, pageTitle, markdownContent);
format.AddPage(page);
var scopeArgs = new Dictionary<string, object>();
var html = format.RenderDynamicPageHtml(pageTitle, scopeArgs);
Now I have a layout file I wish to use located at "~/ErrorLayout.cshtml", however I have no idea how to inject it. At first I thought to set the Template variable on MarkdownPage to my Layout file's path, however that didn't work. I then tried to call format.AddLayout() and unfortunately that threw an exception.
Any help would be highly appreciated, feel free to ask for any further clarification from myself if I have not made what I am trying to do clear.
So I have solved the problem, however I am uncertain if what I have done is the correct method or not, however it works and no exceptions are thrown. Perhaps somebody with a bit more knowledge could correct me if I have done this incorrectly (so I will leave this question open for a couple days before accepting my answer).
I have created a new class which implements the IVirtualPathProvider interface, and named it PathProvider
I have also created a class which implements the IVirtualFile interface, and named it VirtualFile
I then set the VirtualPathProvider in my instance of MarkdownFormat to a new instance of PathProvider. I then set the Template variable on my instance of Markdownpage to a relative path to the cshtml layout/template file I want to make use of, and within the two classes I mentioned before, returned related content for this Template when requested to do so.
My code now looks like this (in case somebody else has the same problem as me) :
var rootPath = HttpContext.Current.Server.MapPath("~/");
if (contents == null)
{
var notFoundPath = Path.Combine(rootPath, "NotFound.md");
contents = File.ReadAllText(notFoundPath);
}
var format = new MarkdownFormat
{
VirtualPathProvider = new PathProvider()
};
const string pageTitle = "Not Found";
var page = new MarkdownPage(format, rootPath, pageTitle, contents)
{
Template = "~/_Layout.cshtml"
};
format.AddPage(page);
var view = new Dictionary<string, object>();
var html = format.RenderDynamicPageHtml(pageTitle, view);
My PathProvider class looks like this :
public class PathProvider : IVirtualPathProvider
{
public IVirtualDirectory RootDirectory { get; private set; }
public string VirtualPathSeparator { get; private set; }
public string RealPathSeparator { get; private set; }
public string CombineVirtualPath(string basePath, string relativePath)
{
throw new NotImplementedException();
}
public bool FileExists(string virtualPath)
{
throw new NotImplementedException();
}
public bool DirectoryExists(string virtualPath)
{
throw new NotImplementedException();
}
public IVirtualFile GetFile(string virtualPath)
{
return new VirtualFile(this, virtualPath);
}
public string GetFileHash(string virtualPath)
{
throw new NotImplementedException();
}
public string GetFileHash(IVirtualFile virtualFile)
{
throw new NotImplementedException();
}
public IVirtualDirectory GetDirectory(string virtualPath)
{
throw new NotImplementedException();
}
public IEnumerable<IVirtualFile> GetAllMatchingFiles(string globPattern, int maxDepth = 2147483647)
{
throw new NotImplementedException();
}
public bool IsSharedFile(IVirtualFile virtualFile)
{
throw new NotImplementedException();
}
public bool IsViewFile(IVirtualFile virtualFile)
{
throw new NotImplementedException();
}
}
and finally my VirtualFile class :
public class VirtualFile : IVirtualFile
{
public IVirtualDirectory Directory { get; private set; }
public string Name { get; private set; }
public string VirtualPath { get; private set; }
public string RealPath { get; private set; }
public bool IsDirectory { get; private set; }
public DateTime LastModified { get; private set; }
public IVirtualPathProvider VirtualPathProvider { get; private set; }
public string Extension { get; private set; }
public VirtualFile(IVirtualPathProvider virtualPathProvider, string filePath)
{
VirtualPathProvider = virtualPathProvider;
VirtualPath = filePath;
RealPath = HttpContext.Current.Server.MapPath(filePath);
}
public string GetFileHash()
{
throw new NotImplementedException();
}
public Stream OpenRead()
{
throw new NotImplementedException();
}
public StreamReader OpenText()
{
throw new NotImplementedException();
}
public string ReadAllText()
{
return File.ReadAllText(RealPath);
}
}
please help. I have this code, it's my class to serialize\deserialize application settings.
[XmlRoot("EvaStartupData")]
[Serializable]
public class MyConfigClass
{
public string ServerName { get; set; }
public string Database { get; set; }
public string UserName { get; set; }
public string UserLogin { get; set; }
public static void MyConfigLoad()
{
FileInfo fi = new FileInfo(myConfigFileName);
if (fi.Exists)
{
XmlSerializer mySerializer = new XmlSerializer(myConfigClass.GetType());
StreamReader myXmlReader = new StreamReader(myConfigFileName);
try
{
myConfigClass = (MyConfigClass)mySerializer.Deserialize(myXmlReader);
myXmlReader.Close();
}
catch (Exception e)
{
MessageBox.Show("Ошибка сериализации MyConfigLoad\n" + e.Message);
}
finally
{
myXmlReader.Dispose();
}
}
}
public static void MyConfigSave()
{
XmlSerializer mySerializer = new XmlSerializer(myConfigClass.GetType());
StreamWriter myXmlWriter = new StreamWriter(myConfigFileName);
try
{
mySerializer.Serialize(myXmlWriter, myConfigClass);
}
catch (Exception e)
{
MessageBox.Show("Ошибка сериализации MyConfigSave\n" + e.Message);
}
finally
{
myXmlWriter.Dispose();
}
}
}
Serialization give's me simple xml-structure:
<ServerName>navuhodonoser</ServerName>
<Database>matrix</Database>
<UserName>Mr.Smith</UserName>
<UserLogin>neo</UserLogin>
How must i modify my class to get this xml structure ?:
<Connection ServerName="navuhodonoser" Database="matrix" ....>
By default the XmlSerializer will serialize all public properties as elements; to override that you'll need to tag each property with [XmlAttribute] (from System.Xml.Serialization namespace) which will give you your desired output.
For example:
[XmlAttribute]
public string ServerName { get; set; }
[XmlAttribute]
public string Database { get; set; }
[XmlElement]
public string UserName { get; set; }
// Note: no attribute
public string UserLogin { get; set; }
will produce something like:
<xml ServerName="Value" Database="Value">
<UserName>Value</UserName> <!-- Note that UserName was tagged with XmlElement, which matches the default behavior -->
<UserLogin>Value</UserLogin>
</xml>
I have a couple of suggestions. Try code more like this:
public static void MyConfigLoad()
{
if (!File.Exists(myConfigFileName))
{
return;
}
XmlSerializer mySerializer = new XmlSerializer(myConfigClass.GetType());
using (StreamReader myXmlReader = new StreamReader(myConfigFileName))
{
try
{
myConfigClass = (MyConfigClass)mySerializer.Deserialize(myXmlReader);
}
catch (Exception e)
{
MessageBox.Show("Ошибка сериализации MyConfigLoad\n" + e.ToString());
}
}
}
public static void MyConfigSave()
{
XmlSerializer mySerializer = new XmlSerializer(myConfigClass.GetType());
using (StreamWriter myXmlWriter = new StreamWriter(myConfigFileName))
{
try
{
mySerializer.Serialize(myXmlWriter, myConfigClass);
}
catch (Exception e)
{
MessageBox.Show("Ошибка сериализации MyConfigSave\n" + e.ToString());
}
}
}
You should put the StreamReader and StreamWriter in using blocks so that they will be disposed even if an exception occurs. Also, I suggest you always display e.ToString() instead of just e.Message, as it will display the entire exception, including any inner exceptions.
Also, File.Exists works just like FileInfo.Exists, but doesn't require you to create an instance before using it.
One final note is that you should look into using the Settings feature instead of creating your own configuration classes. That allows you to easily create type-safe settings that can be used throughout your application, and which can be per-user or per-application.