I have a class which implements IDisposable like such
public class SomeClass : IDisposable
{
private IList<string> _someList = new List<string>();
public IList<string> SomeList
{
get { return _someList; }
}
public void Dispose()
{
_someList = null;
}
}
and a method that uses 'using' blocks to create two instances of this class and add it to a collection like such
private static void Main(string[] args)
{
IList<SomeClass> classes = new List<SomeClass>();
using (var sc = new SomeClass())
{
sc.SomeList.Add("a");
classes.Add(sc);
}
using (var sc = new SomeClass())
{
sc.SomeList.Add("b");
classes.Add(sc);
}
foreach (var a in classes)
{
Console.WriteLine(a.SomeList[0]);
}
Console.ReadLine();
}
Whats happening here is by the time i come to the foreach to iterate through my elements in "classes" the "SomeList" property of both my objects are null.
I do understand that since each using executes Dispose() and in the dispose, i AM null-ing the two lists, they will be null.
My question is how do i achieve this without having to stop null-ing "SomeList" inside my dispose().
thanks
--UPDATE 1
Something closer to real code
base.OnPreRender(e);
Page page = HttpContext.Current.CurrentHandler as Page;
HtmlHead head = (HtmlHead)page.Header;
if (Settings.AreSet)
{
using (var noIndex = new HtmlMeta())
{
noIndex.Name = "somename";
noIndex.Content = "somecontent";
head.Controls.AddAt(0, noIndex);
}
}
using (var machineName = new HtmlMeta())
{
machineName.Name = "somename2";
machineName.Content = "somecontent2";
head.Controls.AddAt(1, machineName);
}
UpdateHeader(head);
--UPDATE #2
A variant of the above class with a string property now, as opposed to a a list.
public class SomeClass2 : IDisposable
{
public string SomeString { get; set; }
public void Dispose()
{
SomeString = string.Empty;
}
}
It still does the same thing for the following method. My 'classes' contains lists of 'SomeClass2' which has 'SomeString' as empty (what i set in my dispose())
IList<SomeClass2> classes = new List<SomeClass2>();
using (var sc = new SomeClass2())
{
sc.SomeString = "a";
classes.Add(sc);
}
using (var sc = new SomeClass2())
{
sc.SomeString = "a";
classes.Add(sc);
}
foreach (var a in classes)
{
Console.WriteLine(a.SomeString);
}
Console.ReadLine();
You don't want Dispose to be called? Just don't use using statement.
Or move the entire logic inside using:
private static void Main(string[] args)
{
IList<SomeClass> classes = new List<SomeClass>();
using (var sc = new SomeClass())
{
sc.SomeList.Add("a");
classes.Add(sc);
using (var sc = new SomeClass())
{
sc.SomeList.Add("b");
classes.Add(sc);
foreach (var a in classes)
{
Console.WriteLine(a.SomeList[0]);
}
}
}
Console.ReadLine();
}
Btw: looks like using IDisposable in that case is pointless. GC would gather unused List<T> objects anyway.
Related
I use following method to update orgnrs in a database:
UpdateService service = new UpdateService()
service.UpdateDocsA(orgnrs);
public void UpdateDocsA(List<string> orgnrs)
{
using (var context = new Data.InexchangeEntitiesA())
{
foreach (string orgnr in orgnrs)
{
try
{
Data.Customers customer = new Data.Customers();
customer.CustNo = orgnr.Trim();
context.Customers.Add(customer);
}
catch (Exception ex)
{
}
}
context.SaveChanges();
}
}
Problem is that I have multiple databases with similar update.
service.UpdateDocsA(orgnrs);
service.UpdateDocsB(orgnrs);
service.UpdateDocsC(orgnrs);
service.UpdateDocsC(orgnrs);
The only difference is in following line:
using (var context = new Data.InexchangeEntitiesA())
using (var context = new Data.InexchangeEntitiesB())
using (var context = new Data.InexchangeEntitiesC())
using (var context = new Data.InexchangeEntitiesD())
I want to create a generic update method. Any ideas how can I achieve it? or how to pass Data.InexchangeEntities to a method?
Assuming that the method signature for InexchangeEntitiesA() and InexchangeEntitiesB() etc. is common why not pass that in to your UpdateDocs method?
If we assume those methods all return an IDataContext object which implements a Customers() method;
UpdateService service = new UpdateService()
service.UpdateDocs(Data.InexchangeEntitiesA, orgnrs);
service.UpdateDocs(Data.InexchangeEntitiesB, orgnrs);
service.UpdateDocs(Data.InexchangeEntitiesC, orgnrs);
service.UpdateDocs(Data.InexchangeEntitiesD, orgnrs);
public void UpdateDocs<T>(Func<T> getContext, List<string> orgnrs) where T : IDataContext
{
using (var context = getContext())
{
foreach (string orgnr in orgnrs)
{
try
{
Data.Customers customer = context.Customers();
customer.CustNo = orgnr.Trim();
context.Customers.Add(customer);
}
catch (Exception ex)
{
}
}
context.SaveChanges();
}
}
Or something like this
public class Customer { }
public interface ISomeFunkyInterface
{
DbSet<Customer> Customers { get; set; }
}
public class DbContextA : DbContext, ISomeFunkyInterface
{
public DbSet<Customer> Customers { get; set; }
}
public void UpdateDocs<T>(List<string> orgnrs)
where T : DbContext, ISomeFunkyInterface ,new()
{
using var context = new T();
foreach (string orgnr in orgnrs)
{
context.Customers.Add(...);
}
context.SaveChanges();
}
service.UpdateDocs(orgnrs, new Data.InexchangeEntitiesA());
service.UpdateDocs(orgnrs, new Data.InexchangeEntitiesB());
service.UpdateDocs(orgnrs, new Data.InexchangeEntitiesC());
service.UpdateDocs(orgnrs, new Data.InexchangeEntitiesD());
service.UpdateDocs(orgnrs, new Data.InexchangeEntitiesE());
...
public void UpdateDocs(List<string> orgnrs, YOURCONTEXTTYPE context)
{
using (context)
{
foreach (string orgnr in orgnrs)
{
try
{
Data.Customers customer = new Data.Customers();
customer.CustNo = orgnr.Trim();
context.Customers.Add(customer);
}
catch (Exception ex)
{
}
}
context.SaveChanges();
}
}
There are multiple ways to achieve that. The most obvious to me is extract what is common and create multiple methods for that:
public void UpdateDocsA(List<string> orgnrs)
{
using (var context = new Data.InexchangeEntitiesA())
{
Perform(context, orgnrs);
}
}
void Perform(DbContext context, List<string> orgnrs)
{
foreach (string orgnr in orgnrs)
{
try
{
Data.Customers customer = new Data.Customers();
customer.CustNo = orgnr.Trim();
context.Customers.Add(customer);
}
catch (Exception ex)
{ }
}
context.SaveChanges();
}
This will reduce the duplicated code massivly. However it still feels ugly, because you still have four Update-methods.
You could also extract what is uncommon and inject a paremeter to switch on:
public void UpdateDocs(string type, List<string> orgnrs)
{
using (var context = GetContext(type))
{
Perform(context, orgnrs);
}
}
DbContext GetContext(string type)
{
return type == "A" ? new Data.InexchangeEntitiesA() :
type == "B" ? new Data.InexchangeEntitiesB() :
type == "C" ? new Data.InexchangeEntitiesC() :
new Data.InexchangeEntitiesD();
}
This does not seem much better, does it? We only have a single method, however it still uses some weird type-switching. Let´s try something more generic - assuming all your entities have some common base-interface or -class:
public void UpdateDocs<T>(List<string> orgnrs) where T: IMyInterface, new()
{
using (var context = new T)
{
Perform(context, orgnrs);
}
}
I'm trying to map CSV file into class object with C#. My problem is that i have 3 different files, but I want to fallow DRY principles. Can someone tell me how to change 'ParseLine' method to make it possible?
C# consol app.
This is how my FileReader looks like:
public class FileReader<T> : IFileReader<T> where T : Entity
{
private readonly ITransactionReader<T> _transactionReader;
public FileReader(ITransactionReader<T> transactionReader)
{
_transactionReader = transactionReader;
}
public List<T> GetInfoFromFile(string filePath)
{
var lines = File.ReadAllLines(filePath);
var genericLines = new List<T>();
foreach (var line in lines)
{
genericLines.Add(_transactionReader.ParseLine(line));
}
return genericLines;
}
}
public interface IFileReader<T> where T : Entity
{
List<T> GetInfoFromFile(string filePath);
}
This is how the object should look like.
public class TransactionReader : ITransactionReader<Transaction>
{
public Transaction ParseLine(string line)
{
var fields = line.Split(";");
var transaction = new Transaction()
{
Id = fields[0],
Month = int.Parse(fields[1]),
Day = int.Parse(fields[2]),
Year = int.Parse(fields[3]),
IncomeSpecification = fields[4],
TransactionAmount = int.Parse(fields[5])
};
return transaction;
}
}
public interface ITransactionReader<T>
{
T ParseLine(string line);
}
This is how I run it for test purposes.
class Program
{
private static readonly string filePath = "C:/Users/<my_name>/Desktop/C# Practice/ERP/ERP/CsvFiles/Transaction.csv";
static void Main(string[] args)
{
ITransactionReader<Transaction> transactionReader = new TransactionReader();
IFileReader<Transaction> fileReader = new FileReader<Transaction>(transactionReader);
List<Transaction> Test()
{
var obj = fileReader.GetInfoFromFile(filePath);
return obj;
}
var list = Test();
}
}
I'm looking to modify that line:
genericLines.Add(_transactionReader.ParseLine(line));
and method arguments to make it open for any CSV fil.
I don't mind to change that composition into something more effective.
I' don't know much about C#.I'm trying to create a program that can calculate results for running competitions. I have created two methods: DoStuff(), with which I set up a Datagridview table columns and displays a text file splitted.And Method- Sorter(), with which I sort the runners into groups and display them in to the List boxes.
My problem.
Every time when i try to debug a program i get a following error:
'Cannot read from a closed TextReader.'
As you can see i set the StreamReader as public. But those two methods can not access it.
Is it possible in some way like this to use one StreamReader?!
Or i have to write my code in Form-Load and then it is the only way to access it?
My Code
public partial class Form1 : Form
{
public StreamReader sr = new StreamReader(#"C:\Users\Oscar\Desktop\result.txt");
public string line;
public List<string> V = new List<string>();
public List<string> V12 = new List<string>();
public List<string> V14 = new List<string>();
public List<string> V18 = new List<string>();
public List<string> V50 = new List<string>();
public List<string> VB = new List<string>();
public List<string> VC = new List<string>();
public List<string> S = new List<string>();
public List<string> S12 = new List<string>();
public List<string> S14 = new List<string>();
public List<string> S18 = new List<string>();
public List<string> S40 = new List<string>();
public Form1()
{
InitializeComponent();
}
public void ReadFile()
{
}
private void Form1_Load(object sender, EventArgs e)
{
SORTER();
Dostuff();
}
public void Dostuff()
{
if (!System.IO.File.Exists(#"C:\Users\Oscar\Desktop\result.txt"))
return;
dataGridView1.ColumnCount = 3;
dataGridView1.Columns[0].HeaderCell.Value = "Number";
dataGridView1.Columns[1].HeaderCell.Value = "name";
dataGridView1.Columns[2].HeaderCell.Value = "time";
using (sr)
while (sr.Peek() > -1 )
{
dataGridView1.Rows.Add(sr.ReadLine().Split(';'));
}
}
public void SORTER()
{
using (sr)
while ((line = sr.ReadLine()) != null)
{
if (line.Contains(";V;")) { V.Add(line); }
else if (line.Contains(";V12;")) { V12.Add(line); }
else if (line.Contains(";V14;")) { V14.Add(line); }
else if (line.Contains(";V18;")) { V18.Add(line); }
else if (line.Contains(";V50;")) { V50.Add(line); }
else if (line.Contains(";S12;")) { S12.Add(line); }
else if (line.Contains(";S14;")) { S14.Add(line); }
else if (line.Contains(";S18;")) { S18.Add(line); }
else if (line.Contains(";S40;")) { S40.Add(line); }
if (line.Contains(";S;")) { S.Add(line); }
}
listBox1.DataSource = V12;
listBox2.DataSource = V50;
listBox3.DataSource = V;
listBox4.DataSource = S;
listBox5.DataSource = S18;
listBox6.DataSource = S40;
}
}
Don't do this inside your Form definition:
public StreamReader sr = new StreamReader(#"C:\Users\Oscar\Desktop\result.txt");
That opens your file once when your form is created and leaves it open. Doing that can block or crash your application if you get an IO error, and often causes “file is in use” errors. You should open a file only when you actually need to use it, then close it immediately after. Wherever you do:
using (sr)
you should instead be doing this:
using (var sr = new StreamReader(#"C:\Users\Oscar\Desktop\result.txt"))
Actually, you should be doing this:
using (var sr = new StreamReader(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "result.txt")))
You have your StreamReader in a using block in your SORTER() method. This means after that block ends, the StreamReader will be disposed. When you try to reuse the same StreamReader in your DoStuff() method, you will get the error you described, as that StreamReader is no longer in a state to be read from.
Instead of having a global variable for your StreamReader, you could create these as local variables in each of your methods.
I have a failing testcase that depends on an external module.
I want to use Rhino Mock to generate a report on called functions.
I created a minimal example that illustrates my problem:
using NUnit.Framework;
using Rhino.Mocks;
using System;
namespace StackOverflow_namespace
{
public interface IUsefulService
{
object HiddenAmongManyCalls();
}
public class ThirdPartyBase
{
private int a = 42;
public ThirdPartyBase(IUsefulService service)
{
object liveFastDieYoung = service.HiddenAmongManyCalls();
liveFastDieYoung.Equals(a);
}
}
public class MyParty : ThirdPartyBase
{
public MyParty(IUsefulService service) : base(service)
{
}
}
[TestFixture]
class StackOverflow
{
[Test]
public void Hypothetical()
{
IUsefulService service = MockRepository.GenerateMock<IUsefulService>();
try
{
var party = new MyParty(service);
}
catch(Exception e)
{
string[] calls = MagicallyGetTheCallsThatWereMadeToTheMock();
foreach(var call in calls)
{
//with my visual studio testrunner for nunit 3 I can investigate stored console output
Console.WriteLine(call);
}
Assert.Fail("Excpexted no exception but was '" + e.GetType().Name + "': " + e.Message);
}
}
private string[] MagicallyGetTheCallsThatWereMadeToTheMock()
{
return new[]
{
"This is where I am lost, I do not know how to get the calls from the repository."
};
}
}
}
I tried to find something online without success.
Do Rhino Mocks record all calls and can I access that list?
Edit:
An attempt to verify Expectations did not work since I am looking for calls I did not expect.
I could build a list of calls using GetArgumentsForCallsMadeOn. I can reflect on the Interface. I started on a method for that but I currently fail to see how I can convert a MethodInfo to an Action<T>.
private IEnumerable<string> GetCallsList<Interface>(Interface rhinomock)
{
Type interfaceType = typeof(Interface);
List<MethodInfo> interfaceMethodInfos = new List<MethodInfo>();
List<string> returnInfos = new List<string>();
StringBuilder callbuilder = new StringBuilder();
foreach (var property in interfaceType.GetProperties())
{
interfaceMethodInfos.Add(property.GetGetMethod());
interfaceMethodInfos.Add(property.GetSetMethod());
}
foreach (var method in interfaceType.GetMethods())
{
interfaceMethodInfos.Add(method);
}
foreach (var methodinfo in interfaceMethodInfos)
{
Action<Interface> magic = null; //convert methodinfo into action - still missing
var calls = rhinomock.GetArgumentsForCallsMadeOn(magic); //magic is currently null, here be crash
foreach (var call in calls)
{
bool more = false;
callbuilder.Clear().Append(interfaceType.Name).Append('.').Append(methodinfo.Name).Append('(');
foreach (var parameter in call)
{
if (more){ callbuilder.Append(", "); }
if (null == parameter) { callbuilder.Append("<null>"); }
else { callbuilder.Append(parameter.ToString()); }
more = true;
}
callbuilder.Append(')');
string callInfo = callbuilder.ToString();
returnInfos.Add(callInfo);
}
}
return returnInfos;
}
I was able to use reflection to get the output I wanted.
Here is the minimal example where the test fails and the output contains all method calls.
using NUnit.Framework;
using Rhino.Mocks;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;
namespace StackOverflow_namespace
{
public interface IUsefulService
{
object HiddenAmongManyCalls();
string TestCall2(string arg1, int arg2);
string FULLACCESS { get; set; }
string READONLY { get; }
}
public class ThirdPartyBase
{
private int a = 42;
public ThirdPartyBase(IUsefulService service)
{
service.TestCall2("callA", 1);
service.TestCall2("callB", 1);
object liveFastDieYoung = service.HiddenAmongManyCalls();
service.TestCall2("callA", 2);
service.TestCall2("callB", 2);
var a = service.FULLACCESS;
var b = service.READONLY;
service.FULLACCESS = "some";
liveFastDieYoung.Equals(a);
}
}
public class MyParty : ThirdPartyBase
{
public MyParty(IUsefulService service) : base(service)
{
}
}
[TestFixture]
class StackOverflow
{
[Test]
public void Hypothetical()
{
IUsefulService service = MockRepository.GenerateMock<IUsefulService>();
try
{
var party = new MyParty(service);
}
catch (Exception e)
{
var calls = GetCallsList(service);
foreach (var call in calls)
{
//with my visual studio testrunner for nunit 3 I can investigate stored console output
Console.WriteLine(call);
}
Assert.Fail("Excpexted no exception but was '" + e.GetType().Name + "': " + e.Message);
}
}
private IEnumerable<string> GetCallsList<Interface>(Interface rhinomock)
{
Type interfaceType = typeof(Interface);
List<MethodInfo> interfaceMethodInfos = new List<MethodInfo>();
List<string> returnInfos = new List<string>();
StringBuilder callbuilder = new StringBuilder();
foreach (var property in interfaceType.GetProperties())
{
AddMethodInfoIfValid(interfaceMethodInfos, property.GetGetMethod());
AddMethodInfoIfValid(interfaceMethodInfos, property.GetSetMethod());
}
foreach (var method in interfaceType.GetMethods())
{
AddMethodInfoIfValid(interfaceMethodInfos, method);
}
foreach (var methodinfo in interfaceMethodInfos)
{
int paramcount = methodinfo.GetParameters().Length;
object[] args = new object[paramcount];
Action<Interface> lambdacall = (i) => methodinfo.Invoke(i, args);
var calls = rhinomock.GetArgumentsForCallsMadeOn(lambdacall);
foreach (var call in calls)
{
bool more = false;
callbuilder.Clear().Append(interfaceType.Name).Append('.').Append(methodinfo.Name).Append('(');
foreach (var parameter in call)
{
if (more) { callbuilder.Append(", "); }
if (null == parameter) { callbuilder.Append("<null>"); }
else {
callbuilder
.Append('(').Append(parameter.GetType().Name).Append(")'")
.Append(parameter.ToString()).Append("'");
}
more = true;
}
callbuilder.Append(')');
string callInfo = callbuilder.ToString();
returnInfos.Add(callInfo);
}
}
return returnInfos;
}
private static void AddMethodInfoIfValid(List<MethodInfo> interfaceMethodInfos, MethodInfo methodinfo)
{
if (null != methodinfo)
{
interfaceMethodInfos.Add(methodinfo);
}
}
}
}
I'm sure its very straightforward but I am struggling to figure out how to write an array to file using CSVHelper.
I have a class for example
public class Test
{
public Test()
{
data = new float[]{0,1,2,3,4};
}
public float[] data{get;set;}
}
i would like the data to be written with each array value in a separate cell. I have a custom converter below which is instead providing one cell with all the values in it.
What am I doing wrong?
public class DataArrayConverter<T> : ITypeConverter
{
public string ConvertToString(TypeConverterOptions options, object value)
{
var data = (T[])value;
var s = string.Join(",", data);
}
public object ConvertFromString(TypeConverterOptions options, string text)
{
throw new NotImplementedException();
}
public bool CanConvertFrom(Type type)
{
return type == typeof(string);
}
public bool CanConvertTo(Type type)
{
return type == typeof(string);
}
}
To further detail the answer from Josh Close, here what you need to do to write any IEnumerable (including arrays and generic lists) in a recent version (anything above 3.0) of CsvHelper!
Here the class under test:
public class Test
{
public int[] Data { get; set; }
public Test()
{
Data = new int[] { 0, 1, 2, 3, 4 };
}
}
And a method to show how this can be saved:
static void Main()
{
using (var writer = new StreamWriter("db.csv"))
using (var csv = new CsvWriter(writer))
{
var list = new List<Test>
{
new Test()
};
csv.Configuration.HasHeaderRecord = false;
csv.WriteRecords(list);
writer.Flush();
}
}
The important configuration here is csv.Configuration.HasHeaderRecord = false;. Only with this configuration you will be able to see the data in the csv file.
Further details can be found in the related unit test cases from CsvHelper.
In case you are looking for a solution to store properties of type IEnumerable with different amounts of elements, the following example might be of any help:
using CsvHelper;
using CsvHelper.Configuration;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace CsvHelperSpike
{
class Program
{
static void Main(string[] args)
{
using (var writer = new StreamWriter("db.csv"))
using (var csv = new CsvWriter(writer))
{
csv.Configuration.Delimiter = ";";
var list = new List<AnotherTest>
{
new AnotherTest("Before String") { Tags = new List<string> { "One", "Two", "Three" }, After="After String" },
new AnotherTest("This is still before") {After="after again", Tags=new List<string>{ "Six", "seven","eight", "nine"} }
};
csv.Configuration.RegisterClassMap<TestIndexMap>();
csv.WriteRecords(list);
writer.Flush();
}
using(var reader = new StreamReader("db.csv"))
using(var csv = new CsvReader(reader))
{
csv.Configuration.IncludePrivateMembers = true;
csv.Configuration.RegisterClassMap<TestIndexMap>();
var result = csv.GetRecords<AnotherTest>().ToList();
}
}
private class AnotherTest
{
public string Before { get; private set; }
public string After { get; set; }
public List<string> Tags { get; set; }
public AnotherTest() { }
public AnotherTest(string before)
{
this.Before = before;
}
}
private sealed class TestIndexMap : ClassMap<AnotherTest>
{
public TestIndexMap()
{
Map(m => m.Before).Index(0);
Map(m => m.After).Index(1);
Map(m => m.Tags).Index(2);
}
}
}
}
By using the ClassMap it is possible to enable HasHeaderRecord (the default) again. It is important to note here, that this solution will only work, if the collection with different amounts of elements is the last property. Otherwise the collection needs to have a fixed amount of elements and the ClassMap needs to be adapted accordingly.
This example also shows how to handle properties with a private set. For this to work it is important to use the csv.Configuration.IncludePrivateMembers = true; configuration and have a default constructor on your class.
Unfortunately, it doesn't work like that. Since you are returning , in the converter, it will quote the field, as that is a part of a single field.
Currently the only way to accomplish what you want is to write manually, which isn't too horrible.
foreach( var test in list )
{
foreach( var item in test.Data )
{
csvWriter.WriteField( item );
}
csvWriter.NextRecord();
}
Update
Version 3 has support for reading and writing IEnumerable properties.