How to read an enumerable without a loop without losing its scope? - c#

I'm facing a pretty weird construct. The Foo type returned in an IEnumerable loses its data as soon as the enumeration ends. This means that I can't do a enumeration.First() because the data would be lost right away.
A loop over it works, but since I know it will contain only a single element that would be weird.
int Test(out int something)
IEnumerable<Foo> enumeration = ...
for (var foo in enumeration) {
something = foo.GetSomething ();
return foo.GetAnInt ();
}
something = 42;
return 0;
}
Another way I though of is abusing a Linq Select, but that's just as horrible.
Is there a way to work around this limitation? Fixing the root cause is obviously superior, but difficult in this case.
Edit: It's an IEnumerable<IDataRecord> that is yield returned from a transactioned SQL data reader.
public IEnumerable<IDataRecord> ExecuteReader (SqlCommand cmd)
{
using (var con = GetConnection()) {
con.Open ();
using (var tr = con.BeginTransaction ()) {
cmd.Connection = con;
var reader = cmd.ExecuteReader ();
while (reader.Read ()) {
yield return reader;
}
tr.Commit ();
}
}
}

The problem is that your ExecuteReader method does simply return the SqlDataReader itself (which implements IDataRecord), instead of returning a block of data. So when you do this:
var list = ExecuteReader(...).ToList();
In that case all elements of the list will be the same SqlDataReader instance, but after the ToList has been executed, the reader has been closed. I'm a bit surprised that you don't get an ObjectDisposedException.
For this to work, you need to return a copy of the data in the IDataRecord. You think you can iterate the elements in the data record. An other option is to change the ExecuteReader to the following:
public IEnumerable<T> ExecuteReader<T>(SqlCommand cmd,
Func<IDataRecord, T> recordCreator)
{
using (var con = GetConnection()) {
con.Open ();
using (var tr = con.BeginTransaction()) {
cmd.Connection = con;
var reader = cmd.ExecuteReader();
while (reader.Read()) {
yield return recordCreator(reader);
}
tr.Commit();
}
}
}
This way you can do the following:
var list = ExecuteReader(command, record => new
{
Item1 = record.GetInt("id"),
Item2 = record.GetString("name")
});
Note: I'm not sure why you need a transaction for this anyway.

How about
int Test(out int something)
{
IEnumerable<Foo> enumeration = ...
var values = enumeration
.Select(foo => new
{
something = foo.GetSomething(),
anInt = foo.GetAnInt()
})
.FirstOrDefault();
if (values != null)
{
something = values.something;
return values.anInt;
}
else
{
something = 42;
return 0;
}
}
GetSomething and GetAnInt are called while inside the enumeration.

Another idea could be to convert the result type of the method from IEnumerable to IEnumerator. That way, scope control is much easier, and returning single results does not require any (fake) loop, too.
Edit: I think I found a way to refactor the whole issue. This circumvents the initial problem by using new disposable class that contains the logic formerly found in the method. It's very readable and less code even.
public TransactedConnection GetConnection (string text)
{
return new TransactedConnection (_ConnectionString, text);
}
public class TransactedConnection : IDisposable
{
private readonly SQLiteCommand _Cmd;
private readonly SQLiteConnection _Con;
private readonly SQLiteTransaction _Tr;
public TransactedConnection (string connection, string text)
{
_Cmd = new SQLiteCommand (text);
_Con = new SQLiteConnection (connection);
_Con.Open ();
_Cmd.Connection = _Con;
_Tr = _Con.BeginTransaction ();
}
public void Dispose ()
{
_Tr.Commit ();
_Tr.Dispose ();
_Con.Dispose ();
_Cmd.Dispose ();
}
public SQLiteParameterCollection Parameters
{
get
{
return _Cmd.Parameters;
}
}
public int ExecuteNonQuery ()
{
return _Cmd.ExecuteNonQuery ();
}
public object ExecuteScalar ()
{
return _Cmd.ExecuteScalar ();
}
public SQLiteDataReader ExecuteReader ()
{
return _Cmd.ExecuteReader ();
}
}
public void Test (string match)
{
var text = "SELECT * FROM Data WHERE foo=#bar;";
using (var cmd = GetConnection (text)) {
cmd.Parameters.Add ("#bar", DbType.String).Value = match;
using (var reader = cmd.ExecuteReader ()) {
while (reader.Read ()) {
Console.WriteLine (reader["foo"]);
}
}
}
}

Related

Closing MySql datareader connection

So this is a little bit code-ceptionlike.
I have a function that is checking the last ID in a table, this function is called within another function. At the end of that function, I have another function that's opening another datareader.
Error:
There is already an open Datareader associated with this connection which must be closed first.
getLastIdfromDB()
public string getLastIdFromDB()
{
int lastIndex;
string lastID ="";
var dbCon = DB_connect.Instance();
if (dbCon.IsConnect())
{
MySqlCommand cmd2 = new MySqlCommand("SELECT ID FROM `competitor`", dbCon.Connection);
try
{
MySqlDataReader reader = cmd2.ExecuteReader();
while (reader.Read())
{
string item = reader2["ID"].ToString();
lastIndex = int.Parse(item);
lastIndex++;
lastID = lastIndex.ToString();
}
}
catch (Exception ex)
{
MessageBox.Show("Error:" + ex.Message);
}
}
return lastID;
}
This function is later-on used in this function:
private void addPlayerBtn_Click(object sender, EventArgs e)
{
ListViewItem lvi = new ListViewItem(getLastIdFromDB());
.........................................^
... HERE
...
... irrelevant code removed
.........................................
var dbCon = DB_connect.Instance();
if (dbCon.IsConnect())
{
MySqlCommand cmd = new MySqlCommand("INSERT INTO `competitor`(`ID`, `Name`, `Age`) VALUES(#idSql,#NameSql,#AgeSql)", dbCon.Connection);
cmd.Parameters.AddWithValue("#idSql", getLastIdFromDB());
cmd.Parameters.AddWithValue("#NameSql", playerName.Text);
cmd.Parameters.AddWithValue("#AgeSql", playerAge.Text);
try
{
cmd.ExecuteNonQuery();
listView1.Items.Clear();
}
catch (Exception ex)
{
MessageBox.Show("Error:" + ex.Message);
dbCon.Connection.Close();
}
finally
{
updateListView();
}
}
}
What would be the best way for me to solve this problem and in the future be sure to close my connections properly?
UPDATE: (per request, included DB_connect)
class DB_connect
{
private DB_connect()
{
}
private string databaseName = "simhopp";
public string DatabaseName
{
get { return databaseName; }
set { databaseName = value; }
}
public string Password { get; set; }
private MySqlConnection connection = null;
public MySqlConnection Connection
{
get { return connection; }
}
private static DB_connect _instance = null;
public static DB_connect Instance()
{
if (_instance == null)
_instance = new DB_connect();
return _instance;
}
public bool IsConnect()
{
bool result = true;
try
{
if (Connection == null)
{
if (String.IsNullOrEmpty(databaseName))
result = false;
string connstring = string.Format("Server=localhost; database={0}; UID=root;", databaseName);
connection = new MySqlConnection(connstring);
connection.Open();
result = true;
}
}
catch (Exception ex)
{
Console.Write("Error: " + ex.Message);
}
return result;
}
public void Close()
{
connection.Close();
}
}
}
You are trying to have multiple open readers on the same connection. This is commonly called "MARS" (multiple active result sets). MySql seems to have no support for it.
You will have to either limit yourself to one open reader at a time, or use more than one connection, so you can have one connection for each reader.
My suggestion would be to throw away that singleton-like thingy and instead use connection pooling and proper using blocks.
As suggested by Pikoh in the comments, using the using clause indeed solved it for me.
Working code-snippet:
getLastIdFromDB
using (MySqlDataReader reader2 = cmd2.ExecuteReader()) {
while (reader2.Read())
{
string item = reader2["ID"].ToString();
lastIndex = int.Parse(item);
lastIndex++;
lastID = lastIndex.ToString();
}
}
Your connection handling here is not good. You need to ditch the DB_connect. No need to maintain a single connection - just open and close the connection each time you need it. Under the covers, ADO.NET will "pool" the connection for you, so that you don't actually have to wait to reconnect.
For any object that implements IDisposable you need to either call .Dispose() on it in a finally block, or wrap it in a using statement. That ensures your resources are properly disposed of. I recommend the using statement, because it helps keep the scope clear.
Your naming conventions should conform to C# standards. Methods that return a boolean should be like IsConnected, not IsConnect. addPlayerBtn_Click should be AddPlayerButton_Click. getLastIdFromDB should be GetlastIdFromDb or getLastIdFromDatabase.
public string GetLastIdFromDatabase()
{
int lastIndex;
string lastID ="";
using (var connection = new MySqlConnection(Configuration.ConnectionString))
using (var command = new MySqlCommand("query", connection))
{
connection.Open();
MySqlDataReader reader = cmd2.ExecuteReader();
while (reader.Read())
{
string item = reader2["ID"].ToString();
lastIndex = int.Parse(item);
lastIndex++;
lastID = lastIndex.ToString();
}
}
return lastID;
}
Note, your query is bad too. I suspect you're using a string data type instead of a number, even though your ID's are number based. You should switch your column to a number data type, then select the max() number. Or use an autoincrementing column or sequence to get the next ID. Reading every single row to determine the next ID and incrementing a counter not good.

Designing an async data provider with BeginExecuteReader in C# [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I am new to the async/await model in C# 5 so you will have to be patient with my ignorance/lack of understanding.
I would like to upgrade my Singleton designed data provider to perform an asynchronous call to a stored procedure then return the data using BeginDataReader and EndDataReader methods in the System.Data namespace.
Here is an example of the structure that I am trying to build but it is not waiting for the data to return:
public class DataProvider{
private static DataProvider instance;
public static DataProvider Instance
{
get
{
if (instance == null)
{
lock (typeof(DataProvider))
{
instance = new DataProvider();
}
}
return instance;
}
}
public virtual async void ExecuteDataReaderAsync(string StoredProcedureName, AsyncCallback callback, params object[] Parameters)
{
InitDatabase();
var connection = new SqlConnection(databaseControllers[connectionStringName].ConnectionString);
var cmd = new SqlCommand();
cmd.Connection = connection;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = dbPrefixName + StoredProcedureName;
await connection.OpenAsync();
SqlCommandBuilder.DeriveParameters(cmd);
if (cmd.Parameters.Count - 1 > Parameters.Count())
throw new InvalidOperationException("The number of parameters provided does not match the number of parameters in the stored procedure. There are " + Parameters.Count().ToString() + " parameters provided, however the stored procedure requires " + cmd.Parameters.Count.ToString() + " parameters.");
for (int i = 0; i < Parameters.Count(); i++)
{
cmd.Parameters[i + 1].Value = Parameters[i];
}
cmd.BeginExecuteReader(new AsyncCallback(callback), cmd);
}
}
public class SubDataProvider : DataProvider
{
private static volatile SubDataProvider instance = new SubDataProvider();
public static SubDataProvider Instance
{
get
{
if (instance == null)
{
lock (typeof(SubDataProvider))
{
if (instance == null)
instance = new SubDataProvider();
}
}
return instance;
}
}
////
//// THIS IS WHERE I GET LOST
////
public async Task<List<Models.MyData>> GetDataAsync(bool IncludeDeleted = false)
{
List<Models.MyData> temp = new List<MyData>();
ExecuteDataReaderAsync("GetData", delegate (IAsyncResult result)
{
var database = (SqlCommand)result.AsyncState;
using (IDataReader reader = database.EndExecuteReader(result))
{
while (reader.Read())
{
temp.Add(FillData(reader));
}
}
if (database.Connection.State == ConnectionState.Open)
database.Connection.Close();
}, false);
return temp;
}
}
public class BusinessController
{
private static volatile BusinessController _instance = new BusinessController();
public static BusinessController Instance
{
get
{
if (_instance == null)
{
lock (typeof (BusinessController))
{
_instance = new BusinessController();
}
}
return _instance;
}
}
public async Task<List<Models.MyData>> GetAllAsync(bool IncludeDeleted = false)
{
return await SubDataProvider.Instance.GetDataAsync(IncludeDeleted);;
}
}
// DEMO
internal class Program
{
private static void Main(string[] args)
{
var x = BusinessController.Instance.GetAllAsync(false);
}
}
My ultimate goal is to get this data back to an asynchronous WebApi but I currently am stuck at not getting any data back. The temp variable gets filled but it never actually returns the filled object.
Where am I going wrong?
Thank you so much in advance!
I don't have your exact codebase here but something like this would be close to what you need:
Inside class DataProvider I renamed ExecuteDataReaderAsync to GetDataReaderAsync
public virtual async Task<IDataReader> GetReaderAsync(string StoredProcedureName, params object[] Parameters)
{
InitDatabase();
var connection = new SqlConnection(databaseControllers[connectionStringName].ConnectionString);
var cmd = new SqlCommand
{
Connection = connection,
CommandType = CommandType.StoredProcedure,
CommandText = dbPrefixName + StoredProcedureName
};
await connection.OpenAsync();
SqlCommandBuilder.DeriveParameters(cmd);
if (cmd.Parameters.Count - 1 > Parameters.Count())
throw new InvalidOperationException("The number of parameters provided does not match the number of parameters in the stored procedure. There are " + Parameters.Count().ToString() + " parameters provided, however the stored procedure requires " + cmd.Parameters.Count.ToString() + " parameters.");
for (int i = 0; i < Parameters.Count(); i++)
{
cmd.Parameters[i + 1].Value = Parameters[i];
}
var reader = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection);
return reader;
}
And used the reader in GetDataAsync inside class SubDataProvider
public async Task<List<MyData>> GetDataAsync(bool IncludeDeleted = false)
{
List<MyData> temp = new List<MyData>();
using (var reader = await GetReaderAsync("GetData"))
{
while (reader.Read())
{
temp.Add(FillData(reader));
}
}
return temp;
}
I can't test the solution with what I have here but if you opt for sending back a SqlDataReader instead of a IDataReader, you'd have a ReadAsync() method at your disposal too. But since you already have an async method for that now, it would be just fine to use reader.Read(). I ditched the callback as it's better to stay with the Task-Asynchronous Paradigm here.
Hope this helps. :)
Update
From #Scott Chamberlain in the comments, one other suggestion would be to keep the reader in DbDataReader level instead of IDataReader so you can still be generic and have access to methods like ReadAsync(). The suggestion deemed worthy, thus added in the answer here.

C# and Reflection don't work twice in a row

I've a problem and I can't figured it out how to solve it.
I've a class for fetching data from a Database, in this class I've a method for a simple select * this method is called
List<T> All<T>(string tableName)
and you have to specify which resource you want to fetch, for example
All<User>("users")
And, aside from the classic SQL Reader and SQL Command, the core of the method is this
public override List<T> All<T>(string resource)
{
List<T> result = new List<T>();
using (MySqlConnection sqlConnection = new MySqlConnection(connectionString))
{
sqlConnection.Open();
try
{
string query = "SELECT * FROM " + resource + " WHERE 1=1";
using (MySqlCommand sqlCommand = new MySqlCommand(query, sqlConnection))
{
lock (locker)
{
MySqlDataReader reader = sqlCommand.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
T model = Activator.CreateInstance<T>();
Dictionary<string, object> _properties = new Dictionary<string, object>();
for (int i = 0; i < reader.FieldCount; i++)
{
string property = reader.GetName(i);
object value = reader.GetValue(i);
_properties.Add(property, value);
}
var type = model.GetType();
var method = type.GetMethod("SetProperties");
var invoked = method.Invoke(model, new object[] { _properties });
result.Add(model);
}
}
reader.Close();
}
}
}
catch (Exception ex)
{
Program.eventLogger.Add(new Event(EventType.Error, "SQL Data Providers", "Exception catched on All", ex));
}
finally
{
sqlConnection.Close();
}
}
return result;
}
Basically, based on the Type from the method header, the method will try to create an new instance of the specific type, later for each field from the query, it will fills all the attributes of the class on a temporaneous list. Once it's done it will try to call the method "SetProperties" which basically set every attributes of the class using reflection.
This is the core of SetProperties, equal for each entity:
public virtual bool SetProperties(Dictionary<string,object> properties)
{
if(this.ValidateData(properties))
{
FillNullableAttributes(properties);
// Iterate trough every key : value pairs in properties
foreach (KeyValuePair<string, object> kvp in properties)
{
if (this.data.Contains(kvp.Key))
{
var property = this.GetType().GetProperty(kvp.Key);
PropertyInfo propertyInfo = this.GetType().GetProperty(kvp.Key);
// Set the current fetched key with the given value if !null
if (kvp.Value != null)
{
Type fetchedType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
object safeConversion = (kvp.Value == null || kvp.Value == DBNull.Value) ? null : Convert.ChangeType(kvp.Value, fetchedType);
if (propertyInfo.CanWrite)
{
propertyInfo.SetValue(this, safeConversion, null);
}
}
}
}
return true;
}
return false;
}
In conclusion the result, which is a list, will be returned and the specific Entity will have its own BindingList filled. The binding list, for each entity is described as follow:
public static BindingList<Seller> items = new BindingList<Seller>();
This code works fine, even if there's a lot of space for improvements I know, but if I called it twice like this:
User.items = new BindingList<User>(provider.All<User>("users"));
User.items = new BindingList<User>(provider.All<User>("users"));
The second list will be filled by empty entities, the counting of the will be correct but they will be empties... and that shouldn't occurs.
The only thing that I figured it out, from the debugging, is that on the second call
var invoked = method.Invoke(model, new object[] { _properties });
invoked is set to false.
The result of:
var invoked = method.Invoke(model, new object[] { _properties });
Is the return value from your SetProperties method, not whether the method was invoked as your question indicates. Your SetProperties method is telling you that it was unable to do its work, debug that and you will find your answer.

What is a proper way to create awaitable function

I have dilemma on how to actually create proper awaitable function. I don't get the entire concept fully, probably due the language barrier :)
A
public async Task<int> InsertASync(string tableName, Dictionary<string, string> data)
{
int r = 0;
using (SQLiteConnection con = NewConnection())
{
await con.OpenAsync();
using (SQLiteCommand com = new SQLiteCommand(Query_Insert(tableName, data), con))
{
r = await com.ExecuteNonQueryAsync();
}
con.Close();
}
return r;
}
or
B
public Task<int> InsertASync(string tableName, Dictionary<string, string> data)
{
return Task<int>.Run(async () =>
{
int r = 0;
using (SQLiteConnection con = NewConnection())
{
await con.OpenAsync();
using (SQLiteCommand com = new SQLiteCommand(Query_Insert(tableName, data), con))
{
r = await com.ExecuteNonQueryAsync();
}
con.Close();
}
return r;
});
}
Reason i'm wondering about it is because the way i create awaitable methods via cheap way.
Example
public void DoHeavyWork() //this will block UI
{
//Do work
}
public Task DoHeavyWorkASync() //this won't
{
return Task.Run(() =>
{
DoHeavyWork();
});
}
You want to go the A route. The compiler will take care of creating the returned task and returning the actual integer, when available.
You can read Asynchronous Programming - Pause and Play with ... for some good in-depth information on what the compiler does internally (how your method is converted into a state machine)

Clean Design to Handle Multiple Result Sets from IDataReader

Maybe SO isn't the place for this, and I apologize if it's not, but I can't help but think there's a better way to do this. This just seems like a gross and not very clean way of checking which result set I'm on and then performing a corresponding action. Anyone have any suggestions? (Please ignore the fact I'm always returning null).
public MemberDto Load(long entityId)
{
using (var cn = new SqlConnection(#"connectionstringstuff"))
{
cn.Open();
using (SqlCommand cm = cn.CreateCommand())
{
cm.CommandText = "Client.[MemberGet]";
cm.CommandType = CommandType.StoredProcedure;
cm.Parameters.AddWithValue("#EntityId", entityId);
using (IDataReader dr = cm.ExecuteReader())
{
var memberModel = new MemberDto();
do
{
while (dr.Read())
{
var sdr = new SafeDataReader(dr);
var firstColumn = sdr.GetName(0);
if (firstColumn.StartsWith("Attribute"))
{
AddAttribute(memberModel, sdr);
}
else if (firstColumn.StartsWith("AlternateId"))
{
AddAlternateId(memberModel, sdr);
}
else
{
memberModel.ClientId = sdr.GetInt64("ClientId");
memberModel.Id = sdr.GetInt64("EntityId");
memberModel.Name = sdr.GetString("EntityName");
}
}
} while (dr.NextResult());
}
}
}
return null;
}
private void AddAttribute(MemberDto model, SafeDataReader reader)
{
model.Attributes.Add(
reader.GetInt32("AttributeTypeId").As<EntityAttributeType>(),
reader.GetString("Value"));
}
private void AddAlternateId(MemberDto model, SafeDataReader reader)
{
model.Attributes.Add(
reader.GetInt32("AlternateIdTypeId").As<EntityAttributeType>(),
reader.GetString("Value"));
}
The way that we approach this is not to loop on the nextresult, but explicitly code it to match the underlying data. It is somewhat more visually obvious what the expected results are with this approach (in my opinion, of course).
Here is an example rewrite, assuming the order is membermodel, attributes, and alternates.
while (dr.Read())
{
var sdr = new SafeDataReader(dr);
memberModel.ClientId = sdr.GetInt64("ClientId");
memberModel.Id = sdr.GetInt64("EntityId");
memberModel.Name = sdr.GetString("EntityName");
}
if (dr.NextResult())
{
while (dr.Read())
{
AddAttribute(memberModel, new SafeDataReader(dr));
}
}
if (dr.NextResult())
{
while (dr.Read())
{
AddAlternateId(memberModel, new SafeDataReader(dr));
}
}
Perhaps all sp that return multiple resultsets should always include first a resultset specifiying which resultsets are returned in which order:
dr = multRsCmd.ExecureReader();
// first rs is always the meta
List<string> resultSetIds = new List<string>();
while (dr.Read())
resultSetIds.Add(dr[0]
foreach (string rsId in resultSetIds)
{
if (!dr.NextResult())
break; // or throw, should not happen
if (rsId == "ClientDataWithAttribute")
{
// code to handle the exact rs layout for ClientDataWithAttribute
//
//
}
else if (rsId == "ClientDataWithAltId")
{
// code to handle the exact rs layout for ClientDataWithAltId
//
//
}
else if (rsId == "ClientData")
{
// code to handle the exact rs layout for ClientData
//
//
}
}
This of course screams out for refactoring:
dr = multRsCmd.ExecureReader();
// first rs is always the meta
List<string> resultSetIds = new List<string>();
while (dr.Read())
resultSetIds.Add(dr[0]
foreach (string rsId in resultSetIds)
{
if (!dr.NextResult())
break; // or throw, should not happen
// pull subclass out of registry of readers
ResultSetReader rsr = ResultSetReaders.Find(rsId);
// subclass cleanly holds the layout-dependent logic
memberModel = rsr.FromDataReader(dr);
}
That looks pretty clean, but you could even add default behavior in the ResultSetReader to drive the DTO property mapping from a config file or other source and only override when you get special conditions. You get the idea.

Categories

Resources