I've been trying to figure this out for the past few days, but I just can't seem to get it work.
So I have a txt file which has this format:
id;könyvcím;szerző;kiadó;kiadási év;
I am using a structs and a list such as this:
public static List<Books> BooksList = new List<Books>();
public struct Books
{
public int id;
public string title;
public string writer;
public string publisher;
public int published_year;
}
And I'm also putting all these into a List based on the struct like this:
StreamReader booksRead = new StreamReader("konyvek.txt", Encoding.UTF8);
booksRead.ReadLine();
while (!booksRead.EndOfStream)
{
string[] split = booksRead.ReadLine().Split(';');
Books inRead = new Books();
inRead.id = Convert.ToInt32(split[0]);
inRead.title = split[1];
inRead.writer = split[2];
inRead.publisher = split[3];
inRead.published_year = Convert.ToInt32(split[4]);
BooksList.Add(inRead);
}
booksRead.Close();
All I want is, for example, to find where the line with ID 2 is, and remove that line from my textfile. I've tried to get the index of the line I want, and remove it like that from my textfile, but it even fails to get the index, I tried using IndexOf, FindIndex and trying to go on a loop. I'm pretty sure my struct is not happy with me for using it like that because I get errors such as this when I run my code:
System.InvalidCastException: 'Unable to cast object of type 'Books' to
type 'System.IConvertible'.'
Here is the way I'm trying to get the index of the line I want to remove
Books item = new Books();
for (int i = 0; i < BooksList.Count; i++)
{
if (Convert.ToInt32(textBox_id_delete.Text) == item.id)
{
RemoveAt = item.id;
}
}
int index = BooksList.FindIndex(x => Convert.ToInt32(x) == RemoveAt);
MessageBox.Show(Convert.ToString(index));
I'm pretty sure I'm approaching this extremely wrong, and I'd accept any kind of help.
You are doing it completely wrong for a number of reasons.
First, how would you do that the way you are doing:
void Main()
{
var filename = #"c:\myFolder\mybooklist.txt";
// read into an enumerable
var books = File.ReadAllLines(filename)
.Select(x => x.Split(';'))
.Select(x => new Book {
Id = int.TryParse(x[0], out int bookId)?bookId:0,
Title = x[1],
Writer = x[2],
Publisher = x[3],
Published_year=int.TryParse(x[4], out int year)?year:0
});
// remove the one with id 2
// and save back
var otherBooks = books.Where(b => b.Id != 2);
File.WriteAllLines(filename, otherBooks.Select(b => $"{b.Id};{b.Title};{b.Writer};{b.Publisher};{b.Published_year}"));
}
public struct Book
{
public int Id;
public string Title;
public string Writer;
public string Publisher;
public int Published_year;
}
And now what is wrong with this.
A text file is not a database but you are trying to use a text file as a database.
With a text file, you are not actually doing any control here, if the ID is unique or not (there might be N books with the ID 2).
(Side matter) You are using C#, but looks like you are coming from another language and not using the naming conventions at all.
IMHO, instead you should simply use a database, an embedded one for example like LiteDb or Sqlite. If you care to see a sample with LiteDb or Sqlite, let me know.
EDIT: I am adding SQLite and LiteDb samples. In either case, you would need to add Sqlite.Data.Sqlite and LiteDB respectively from Nuget and add using statements.
In case of SQLite, please note that you could use Linq adding some drivers. I directly used the ADO.Net commands and didn't use a Book class for mapping.
LiteDB, being a NoSQL database written in C# for C#, can directly use objects and support Linq out of the box.
Samples show only the surface for both.
SQLite sample:
private static readonly string dataFile = #"d:\temp\books.s3db";
void Main()
{
CreateDb(dataFile);
SeedSampleData(dataFile);
// List the current data
Console.WriteLine("Current Data");
Console.WriteLine("".PadRight(100, '='));
ListData(dataFile);
Console.WriteLine("".PadRight(100, '='));
DeleteSampleRow(dataFile);
// List the current data
Console.WriteLine("After deleting");
Console.WriteLine("".PadRight(100, '='));
ListData(dataFile);
Console.WriteLine("".PadRight(100, '='));
}
void DeleteSampleRow(string dbName)
{
string deleteById = "delete from books where id = #id";
string deleteByTitle = "delete from books where Title = #title";
string deleteByWriter = "delete from books where Writer = #writer";
using (SQLiteConnection cn = new SQLiteConnection($"Data Source={dbName}"))
using (SQLiteCommand cmdById = new SQLiteCommand(deleteById, cn))
using (SQLiteCommand cmdByTitle = new SQLiteCommand(deleteByTitle, cn))
using (SQLiteCommand cmdByWriter = new SQLiteCommand(deleteByWriter, cn))
{
cmdById.Parameters.Add("#id", DbType.Int32).Value = 2; // delete the book with id = 2
cmdByTitle.Parameters.Add("#title", DbType.String).Value = $"Sample Title #5"; // delete all books having title "Sample Title #5"
cmdByWriter.Parameters.Add("#writer", DbType.String).Value = $"Sample Writer #3"; // delete all books written by "Sample Writer #3"
cn.Open();
cmdById.ExecuteNonQuery();
cmdByTitle.ExecuteNonQuery();
cmdByWriter.ExecuteNonQuery();
cn.Close();
}
}
void ListData(string dbName)
{
string selectCommand = "select * from books";
using (SQLiteConnection cn = new SQLiteConnection($"Data Source={dbName}"))
using (SQLiteCommand cmd = new SQLiteCommand(selectCommand, cn))
{
cn.Open();
var r = cmd.ExecuteReader();
while (r.Read())
{
Console.WriteLine($"{r["id"]},{r["title"]},{r["writer"]},{r["publisher"]},{r["published_year"]}");
}
cn.Close();
}
}
private void CreateDb(string dbName)
{
if (File.Exists(dbName)) // if it exists, delete and create afresh, just for sampling
{
File.Delete(dbName);
}
string createTable = #"Create Table books (
id int primary key not null,
title varchar(500) not null,
writer varchar(100) not null,
publisher varchar(100) not null,
published_year int not null
)";
using (SQLiteConnection cn = new SQLiteConnection($"Data Source={dbName}"))
using (SQLiteCommand cmd = new SQLiteCommand(createTable, cn))
{
cn.Open();
cmd.ExecuteNonQuery();
cn.Close();
}
}
private void SeedSampleData(string dbName)
{
string insertCommand = #"insert into books
(id, title, writer, publisher, published_year)
values
(#id, #title, #writer, #publisher, #year);";
using (SQLiteConnection cn = new SQLiteConnection($"Data Source={dbName}"))
using (SQLiteCommand cmd = new SQLiteCommand(insertCommand, cn))
{
cmd.Parameters.Add("#id", DbType.Int32);
cmd.Parameters.Add("#title", DbType.String);
cmd.Parameters.Add("#writer", DbType.String);
cmd.Parameters.Add("#publisher", DbType.String);
cmd.Parameters.Add("#year", DbType.Int32);
Random r = new Random();
cn.Open();
int id = 1;
using (SQLiteTransaction transaction = cn.BeginTransaction())
{
cmd.Parameters["#id"].Value = id++;
cmd.Parameters["#title"].Value = $"Around the World in Eighty Days";
cmd.Parameters["#writer"].Value = $"Jules Verne";
cmd.Parameters["#publisher"].Value = $"Le Temps, Pierre-Jules Hetzel";
cmd.Parameters["#year"].Value = 1873;
cmd.ExecuteNonQuery();
cmd.Parameters["#id"].Value = id++;
cmd.Parameters["#title"].Value = $"A Tale of Two Cities";
cmd.Parameters["#writer"].Value = $"Charles Dickens";
cmd.Parameters["#publisher"].Value = $"Chapman & Hall";
cmd.Parameters["#year"].Value = 1859;
cmd.ExecuteNonQuery();
// add dummy 10 more rows
for (int i = 0; i < 10; i++)
{
cmd.Parameters["#id"].Value = id++;
cmd.Parameters["#title"].Value = $"Sample Title #{i}";
cmd.Parameters["#writer"].Value = $"Sample Writer #{r.Next(1, 5)}";
cmd.Parameters["#publisher"].Value = $"Sample Publisher #{i}";
cmd.Parameters["#year"].Value = r.Next(1980, 2022);
cmd.ExecuteNonQuery();
}
transaction.Commit();
}
// databases generally use some indexes
new SQLiteCommand(#"Create Index if not exists ixId on books (id);", cn).ExecuteNonQuery();
new SQLiteCommand(#"Create Index if not exists ixTitle on books (title);", cn).ExecuteNonQuery();
new SQLiteCommand(#"Create Index if not exists ixWriter on books (writer);", cn).ExecuteNonQuery();
new SQLiteCommand(#"Create Index if not exists ixPublisher on books (publisher);", cn).ExecuteNonQuery();
cn.Close();
}
}
LiteDb sample:
private static readonly string dataFile = #"d:\temp\books.litedb";
void Main()
{
//CreateDb(dataFile); // this step is not needed with LiteDB
// instead we just simply delete the datafile if it exists
// for starting afresh
// if it exists, delete and create afresh, just for sampling
// so you can run this same sample over and over if you wish
if (File.Exists(dataFile))
{
File.Delete(dataFile);
}
SeedSampleData(dataFile);
// List the current data
Console.WriteLine("Current Data");
Console.WriteLine("".PadRight(100, '='));
ListData(dataFile);
Console.WriteLine("".PadRight(100, '='));
DeleteSampleRow(dataFile);
// List the current data
Console.WriteLine("After deleting");
Console.WriteLine("".PadRight(100, '='));
ListData(dataFile);
Console.WriteLine("".PadRight(100, '='));
}
void DeleteSampleRow(string dbName)
{
using (var db = new LiteDatabase(dbName))
{
var bookCollection = db.GetCollection<Book>("Books");
// by ID
bookCollection.Delete(2);
// by Title
bookCollection.DeleteMany(c => c.Title == "Sample Title #5");
// by Writer
bookCollection.DeleteMany(c => c.Writer == "Sample Writer #3");
}
}
void ListData(string dbName)
{
using (var db = new LiteDatabase(dbName))
{
var bookCollection = db.GetCollection<Book>("Books");
foreach (var book in bookCollection.FindAll())
{
Console.WriteLine($"{book.Id},{book.Title},{book.Writer},{book.Publisher},{book.Published_year}");
}
}
}
private void SeedSampleData(string dbName)
{
Random r = new Random();
var books = new List<Book> {
new Book {Title="Around the World in Eighty Days",Writer = "Jules Verne",Publisher = "Le Temps, Pierre-Jules Hetzel",Published_year= 1873},
new Book {Title="A Tale of Two Cities",Writer = "Charles Dickens",Publisher = "Chapman & Hall",Published_year= 1859},
};
// add dummy 10 more rows
books.AddRange(Enumerable.Range(0, 10).Select(i => new Book
{
Title = $"Sample Title #{i}",
Writer = $"Sample Writer #{r.Next(1, 5)}",
Publisher = $"Sample Publisher #{i}",
Published_year = r.Next(1980, 2022)
}));
using (var db = new LiteDatabase(dbName))
{
var bookCollection = db.GetCollection<Book>("Books");
bookCollection.InsertBulk(books);
// databases generally use some indexes
// create the same indexes that we created in SQLite sample
bookCollection.EnsureIndex(c => c.Id);
bookCollection.EnsureIndex(c => c.Title);
bookCollection.EnsureIndex(c => c.Writer);
bookCollection.EnsureIndex(c => c.Publisher);
}
}
public class Book
{
public int Id {get;set;}
public string Title {get;set;}
public string Writer {get;set;}
public string Publisher {get;set;}
public int Published_year {get;set;}
}
welcome to SO. I'm going to assume you've got a reason for keeping the data in a text file. As several answers have suggested if you need it in a text file the easiest thing to do is to simply create a new file with the lines you want.
One way to do that is to make use of a interator function to filter the lines. This lets you easily use the .NET File class to do the rest - creating the new file and removing the old if you want to. Often keeping the old file and archiving it can be useful too but anyway, here's a way to filter the lines.
static void Main(string[] _)
{
var filteredLines = FilterOnID(File.ReadAllLines("datafile.txt"), "2");
File.WriteAllLines("updated.datafile.txt", filteredLines);
// rename if necessary
File.Delete("datafile.txt");
File.Move("updated.datafile.txt", "datafile.txt");
}
static IEnumerable<string> FilterOnID(IEnumerable<string> lines, string id)
{
foreach (var line in lines)
{
var fields = line.Split(';');
if (fields.Length != 0 || !string.IsNullOrEmpty(fields[0]))
{
if (id == fields[0])
continue;
}
yield return line;
}
}
To test I added simple file like so:
1;field1;field2;field3
2;field1;field2;field3
3;field1;field2;field3
4;field1;field2;field3
5;field1;field2;field3
6;field1;field2;field3
And after running you get this:
1;field1;field2;field3
3;field1;field2;field3
4;field1;field2;field3
5;field1;field2;field3
6;field1;field2;field3
When you put books into a list from a file, you can search the book for remove from BooksList.
Delete it and save BooksList into a file.
var removeBook = BookList.FirstOrDefault(book => book.id == removeId);
if (removeBook != null)
{
BookList.Remove(removeBook);
}
var booksAsString = BookList.Select(book => $"{book.id};{book.title};{book.writer};{book.publisher};{book.published_year}");
File.WriteAllLines("konyvek.txt", booksAsString, Encoding.UTF8);
I'm trying to write a simple helper function that connects to Oracle database using Oracle.ManagedDataAccess.Core library, and returns the rows.
Returned object should survive when connection to database is closed. Something similar to the following. Not sure what the will be.
public <sometype> GetOracleResults(string connectionString, string cmdText, List<OracleParameter> oracleParameters)
{
<sometype> results = null;
try
{
using (OracleConnection oracleConnection = new OracleConnection(connectionString))
{
oracleConnection.Open();
using (OracleCommand oracleCommand = new OracleCommand(cmdText, oracleConnection))
{
foreach (var param in oracleParameters)
{
oracleCommand.Parameters.Add(param);
}
OracleDataReader oracleDataReader = oracleCommand.ExecuteReader();
if(oracleDataReader.HasRows)
{
results = new <sometype>();
while (oracleDataReader.Read())
{
//loop through the reader and add results
return results;
}
}
}
}
}
catch (Exception)
{
//todo
throw;
}
}
Does it have to be strongly typed? If not, a coworker showed me sticking the data in a List of Dictionary items, E.G.:
var columns = new List<string>();
for (int i = 0; i < myReader.FieldCount; i++)
{
columns.Add(myReader.GetName(i).Trim());
}
while (myReader.Read() && results.Count < 200)
{
Dictionary<string, string> row = new Dictionary<string, string>();
foreach (var column in columns)
{
row.Add(column, Convert.ToString(myReader[column], CultureInfo.InvariantCulture).Trim());
}
results.Add(row);
}
I suppose if you know the schema so that the generic type coming into the method had property names matching the column names, then you could use reflection to match up the column results with the C# object.
How can I optimize the following piece of code?
IList<OrderItem> OrderItemsList = new List<OrderItem>();
while (orderItemsResult.Read())
{
new OrderItem()
{
ItemName = orderItemsResult.GetString("item_name"),
Price = orderItemsResult.GetFloat("price"),
Quantity = orderItemsResult.GetInt32("quantity")
},
}
Although a bit late to reply ,still I shall add my thoughts.
We can do without List<OrderItem> object.
The below code returns IEnumerable<OrderItem>
while (orderItemsResult.Read())
{
yield return new OrderItem()
{
ItemName = orderItemsResult.GetString("item_name"),
Price = orderItemsResult.GetFloat("price"),
Quantity = orderItemsResult.GetInt32("quantity")
};
}
As #crowcoder and #er-sho pointed out in the comments, you need to add the OrderItem to OrderItemsList:
IList<OrderItem> OrderItemsList = new List<OrderItem>();
while (orderItemsResult.Read())
{
var orderItem = new OrderItem()
{
ItemName = orderItemsResult.GetString("item_name"),
Price = orderItemsResult.GetFloat("price"),
Quantity = orderItemsResult.GetInt32("quantity")
};
// add to the list
OrderItemsList.Add(orderItem);
}
As in the accepted answer, you can implement it as an iterator function, but keep in mind that in case orderedItemsResult is an IDisposable such as IDataReader for reading from something like a database (the methods called on it appear that this is the case) to make sure to embed it in a try/finally or using block.
For example:
IEnumerable<OrderItem> GetOrders(string connectionString, string queryString)
{
IDbConnection connection = new MyDbConnection(connectionString);
IDbCommand command = null;
IDbReader orderedItemsResult = null;
try
{
connection.Open();
command = new MyCommand(queryString, connection);
orderedItemsResult = command.ExecuteReader();
while (orderItemsResult.Read())
{
yield return new OrderItem()
{
ItemName = orderItemsResult.GetString("item_name"),
Price = orderItemsResult.GetFloat("price"),
Quantity = orderItemsResult.GetInt32("quantity")
};
}
}
finally
{
orderItemsResult?.Dispose();
command?.Dispose();
connection.Dispose();
}
}
The IEnumerable returned which implements IDisposable as well executes the finally block whenever it is disposed (for example, when a foreach is done with it)
I found some solutions and suggestions on the Internet concering this problem, however, I could not yet manage to apply this to my solution. I wrote a personal MySQL wrapper which features this
public async Task<IEnumerable<DatabaseFieldSet>> QueryParamsAsync(string query, string[] parameter,
string[] value)
{
if (!IsConnected)
await ConnectAsync();
var rows = new List<DatabaseFieldSet>();
using (var cmd = CreateCommand(query))
{
foreach (var str in parameter)
{
foreach (var val in value)
{
//cmd.Parameters.Add(str, MySqlDbType.VarChar).Value = val;
cmd.Parameters.AddWithValue(str, val);
}
}
using (var reader = await cmd.ExecuteReaderAsync())
{
uint currentRowIndex = 0;
while (await reader.ReadAsync())
{
var row = new DatabaseFieldSet(currentRowIndex);
var fields = new List<DatabaseField>();
for (var i = 0; i < reader.FieldCount; ++i)
{
var column = new DatabaseReference((uint) i);
fields.Add(new DatabaseField(reader[i], column, row));
}
row.Fields = fields;
rows.Add(row);
currentRowIndex++;
}
}
}
return rows;
}
I tried to add
cmd.Parameters.Clear();
at different locations, none worked or was throwing fatal error while executing. I hope somebody can tell me where to put this single statement.
You are doing it wrong. It rather should be like below. Your double foreach causing the issue and you are eventually trying to add the same parameter again and so the error. You rather should define a counter variable and access every item in your value array.
int i = 0;
foreach (var str in parameter)
{
cmd.Parameters.AddWithValue(str, val[i]);
i++;
}
I have got problem with this method:
public List<int> menu_wid_w_kat()
{
DataSet1TableAdapters.menu_widac_wszystkoTableAdapter pk = new DataSet1TableAdapters.menu_widac_wszystkoTableAdapter();
List<int> lista = new List<int>();
lista = pk.?
return lista;
}
In dataset1 I have got Fill, GetData (#id) and this is connected with procedure (procedure working correct)
Problem is in this method because I don't know how connect this method with dataset1 (but I don't want to using linq: (lista = (from o in pk.GetData() select o.nazwa).ToList();))
my idea was but does not work but probably you will understand what I want to do it. Connection with method put (id number) and get data list:
lista = pk.GetData(id)
I just want to take data from this procedure which exist in dataset1.
correct but I can`t find method GetIdList()
baza nasza_baza = new baza();
var da = new DataSet1TableAdapters.menu_widac_wszystkoTableAdapter();
List productIDs = da. <- I cant`t find method
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace DataSet1TableAdapters
{
public partial class menu_widac_wszystkoTableAdapter
{
public List<int> GetIdList(int ids)
{
List<int> list = new List<int>();
System.Data.SqlClient.SqlCommand command = this.CommandCollection(ids);
command.CommandTimeout = command.Connection.ConnectionTimeout;
System.Data.ConnectionState previousConnectionState = command.Connection.State;
try
{
if (((command.Connection.State & System.Data.ConnectionState.Open) != System.Data.ConnectionState.Open))
{
command.Connection.Open();
}
using (System.Data.SqlClient.SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
list.Add(reader.GetInt32(0));
}
}
}
finally
{
if ((previousConnectionState == System.Data.ConnectionState.Closed))
{
command.Connection.Close();
}
}
return list;
}
private System.Data.SqlClient.SqlCommand CommandCollection(int p)
{
throw new NotImplementedException();
}
}
}
The fastest approach is to extend the autogenerated TableAdapter in your DataSet with a method that returns your ID's as List<int>.
If you want to extend the functionality you can't change the generated classes in the DatesetName.designer.cs/vb (it will be recreated on any change) but the file without designer in its name(create it if it not exists). Then you have to extend the partial TableAdapter-class(in your case menu_widac_wszystkoTableAdapter).
Have a look at following code to see what i mean, i haven't tested it but i hope you take my point:
namespace YourRootNameSpace.DataSet1TableAdapters {
{
public partial class menu_widac_wszystkoTableAdapter
{
public List<int> GetIdList(int categoryID)
{
List<int> list = new List<int>();
System.Data.SqlClient.SqlCommand command = this.CommandCollection[0];
command.CommandTimeout = command.Connection.ConnectionTimeout;
System.Data.ConnectionState previousConnectionState = command.Connection.State;
try {
if (((command.Connection.State & System.Data.ConnectionState.Open) != System.Data.ConnectionState.Open)) {
command.Connection.Open();
}
command.Parameters[ "#id" ].Value = categoryID;
using (System.Data.SqlClient.SqlDataReader reader = command.ExecuteReader()) {
while (reader.Read()) {
list.Add(reader.GetInt32(0));
}
}
} finally {
if ((previousConnectionState == System.Data.ConnectionState.Closed)) {
command.Connection.Close();
}
}
return list;
}
}
}
Edit: After i've read your edited question i saw that i've misunderstood your requirement a little bit, but my approach should be perfect anyway. Basically you don't want to get a DataTable but a List<int> with your product_ids.
After you've extended the TableAdapter in the abovementioned way:
var da = new DataSet1TableAdapters.menu_widac_wszystkoTableAdapter();
List<int> productIDs = da.GetIdList();