How to improve compile time using CSharpCodeProvider - c#

I have an application where user can create its own formula it works fine
for all case but when i have DateTime Fields in my formula the compilation part of the code take around 132 ms for one formula where at the same time i
have 31 formula each with different value.
using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
namespace abc
{
public class Abc
{
static void Main()
{
var code = #"
using System;
namespace First
{
public class program
{
public static int Main()
{
if(Convert.ToDateTime(""01-04-2019 10:25:00"")>Convert.ToDateTime(""01-04-2019 15:00:00""))
{
return 1;
}
else
{
return 0;
}
}
}
}";
Console.WriteLine(code);
var options = new CompilerParameters();
options.GenerateExecutable = false;
options.GenerateInMemory = false;
var provider = new CSharpCodeProvider();
var compile = provider.CompileAssemblyFromSource(options, code);
var type = compile.CompiledAssembly.GetType("First.program");
var abc = Activator.CreateInstance(type);
var method = type.GetMethod("Main");
var result = method.Invoke(abc, null);
Console.WriteLine(result); //output: abc
}
}
}
At this point
var compile = provider.CompileAssemblyFromSource(options, code);
It takes 132 ms but if don't use any date fields it takes 1 ms

Related

Get line number of exception of dynamically loaded assembly

I have an external text file, it is C# class with some logic. In my main program I need to compile that file and run whatever method of that class.
Example of the external class:
using System;
public class DymamicClass
{
public string TestValue()
{
var items = new string[] { "item1", "item2" };
return items[9];
}
}
For loading external file I use these steps:
CSharpCodeProvider Compiler = new CSharpCodeProvider();
List<string> importDlls = new List<string>(new string[] { "System.dll", "System.Data.dll" });
CompilerParameters compilerPars = new CompilerParameters(importDlls.ToArray());
compilerPars.GenerateInMemory = true;
compilerPars.IncludeDebugInformation = true;
compilerPars.CompilerOptions += " /debug:pdbonly";
string path = Assembly.GetExecutingAssembly().Location;
compilerPars.ReferencedAssemblies.Add(path);
CompilerResults Results = Compiler.CompileAssemblyFromFile(compilerPars, codePath);
After the file loaded to the program, I try to execute TestValue() method from the loaded class. There is "index was outside the bounds of the array" exception in the method. I need to get the exact line that threw exception. But instead of line "return items[9];" I always get the line "public string TestValue()".
Here is sample how I process exception:
var trace = new System.Diagnostics.StackTrace(exception, true);
if (trace.FrameCount > 0)
{
var frame = trace.GetFrame(trace.FrameCount - 1);
var className = frame.GetMethod().ReflectedType.Name;
var methodName = frame.GetMethod().ToString();
var lineNumber = frame.GetFileLineNumber();
}
How to get the right line of exception?
you need to get Inner Exception. Try in this way :
using System;
using System.Collections.Generic;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
public class Folders
{
public static void Main(string[] args)
{
try
{
var source = #"using System;
public static class DymamicClass
{
public static string TestValue()
{
var items = new string[] { ""item1"", ""item2"" };
return items[9];
}
}";
CSharpCodeProvider Compiler = new CSharpCodeProvider();
List<string> importDlls = new List<string>(new string[] { "System.dll", "System.Data.dll" });
CompilerParameters compilerPars = new CompilerParameters(importDlls.ToArray());
compilerPars.GenerateInMemory = true;
compilerPars.IncludeDebugInformation = true;
compilerPars.CompilerOptions += " /debug:pdbonly";
string path = Assembly.GetExecutingAssembly().Location;
compilerPars.ReferencedAssemblies.Add(path);
CompilerResults Results = Compiler.CompileAssemblyFromSource(compilerPars, source);
Assembly assembly = Results.CompiledAssembly;
Type program = assembly.GetType("DymamicClass");
MethodInfo main = program.GetMethod("TestValue");
main.Invoke(null, null);
}
catch (Exception e)
{
var trace = new System.Diagnostics.StackTrace(e.InnerException, true);
if (trace.FrameCount > 0)
{
var frame = trace.GetFrame(trace.FrameCount - 1);
var className = frame.GetMethod().ReflectedType.Name;
var methodName = frame.GetMethod().ToString();
var lineNumber = frame.GetFileLineNumber();
}
}
}
}

C# Compile at Runtime Visual Studio unusual behavior

Helo,
i have a strange behavior in Visual Studio.
My code is a compiling at runtime, in VS it
workts fine but if i start the programm from
release-folder the compiled dll seems to be blocked,
what is not that unusal but why does VS dont say that?
Can some one tell me also how to unload a compiled dll
in runtime?
VS:
Release-Folder:
My Test Code here:
static void Main(string[] args)
{
var property = "Value";
var key = "MyKey";
var binding = string.Format("{0}.{1}", key, property);
var dllName = string.Format("Condition_{0}_{1}.dll", key, property);
var input = "65535";
var condition = "Convert.ToInt64(input) > 0xFFFF ? 0 : 501";
for (var i = 0; i < 5; i++)
{
try
{
var conditionCompiled = BuildCondition(key, property, condition);
Type moduleType = null;
if (conditionCompiled.Errors.HasErrors)
{
if (conditionCompiled.Errors[0].ErrorNumber != "CS0042")
throw new ConditionCompileException(conditionCompiled.Errors[0].ErrorText);
var assemblyPath = Path.Combine(Directory.GetCurrentDirectory(), dllName);
var assembly = Assembly.LoadFile(assemblyPath);
moduleType = assembly.GetType("DynaCore.ConditionRunner");
}
else
{
var module = conditionCompiled.CompiledAssembly.GetModules()[0];
moduleType = module.GetType("DynaCore.ConditionRunner");
}
var method = moduleType.GetMethod("RunCondition");
var value = method.Invoke(null, new object[] { input });
Console.WriteLine("DynaCore.ConditionRunner.RunCondition({0}) => {1}", input, value);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
if (e.InnerException != null)
Console.WriteLine(e.InnerException.Message);
}
}
Console.ReadLine();
}
private static CompilerResults BuildCondition(string name, string property, string condition)
{
var csc = new CSharpCodeProvider(new Dictionary<string, string>() { { "CompilerVersion", "v3.5" } });
var parameters = new CompilerParameters(new[] { "mscorlib.dll", "System.Core.dll" }, string.Format("Condition_{0}_{1}.dll", name, property), true);
parameters.GenerateExecutable = false;
var results = csc.CompileAssemblyFromSource(parameters, string.Format(
#" using System.Linq;
using System;
using DESFireSDK.Dependency;
namespace DynaCore
{{
class ConditionRunner
{{
public static object RunCondition(string input)
{{
return {0};
}}
}}
}}"
, condition));
return results;
}

Convert string to executable code performance

I'm looking into solutions to convert a string into executeable code. My code is extremely slow and executes at 4.7 seconds. Is there any faster way of doing this?
String: "5 * 5"
Output: 25
The code:
class Program {
static void Main(string[] args) {
var value = "5 * 5";
Stopwatch sw = new Stopwatch();
sw.Start();
var test = Execute(value);
sw.Stop();
Debug.WriteLine($"Execute string at: {sw.Elapsed}");
}
private static object Execute(string content) {
var codeProvider = new CSharpCodeProvider();
var compilerParameters = new CompilerParameters {
GenerateExecutable = false,
GenerateInMemory = true
};
compilerParameters.ReferencedAssemblies.Add("system.dll");
string sourceCode = CreateExecuteMethodTemplate(content);
CompilerResults compilerResults = codeProvider.CompileAssemblyFromSource(compilerParameters, sourceCode);
Assembly assembly = compilerResults.CompiledAssembly;
Type type = assembly.GetType("Lab.Cal");
MethodInfo methodInfo = type.GetMethod("Execute");
return methodInfo.Invoke(null, null);
}
private static string CreateExecuteMethodTemplate(string content) {
var builder = new StringBuilder();
builder.Append("using System;");
builder.Append("\r\nnamespace Lab");
builder.Append("\r\n{");
builder.Append("\r\npublic sealed class Cal");
builder.Append("\r\n{");
builder.Append("\r\npublic static object Execute()");
builder.Append("\r\n{");
builder.AppendFormat("\r\nreturn {0};", content);
builder.Append("\r\n}");
builder.Append("\r\n}");
builder.Append("\r\n}");
return builder.ToString();
}
}
Well, there is an easier hack:
var _Result = new DataTable().Compute("5*5"); // _Result = 25
var _Result2 = new DataTable().Compute("5+5*5"); // _Result2 = 30
It also has more options. Please have a look at the Documentation.

Execute code lines from a text file in C#

I have a text file looks like:
AssembleComponent Motor = new AssembleComponent;
AssembleComponent Shaft = new AssembleComponent;
......
Motor.cost = 100;
Motor.quantity = 100;
Shaft.cost = 10;
Shaft.quantity = 100;
......
I wish to execute these code lines in C#, so that I will have these Motor.cost, Motor.quantity, Shaft.cost, Shaft.quantity variables stored in the memory for later calculation.
What can I do to achieve this?
Store it as XML instead
<?xml version="1.0" encoding="UTF-8"?>
<Components>
<Component name="Motor" cost="100" quantity="100" />
<Component name="Shaft" cost="10" quantity="100" />
</Components>
Assuming that you have this definition
public class AssembleComponent
{
public decimal Cost { get; set; }
public int Quantity { get; set; }
}
Load it like this
var components = new Dictionary<string, AssembleComponent>();
XDocument doc = XDocument.Load(#"C:\Users\Oli\Desktop\components.xml");
foreach (XElement el in doc.Root.Descendants()) {
string name = el.Attribute("name").Value;
decimal cost = Decimal.Parse(el.Attribute("cost").Value);
int quantity = Int32.Parse(el.Attribute("quantity").Value);
components.Add(name, new AssembleComponent{
Cost = cost, Quantity = quantity
});
}
You can then access the components like this
AssembleComponent motor = components["Motor"];
AssembleComponent shaft = components["Shaft"];
Note: Creating the variable names dynamically by calling the compiler at runtime is not very useful since you need to know them at compile-time (or design-time if you prefer) to do something useful with them. Therefore, I added the components to a dictionary. This is a good way of creating "variables" dynamically.
You can use Microsoft.CSharp.CSharpCodeProvider to compile code on-the-fly.
Specifically, take a look at CompileAssemblyFromFile.
If it's just about data don't use a flat textfile but XML-instead.
You can deserialize the XML in to objects and perform the necessary actions on them.
Here's some code that I've used in the past, that does most of what you want though you may need to adapt it to your specific needs. In a nutshell, it does the following:
Create a temporary namespace and a public static method in that namespace.
Compile the code to an in-memory assembly.
Extract the compiled method and turn it into a delegate.
Execute the delegate.
At that point it's like executing a normal static method, so when you say you want the results in memory for later use, you'd have to figure out how that would work.
public void CompileAndExecute(string CodeBody)
{
// Create the compile unit
CodeCompileUnit ccu = CreateCode(CodeBody);
// Compile the code
CompilerParameters comp_params = new CompilerParameters();
comp_params.GenerateExecutable = false;
comp_params.GenerateInMemory = true;
comp_params.TreatWarningsAsErrors = true;
comp_results = code_provider.CompileAssemblyFromDom(comp_params, ccu);
// CHECK COMPILATION RESULTS
if (!comp_results.Errors.HasErrors)
{
Type output_class_type = comp_results.CompiledAssembly.GetType("TestNamespace.TestClass");
if (output_class_type != null)
{
MethodInfo compiled_method = output_class_type.GetMethod("TestMethod", BindingFlags.Static | BindingFlags.Public);
if (compiled_method != null)
{
Delgate created_delegate = Delegate.CreateDelegate(typeof(System.Windows.Forms.MethodInvoker), compiled_method);
if (created_delegate != null)
{
// Run the code
created_delegate.DynamicInvoke();
}
}
}
}
else
{
foreach (CompilerError error in comp_results.Errors)
{
// report the error
}
}
}
public CodeCompileUnit CreateCode(string CodeBody)
{
CodeNamespace code_namespace = new CodeNamespace("TestNamespace");
// add the class to the namespace, add using statements
CodeTypeDeclaration code_class = new CodeTypeDeclaration("TestClass");
code_namespace.Types.Add(code_class);
code_namespace.Imports.Add(new CodeNamespaceImport("System"));
// set function details
CodeMemberMethod method = new CodeMemberMethod();
method.Attributes = MemberAttributes.Public | MemberAttributes.Static;
method.ReturnType = new CodeTypeReference(typeof(void));
method.Name = "TestMethod";
// add the user typed code
method.Statements.Add(new CodeSnippetExpression(CodeBody));
// add the method to the class
code_class.Members.Add(method);
// create a CodeCompileUnit to pass to our compiler
CodeCompileUnit ccu = new CodeCompileUnit();
ccu.Namespaces.Add(code_namespace);
return ccu;
}
You have two main options:
Expand the text until it becomes valid C# code, compile it and execute it
Parse it and execute it yourself (i.e. interpret it).
This can be done following these steps: CodeGeneration => InMemory Compilation to Exe ==> Execution.
You can design the construct similar to this:
public bool RunMain(string code)
{
const string CODE_NAMESPACE = "CompileOnFly";
const string CODE_CLASS = "Program";
const string CODE_METHOD = "Main";
try
{
var code_namespace = new CodeNamespace(CODE_NAMESPACE);
// add the class to the namespace, add using statements
var code_class = new CodeTypeDeclaration(CODE_CLASS);
code_namespace.Types.Add(code_class);
code_namespace.Imports.Add(new CodeNamespaceImport("System"));
// set function details
var method = new CodeMemberMethod();
method.Attributes = MemberAttributes.Public | MemberAttributes.Static;
method.ReturnType = new CodeTypeReference(typeof(void));
method.Name = CODE_METHOD;
// add the user typed code
method.Statements.Add(new CodeSnippetExpression(code));
// add the method to the class
code_class.Members.Add(method);
// create a CodeCompileUnit to pass to our compiler
CodeCompileUnit code_compileUnit = new CodeCompileUnit();
code_compileUnit.Namespaces.Add(code_namespace);
var compilerParameters = new CompilerParameters();
compilerParameters.ReferencedAssemblies.Add("system.dll");
compilerParameters.GenerateExecutable = true;
compilerParameters.GenerateInMemory = true;
compilerParameters.TreatWarningsAsErrors = true;
var code_provider = CodeDomProvider.CreateProvider("CSharp");
var comp_results = code_provider.CompileAssemblyFromDom(compilerParameters, code_compileUnit);
if (comp_results.Errors.HasErrors)
{
StringBuilder sb = new StringBuilder();
foreach (CompilerError error in comp_results.Errors)
{
sb.AppendLine(String.Format("Error ({0}): {1}", error.ErrorNumber, error.ErrorText));
}
throw new InvalidOperationException(sb.ToString());
}
//Get assembly, type and the Main method:
Assembly assembly = comp_results.CompiledAssembly;
Type program = assembly.GetType($"{CODE_NAMESPACE}.{CODE_CLASS}");
MethodInfo main = program.GetMethod(CODE_METHOD);
//runtit
main.Invoke(null, null);
return true;
}
catch(Exception compileException)
{
Console.Write(compileException.ToString());
return false;
}
}
In the code above we are actually creating a simple console Program.Main() as
namespace CompileOnFly
{
internal class Program
{
static void Main()
{
//<your code here>
}
}
}
in memory then compiling it as executable in Memory and executing it. But the Main() body //<your code here> is added dynamically with the parameter code to the method.
So If you have a script in the text file script.txt as this:
Console.Write("Write your name: ");
var name = Console.ReadLine();
Console.WriteLine("Happy new year 2023 " + name);
You can simply read all the text and send it as parameter to it:
var code = File.ReadAllText(#"script.txt");
RunMain(code);
To run the statements in the script.txt file.

How to use a dynamic CSV delimiter with FileHelpers?

Question:
I need to read a CSV file.
I use the FileHelpers library to achieve this.
The problem is I need a dynamic delimiter (user defined), meaning anything can be delimiter (Comma, semicolon, tab, newline, but also anything else).
The problem is, FileHelpers defines the delimiter in an attribute, which means at compile-time. This makes it impossible to do it dynamically.
What I can do is declare a new class, which inherits from one base class, and set the delimiter on this new class.
[FileHelpers.DelimitedRecord(",")]
public class CommaCustomer : BaseCustomer
{
}
That way I only have to make changes in the base class for every new delimiter.
The problem is, this is I can't (and don't want to) create a child class for every possible delimiter.
This is the code I have so far:
using System;
using System.Data;
using System.IO;
//using FileHelpers;
//using FileHelpers.RunTime;
namespace Examples
{
class MainClass
{
[STAThread]
static void Main()
{
FileHelpers.FileHelperEngine engine = new FileHelpers.FileHelperEngine(typeof(SemicolonCustomer));
// To read use:
string str = #"D:\Username\Desktop\FileHelpers_Examples_CSharp_VbNet\Data\SemicolonCustomers.txt";
//str = #"D:\Username\Desktop\FileHelpers_Examples_CSharp_VbNet\Data\CustomersDelimited.txt";
SemicolonCustomer[] custs = (SemicolonCustomer[])engine.ReadFile(str);
//Customer[] custs = (Customer[]) engine.ReadFile("yourfile.txt");
foreach (SemicolonCustomer cli in custs)
{
Console.WriteLine();
Console.WriteLine("Customer: " + cli.CustId.ToString() + " - " + cli.Name);
Console.WriteLine("Added Date: " + cli.AddedDate.ToString("d-M-yyyy"));
Console.WriteLine("Balance: " + cli.Balance.ToString());
Console.WriteLine();
Console.WriteLine("-----------------------------");
} // Next cli
Console.ReadKey();
Console.WriteLine("Writing data to a delimited file...");
Console.WriteLine();
// To write use:
//engine.WriteFile("myyourfile.txt", custs);
//If you are using .NET 2.0 or greater is
//better if you use the Generics version:
// FileHelperEngine engine = new FileHelperEngine<Customer>();
// To read use (no casts =)
// Customer[] custs = engine.ReadFile("yourfile.txt");
// To write use:
// engine.WriteFile("yourfile.txt", custs);
} // End Sub Main
} // End Class MainClass
//------------------------
// RECORD CLASS (Example, change at your will)
// TIP: Remember to use the wizard to generate this class
public class BaseCustomer
{
public int CustId;
public string Name;
public decimal Balance;
[FileHelpers.FieldConverter(FileHelpers.ConverterKind.Date, "ddMMyyyy")]
public DateTime AddedDate;
}
[FileHelpers.DelimitedRecord(";")]
public class SemicolonCustomer : BaseCustomer
{
}
[FileHelpers.DelimitedRecord(",")]
public class CommaCustomer : BaseCustomer
{
}
}
Is it somehow possible at runtime to compile a child class
[FileHelpers.DelimitedRecord(\"" + delimiter + "\")]
public class AnyDelimiterCustomer : BaseCustomer
{
}
And then reference this runtime compiled class in code ?
I just realized there is a DelimitedFileEngine which solves your problem another way.
You can just go
var engine = new DelimitedFileEngine(typeof(BaseCustomer));
engine.Options.Delimiter = ",";
It seems that BaseCustomer needs to be decorated with a [DelimitedRecord] attribute, otherwise an exception is raised but the delimiter is overridden by whatever is supplied to engine.Options.Delimiter.
The following example imports a comma delimited record using a format which is marked as bar delimited.
[DelimitedRecord("|")]
public class Format1
{
public string Field1;
public string Field2;
public string Field3;
public string Field4;
}
static void Main(string[] args)
{
var engine = new DelimitedFileEngine(typeof(Format1));
// change the delimiter
engine.Options.Delimiter = ",";
// import a comma separated record
object[] importedObjects = engine.ReadString(#"a,b,c,d");
foreach (object importedObject in importedObjects)
{
if (importedObject is Format1)
{
Format1 format1 = (Format1)importedObject;
// process it (for example, check the values)
Assert.AreEqual("a", format1.Field1);
Assert.AreEqual("b", format1.Field2);
Assert.AreEqual("c", format1.Field3);
Assert.AreEqual("d", format1.Field4);
}
}
}
No thats not possible.
But you can use the FileHelper DelimitedClassBuilder to build a dynamic file parser where you can set the delimiter at runtime:
DelimitedClassBuilder dcb = new DelimitedClassBuilder("Name",
"Here goes your col separator");
// You have to build your field definitions by hand now
dcb.AddField("FieldName", typeof(decimal));
...
// build the engine
DelimitedFileEngine fileEngine = new DelimitedFileEngine(dcb.CreateRecordClass());
// read the file
dynamic[] data = fileEngine.ReadFile(filePath);
You can use runtime classes. You have two choices. Either compile your class from a string
For instance
// The class definition
public string mClass =
#"
[DelimitedRecord(""" + delimiter + #""")]
public class BaseCustomer
{
public int CustId;
public string Name;
public decimal Balance;
[FileHelpers.FieldConverter(FileHelpers.ConverterKind.Date, ""ddMMyyyy"")]
public DateTime AddedDate;
}
";
Type t = ClassBuilder.ClassFromString(mClass);
FileHelperEngine engine = new FileHelperEngine(t);
DataTable = engine.ReadFileAsDT("test.txt");
Or alternatively, you can use the DelimitedClassBuilder class.
DelimitedClassBuilder cb = new DelimitedClassBuilder("BaseCustomer", delimiter);
cb.AddField("CustId", typeof(int));
cb.LastField.TrimMode = TrimMode.Both;
cb.LastField.FieldNullValue = 0;
cb.AddField("Balance", typeof(Decimal));
cb.AddField("AddedDate", typeof(DateTime));
engine = new FileHelperEngine(cb.CreateRecordClass());
DataTable dt = engine.ReadFileAsDT("test.txt");
It is possible.
But only by moving the serialization type into a separate assembly.
Like this:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace FlaechenupdateScript
{
static class Program
{
// http://www.codeproject.com/KB/cs/runtimecompiling.aspx
private static System.Reflection.Assembly BuildAssembly(string code)
{
Microsoft.CSharp.CSharpCodeProvider provider =
new Microsoft.CSharp.CSharpCodeProvider();
System.CodeDom.Compiler.ICodeCompiler compiler = provider.CreateCompiler();
System.CodeDom.Compiler.CompilerParameters compilerparams = new System.CodeDom.Compiler.CompilerParameters();
string strLocation = System.Reflection.Assembly.GetExecutingAssembly().Location;
string strBasePath = System.IO.Path.GetDirectoryName(strLocation);
string strSerializationTypes = System.IO.Path.Combine(strBasePath, "SerializationTypes.dll");
string strFileHelpersLocation = System.IO.Path.Combine(strBasePath, "FileHelpers.dll");
compilerparams.ReferencedAssemblies.Add(strSerializationTypes);
compilerparams.ReferencedAssemblies.Add(strFileHelpersLocation);
compilerparams.GenerateExecutable = false;
compilerparams.GenerateInMemory = true;
System.CodeDom.Compiler.CompilerResults results =
compiler.CompileAssemblyFromSource(compilerparams, code);
if (results.Errors.HasErrors)
{
System.Text.StringBuilder errors = new System.Text.StringBuilder("Compiler Errors :\r\n");
foreach (System.CodeDom.Compiler.CompilerError error in results.Errors)
{
errors.AppendFormat("Line {0},{1}\t: {2}\n",
error.Line, error.Column, error.ErrorText);
}
throw new Exception(errors.ToString());
}
else
{
return results.CompiledAssembly;
}
} // End Function BuildAssembly
public static Type GetClassType(Type tt, string strDelimiter)
{
string strFullTypeName = tt.FullName;
string strTypeUniqueName = System.Guid.NewGuid().ToString() + System.Guid.NewGuid().ToString() + System.Guid.NewGuid().ToString() + System.Guid.NewGuid().ToString();
strTypeUniqueName = "_" + strTypeUniqueName.Replace("-", "_");
string xx = #"
namespace CrapLord
{
[FileHelpers.IgnoreFirst]
[FileHelpers.IgnoreEmptyLines]
[FileHelpers.DelimitedRecord(""" + strDelimiter + #""")]
public class " + strTypeUniqueName + #" : " + strFullTypeName + #"
{
}
}
";
System.Reflection.Assembly a = BuildAssembly(xx);
var o = a.CreateInstance("CrapLord." + strTypeUniqueName);
Type t = o.GetType();
//System.Reflection.MethodInfo mi = t.GetMethod("EvalCode");
//var s = mi.Invoke(o, null);
return t;
}
/// <summary>
/// Der Haupteinstiegspunkt für die Anwendung.
/// </summary>
[STAThread]
static void Main()
{
//Application.EnableVisualStyles();
//Application.SetCompatibleTextRenderingDefault(false);
//Application.Run(new Form1());
Type t = GetClassType(typeof(Tools.Serialization.CSV.Customer), ",");
//FileHelpers.FileHelperEngine engine = new FileHelpers.FileHelperEngine(typeof(SemicolonCustomer));
FileHelpers.FileHelperEngine engine = new FileHelpers.FileHelperEngine(t);
string str = "path/to/datafile";
Tools.Serialization.CSV.Customer[] custs = (Tools.Serialization.CSV.Customer[])engine.ReadFile(str);
//Customer[] custs = (Customer[]) engine.ReadFile("yourfile.txt");
foreach (Tools.Serialization.CSV.Customer cli in custs)
{
Console.WriteLine();
Console.WriteLine("Customer: " + cli.CustId.ToString() + " - " + cli.Name);
Console.WriteLine("Added Date: " + cli.AddedDate.ToString("d-M-yyyy"));
Console.WriteLine("Balance: " + cli.Balance.ToString());
Console.WriteLine();
Console.WriteLine("-----------------------------");
} // Next cli
Console.WriteLine(Environment.NewLine);
Console.WriteLine(" --- Press any key to continue --- ");
Console.ReadKey();
}
}
}
SerializationTypes Assembly:
using System;
using System.Collections.Generic;
using System.Text;
namespace Tools.Serialization.CSV
{
//------------------------
// RECORD CLASS (Example, change at your will)
// TIP: Remember to use the wizard to generate this class
public class Customer
{
public int CustId;
public string Name;
public decimal Balance;
[FileHelpers.FieldConverter(FileHelpers.ConverterKind.Date, "ddMMyyyy")]
public DateTime AddedDate;
}
}
Maybe you want to use the TextFieldParser from Microsoft.VisualBasic.FileIO Namespace:
string[] fields;
string[] delimiter = new string[] { "|" };
using (Microsoft.VisualBasic.FileIO.TextFieldParser parser =
new Microsoft.VisualBasic.FileIO.TextFieldParser(filename))
{
parser.Delimiters = delimiter;
parser.HasFieldsEnclosedInQuotes = false;
while (!parser.EndOfData)
{
fields = parser.ReadFields();
//Do what you need
}
}
adding a cast solves the problem for me (FileHelpers V3.5.1)
var engine = new DelimitedFileEngine(typeof(BaseCustomer));
((FileHelpers.Options.DelimitedRecordOptions)engine.Options).Delimiter=",";

Categories