System.ObjectDisposedException: 'Cannot read from a closed TextReader.' - c#

For my homework I have to make a XAML project using a csv file to show pictures of zoo animals along with other info which is included in the code.
It's almost done but when I run the program it gives me this error:
System.ObjectDisposedException: 'Cannot read from a closed TextReader.'
private async void Button_Click(object sender, RoutedEventArgs e)
{
var picker = new Windows.Storage.Pickers.FileOpenPicker();
picker.FileTypeFilter.Add(".csv");
var file = await picker.PickSingleFileAsync();
if (file == null)
{
tbFileStatus.Text = "(not a valid file chosen)";
return;
}
tbFileStatus.Text = file.Path;
using (var fileAccess = await file.OpenReadAsync())
{
using (var stream = fileAccess.AsStreamForRead())
{
using (var reader = new StreamReader(stream))
{
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
var lvAnimals = csv.GetRecords<Animal>();
}
while (!reader.EndOfStream)
{
var row = reader.ReadLine();
var parts = row.Split(',');
var number = (parts[0]);
var name = parts[1];
var species = parts[2];
var latin_name = parts[3];
var cage = parts[4];
var picture = parts[5];
var llvAnimals = new Animal
{
Number = number,
Name = name,
Species = species,
Latin_Name = latin_name,
Cage = cage,
Picture = picture
};
lvAnimals.ItemsSource = llvAnimals;
}
}
}
}
}

You are passing your StreamReader to the CsvReader.
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
...
}
while (!reader.EndOfStream)
...
By making use of using, you are disposing the CsvReader after its usage. Keep in mind, that some implementations like this also disposing their readers. This can be seen in the corresponding implementation. This means, the passed StreamReader gets also closed and can't be used afterwards. This causes the System.ObjectDisposedException.
To solve this, extend the usage scope like this:
using (var reader = new StreamReader(stream))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
...
while (!reader.EndOfStream)
...
}

Related

how to access records in csv helper

The code below reads a column of a csv file.
It does that properly.
I want to copy var records to a decimal array.
I am using csv helper.
How best to do it?
using (var reader = new StreamReader(filename))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
var records = new List<Filevalues>();
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
var record = new Filevalues
{
File_vals = csv.GetField<decimal>("File_vals"),
};
records.Add(record);
}
}
public class Filevalues
{
public decimal File_vals{ get; set; }
}
At its most simple:
using var csv = new CsvReader(new StreamReader(filename), CultureInfo.InvariantCulture));
csv.GetRecords<Filevalues>().Select(f => f.File_vals).ToArray();
(I think I'd ToList() it instead of array, and work with the List)
If the CSV is so simple that it's just a list of decimals, I might skip using a CSV library all together:
File.ReadLines(path).Skip(1).Select(decimal.Parse).ToArray();
Either:
using (var reader = new StreamReader(filename))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
var listOfDecimals = new List<decimal>();
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
listOfDecimals.Add(csv.GetField<decimal>("File_vals"));
}
var arrayOfDecimals = listOfDecimals.ToArray();
}
ToArray: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.toarray
Or:
using (var reader = new StreamReader(filename))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
var records = new List<Filevalues>();
csv.Read();
csv.ReadHeader();
while (csv.Read())
{
var record = new Filevalues
{
File_vals = csv.GetField<decimal>("File_vals"),
};
records.Add(record);
}
var arrayOfDecimals = records.Select(x => x.File_vals).ToArray();
}
public class Filevalues
{
public decimal File_vals{ get; set; }
}
LINQ Projection: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/linq/projection-operations

csvhelper: how to write a specific "cell" on an existing csv on C#?

I have a customer list in csv format which I'm using to send out emails. I would like to write to the CSV after each row has been executed in order to place a conditional rule. I'm using csvhelper to manipulate the file. Here's the code:
var scan = new StreamReader(myBlob);
var csvv = new CsvReader(scan, CultureInfo.InvariantCulture);
var records = csvv.GetRecords<Records>().ToList();
var scanwriter = new StreamWriter(myBlob4);
var csvwriter = new CsvWriter(scanwriter, CultureInfo.InvariantCulture);
foreach (Records record in records)
{
var from = new EmailAddress("example.com", "John");
var to = new EmailAddress(record.Email, record.Name);
var subject = "exapmple";
var msg = MailHelper.CreateSingleEmail(from, to, subject, txtf, htmlf);
StringBuilder text = new StringBuilder();
text.AppendFormat("sent", record.EmailSent);
csvwriter.WriteField(record.EmailSent);
csvwriter.NextRecord();
var response = await client.SendEmailAsync(msg);
}
However my csv is not appending the "sent" value to the file under the emailsent column. I'm using StringBuilder which might not be helpful in this scenario.
It seems like you are trying to do something more like this.
void Main()
{
var records = new List<SendEmail>
{
new SendEmail{ Email = "example.com", Name = "John" },
new SendEmail{ Email = "example2.com", Name = "Jenny" }
};
var csvwriter = new CsvWriter(Console.Out, CultureInfo.InvariantCulture);
foreach (var record in records)
{
// var from = new EmailAddress("example.com", "John");
// var to = new EmailAddress(record.Email, record.Name);
//
// var subject = "exapmple";
//
// var msg = MailHelper.CreateSingleEmail(from, to, subject, txtf, htmlf);
record.EmailSent = "sent";
csvwriter.WriteRecord(record);
csvwriter.NextRecord();
//var response = await client.SendEmailAsync(msg);
}
}
public class SendEmail
{
public string Email { get; set; }
public string Name { get; set; }
public string EmailSent { get; set; }
}
//using blocks will make sure the streams and disposed and file handles are closed properly,
// **even if an exception is thrown **
using(var scan = new StreamReader(myBlob))
using (var csvv = new CsvReader(scan, CultureInfo.InvariantCulture))
using (var scanwriter = new StreamWriter(myBlob4))
using (var csvwriter = new CsvWriter(scanwriter, CultureInfo.InvariantCulture))
{
var records = csvv.GetRecords<Records>(); //ToList() was not needed or helpful here
foreach (var record in records)
{
var from = new EmailAddress("example.com", "John");
var to = new EmailAddress(record.Email, record.Name);
var subject = "example";
var msg = MailHelper.CreateSingleEmail(from, to, subject, txtf, htmlf);
csvwriter.WriteField($"sent {record.EmailSent}");
csvwriter.NextRecord();
var response = await client.SendEmailAsync(msg);
}
}

Reading a CSV file using CsvHelper

I'm a newbie. I want to get data from the CSV file-the Id and Name fields, but when I run the reading method, I get only 100 lines of an incomprehensible type: "CsvHelper.CsvReaderd__87`1[Program+Product]". I do not know how to get data from CSV, I also cannot understand where the error is.
Although the documentation says that having the same names of properties and CSV headers, you do not need to write additional configurations. However, I get the result specified above. The CSV names match the classes. Link to the documentation:https://joshclose.github.io/CsvHelper/getting-started/
reading method:
{
using (var reader = new StreamReader("C:\\Users\\Saint\\Desktop\\TaskRetail\\file.csv", Encoding.UTF8))
using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
{
var records = csv.GetRecords<Product>();
Console.WriteLine($"{records}");
}
}
CSV is created without problems, there are two columns with Id and Name with filled rows, there are 100 rows in total:
method for creating a csv with the Id and Name fields:
using (var writer = new StreamWriter("C:\\Users\\Saint\\Desktop\\TaskRetail\\file.csv", false, Encoding.UTF8))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords(products);
}
the entire code:
using CsvHelper;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
using System.Xml;
public class Program
{
public class Product
{
public int Id { get; set; }
public string Name { get; set; }
public Product(int id, string name)
{
Id = id;
Name = name;
}
}
public const string PathToDoc = "C:/Users/Saint/Desktop/TaskRetail/yml.xml";
public static void Main(string[] args)
{
string url = "https://www.googleapis.com/drive/v3/files/1sSR9kWifwjIP5qFWcyxGCxN0-MoEd_oo?alt=media&key=AIzaSyBsW_sj1GCItGBK0vl8hr9zu1I1vTI1Meo";
string savePath = #"C:\Users\Saint\Desktop\TaskRetail\yml.xml";
WebClient client = new WebClient();
client.DownloadFile(url, savePath);
Research();
}
public static void Research()
{
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
var document = new XmlDocument();
document.Load(PathToDoc);
var xmlDoc = document.SelectNodes("/yml_catalog/shop/offers/offer");
var count = xmlDoc.Count;
var products = new List<Product>();
Console.WriteLine($"Offers count: {count}");
for (var i = 0; i < count; i++)
{
var element = xmlDoc.Item(i);
var id = int.Parse(element.Attributes.GetNamedItem("id").Value);
var name = element.SelectSingleNode("name").InnerText;
var product = new Product(id, name);
//Console.WriteLine($"Id: {id}, name: {name}");
products.Add(product);
using (var writer = new StreamWriter("C:\\Users\\Saint\\Desktop\\TaskRetail\\file.csv", false, Encoding.UTF8))
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
{
csv.WriteRecords(products);
}
var config = new CsvConfiguration(CultureInfo.InvariantCulture) { Delimiter = ",", PrepareHeaderForMatch = header => header.Header.ToLower() };
using (var reader = new StreamReader("C:\\Users\\Saint\\Desktop\\TaskRetail\\file.csv", Encoding.UTF8))
using (var csv = new CsvReader(reader, config))
{
var records = csv.GetRecords<Product>();
foreach (var record in records)
{
Console.WriteLine($"{record.Id} {record.Name}");
}
}
}
}
}
Because GetRecords() does return an object of type IEnumerable,
you have to iterate over your records to print each one of them:
foreach(var record in records)
{
Console.WriteLine($"{record.Id} {record.Name}");
}
Furthermore you have to access each property you want to print individually.
Another option would be to override the ToString() method in your Product class.
EDIT
The initial problem wasn't the correct printing of the values but the parsing of the file as I learned from this comment:
CsvHelper.HeaderValidationException: 'Header with name 'id'[0] was not found. Header with name 'name'[0] was not found.
To tackle this problem one have to make sure that the delimiter character is set correctly. This can be enforced in the config object of the CsvHelper. Furthermore to avoid casing errors the configuration can be set to ignore the casing of the headers:
var config = new CsvConfiguration(CultureInfo.InvariantCulture)
{
Delimiter = ",", // Enforce ',' as delimiter
PrepareHeaderForMatch = header => header.Header.ToLower() // Ignore casing
};
using (var csv = new CsvReader(reader, config))
{
...
}

amazonS3client.SelectObjectContentAsync - downloading the large jsonline formate file - unwanted line break

I am trying to download a file content from the S3 bucket using the SelectObjectContentAsync method from AWSSDK for C#.
But there are some unwanted line break(\n) in mid of the raw data.
Data Example :
{"Id":1,"Name":"aaa"}, {"Id":2,"N
\name":"bbb"}
My Code :
var amazonS3Client = new AmazonS3Client(awsAccessKeyId, awsSecretAccessKey, region);
SelectObjectContentRequest selectObjectContentRequest = new SelectObjectContentRequest()
{
Bucket = bucketName,
Key = key,
ExpressionType = ExpressionType.SQL,
Expression = query,
InputSerialization = new InputSerialization()
{
JSON = new JSONInput()
{
JsonType = JsonType.Lines
},
CompressionType = CompressionType.Gzip
},
OutputSerialization = new OutputSerialization()
{
JSON = new JSONOutput()
{
RecordDelimiter = ","
}
}
};
using (var content = amazonS3Client.SelectObjectContentAsync(selectObjectContentRequest).Result.Payload)
{
foreach (var item in content)
{
if (item is RecordsEvent recordsEvent)
{
using (var reader = new StreamReader(recordsEvent.Payload, Encoding.UTF8))
{
using (var file = new StreamWriter(path, true))
{
file.WriteLine(reader.ReadToEnd());
}
}
}
}
}

C# TFS SDK Get Items from changeset

I want to get the Content from an item from a specific Changeset. With my code, I get the correct number of items, but every property of the item is empty, only the correct URL is filled up. All other properties are set to null.
How can I solve this?
string collectionUri = #"https://tfs.myServer.de/MyProject";
using (TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(new Uri(collectionUri)))
{
TfvcHttpClient tfvcClient = tpc.GetClient<TfvcHttpClient>();
var changedItems = tfvcClient.GetChangesetAsync(125453).Result;
IEnumerable<TfvcChange> changesetChanges=tfvcClient.GetChangesetChangesAsync(changedItems.ChangesetId).Result;
foreach (var itemsChange in changesetChanges)
{
Console.WriteLine(itemsChange.NewContent.Content);
}
}
This gets you the contents of the items of a changeset
private static async Task ReadContent(TfvcHttpClient tfvcClient)
{
var changesetId = 123456;
var changesetChanges = await tfvcClient.GetChangesetChangesAsync(changesetId);
var tfvcVersionDescriptor = new TfvcVersionDescriptor(null, TfvcVersionType.Changeset, changesetId.ToString());
foreach (var changesetChange in changesetChanges)
{
var path = changesetChange.Item.Path;
Stream contentStream = await tfvcClient.GetItemContentAsync(path, versionDescriptor: tfvcVersionDescriptor);
using (StreamReader streamReader = new StreamReader(contentStream))
{
var content = streamReader.ReadToEnd();
}
}
}

Categories

Resources