Return Stream from WCF service, using SqlFileStream - c#

I have a WCF service, from which users can request large datafiles (stored in an SQL database with FileStream enabled). These files should be streamed, and not loaded into memory before sending them off.
So I have the following method that should return a stream, which is called by the WCF service, so that it can return the Stream to the client.
public static Stream GetData(string tableName, string columnName, string primaryKeyName, Guid primaryKey)
{
string sqlQuery =
String.Format(
"SELECT {0}.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT() FROM {1} WHERE {2} = #primaryKey", columnName, tableName, primaryKeyName);
SqlFileStream stream;
using (TransactionScope transactionScope = new TransactionScope())
{
byte[] serverTransactionContext;
string serverPath;
using (SqlConnection sqlConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnString"].ToString()))
{
sqlConnection.Open();
using (SqlCommand sqlCommand = new SqlCommand(sqlQuery, sqlConnection))
{
sqlCommand.Parameters.Add("#primaryKey", SqlDbType.UniqueIdentifier).Value = primaryKey;
using (SqlDataReader sqlDataReader = sqlCommand.ExecuteReader())
{
sqlDataReader.Read();
serverPath = sqlDataReader.GetSqlString(0).Value;
serverTransactionContext = sqlDataReader.GetSqlBinary(1).Value;
sqlDataReader.Close();
}
}
}
stream = new SqlFileStream(serverPath, serverTransactionContext, FileAccess.Read);
transactionScope.Complete();
}
return stream;
}
My problem is with the TransactionScope and the SqlConnection. The way I'm doing it right now doesn't work, I get a TransactionAbortedException saying "The transaction has aborted". Can I close the transaction and the connection before returning the Stream? Any help is appreciated, thank you
Edit:
I've created a wrapper for a SqlFileStream, that implements IDisposable so that I can close everything up once the stream is disposed. Seems to be working fine
public class WcfStream : Stream
{
private readonly SqlConnection sqlConnection;
private readonly SqlDataReader sqlDataReader;
private readonly SqlTransaction sqlTransaction;
private readonly SqlFileStream sqlFileStream;
public WcfStream(string connectionString, string columnName, string tableName, string primaryKeyName, Guid primaryKey)
{
string sqlQuery =
String.Format(
"SELECT {0}.PathName(), GET_FILESTREAM_TRANSACTION_CONTEXT() FROM {1} WHERE {2} = #primaryKey",
columnName, tableName, primaryKeyName);
sqlConnection = new SqlConnection(connectionString);
sqlConnection.Open();
sqlTransaction = sqlConnection.BeginTransaction();
using (SqlCommand sqlCommand = new SqlCommand(sqlQuery, sqlConnection, sqlTransaction))
{
sqlCommand.Parameters.Add("#primaryKey", SqlDbType.UniqueIdentifier).Value = primaryKey;
sqlDataReader = sqlCommand.ExecuteReader();
}
sqlDataReader.Read();
string serverPath = sqlDataReader.GetSqlString(0).Value;
byte[] serverTransactionContext = sqlDataReader.GetSqlBinary(1).Value;
sqlFileStream = new SqlFileStream(serverPath, serverTransactionContext, FileAccess.Read);
}
protected override void Dispose(bool disposing)
{
sqlDataReader.Close();
sqlFileStream.Close();
sqlConnection.Close();
}
public override void Flush()
{
sqlFileStream.Flush();
}
public override long Seek(long offset, SeekOrigin origin)
{
return sqlFileStream.Seek(offset, origin);
}
public override void SetLength(long value)
{
sqlFileStream.SetLength(value);
}
public override int Read(byte[] buffer, int offset, int count)
{
return sqlFileStream.Read(buffer, offset, count);
}
public override void Write(byte[] buffer, int offset, int count)
{
sqlFileStream.Write(buffer, offset, count);
}
public override bool CanRead
{
get { return sqlFileStream.CanRead; }
}
public override bool CanSeek
{
get { return sqlFileStream.CanSeek; }
}
public override bool CanWrite
{
get { return sqlFileStream.CanWrite; }
}
public override long Length
{
get { return sqlFileStream.Length; }
}
public override long Position
{
get { return sqlFileStream.Position; }
set { sqlFileStream.Position = value; }
}
}

Normally I might suggest wrapping the stream in a custom stream that closes the transaction when disposed, however IIRC WCF makes no guarantees about which threads do what, but TransactionScope is thread-specific. As such, perhaps the better option is to copy the data into a MemoryStream (if it isn't too big) and return that. The Stream.Copy method in 4.0 should make that a breeze, but remember to rewind the memory-stream before the final return (.Position = 0).
Obviously this will be a big problem if the stream is big, ... but, if the stream is big enough for that to be a concern, then personally I'd be concerned at the fact that it is running in TransactionScope at all, since that has inbuilt time limits, and causes serializable isolation (by default).
A final suggestion would be to use a SqlTransaction, which is then not thread-dependent; you could write a Stream wrapper that sits around the SqlFileStream, and close the reader, transaction and connection (and the wrapped stream) in the Dispose(). WCF will call that (via Close()) after processing the results.

Hmm I might be missing something here, but it seems to me a simpler approach would be to provide the stream to the WCF method and writing to it from there, rather than trying to return a stream which the client reads from?
Here's an example for a WCF method:
public void WriteFileToStream(FetchFileArgs args, Stream outputStream)
{
using (SqlConnection conn = CreateOpenConnection())
using (SqlTransaction tran = conn.BeginTransaction(IsolationLevel.ReadCommitted))
using (SqlCommand cmd = conn.CreateCommand())
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "usp_file";
cmd.Transaction = tran;
cmd.Parameters.Add("#FileId", SqlDbType.NVarChar).Value = args.Id;
using (SqlDataReader reader = cmd.ExecuteReader())
{
if (reader.Read())
{
string path = reader.GetString(3);
byte[] streamContext = reader.GetSqlBytes(4).Buffer;
using (var sqlStream = new SqlFileStream(path, streamContext, FileAccess.Read))
sqlStream.CopyTo(outputStream);
}
}
tran.Commit();
}
}
In my app, the consumer happens to be an ASP.NET application, and the calling code looks like this:
_fileStorageProvider.WriteFileToStream(fileId, Response.OutputStream);

Logically none of the SQL related stuff belongs to Stream wrapper class (WcfStream) especially if you intend to send WcfStream instance to external clients.
What you could’ve done was to have an event that would be triggered once WcfStream is disposed or closed:
public class WcfStream : Stream
{
public Stream SQLStream { get; set; }
public event EventHandler StreamClosedEventHandler;
protected override void Dispose(bool disposing)
{
if (disposing)
{
SQLStream.Dispose();
if (this.StreamClosedEventHandler != null)
{
this.StreamClosedEventHandler(this, new EventArgs());
}
}
base.Dispose(disposing);
}
}
Then in you main code you would hook up an event handler to StreamClosedEventHandler and close all sql-related objects there as such:
...
WcfStream test = new WcfStream();
test.SQLStream = new SqlFileStream(filePath, txContext, FileAccess.Read);
test.StreamClosedEventHandler +=
new EventHandler((sender, args) => DownloadStreamCompleted(sqlDataReader, sqlConnection));
return test;
}
private void DownloadStreamCompleted(SqlDataReader sqlDataReader, SQLConnection sqlConnection)
{
// You might want to commit Transaction here as well
sqlDataReader.Close();
sqlConnection.Close();
}
This looks to be working for me and it keeps Streaming logic separate from SQL-related code.

Related

Separating concerns for NpgsqlConnection

I have an architecture where multiple repository classes implement different queries and commands that are run against the database. I would like to separate the concern of "connecting to the database" + "running queries" and "providing a query to run" + "treating the result". I've written a Connection class that is then passed as a constructor argument to the repositories like such:
public class PostgreSqlConnection
{
private string connectionString;
public PostgreSqlConnection(string connectionString)
{
this.connectionString = connectionString;
}
public async Task<NpgsqlDataReader> ExecuteQueryCommand(NpgsqlCommand command)
{
using NpgsqlConnection connection = new NpgsqlConnection(this.connectionString);
await connection.OpenAsync();
command.Connection = connection;
command.Prepare();
return await command.ExecuteReaderAsync();
}
public async Task ExecuteNonQueryCommand(NpgsqlCommand command)
{
using NpgsqlConnection connection = new NpgsqlConnection(this.connectionString);
await connection.OpenAsync();
command.Connection = connection;
command.Prepare();
await command.ExecuteNonQueryAsync();
}
}
The instantiation would look something like this:
PostgreSqlConnection connection = new PostgreSqlConnection("...connection string");
IRepositoryA repA = new PostgreSqlRepositoryA(connection);
IRepositoryB repB = new PostgreSqlRepositoryB(connection);
Code duplication aside, this doesn't work since in the query case, the connection would be disposed of at the end of the ExecuteQueryCommand method and the reader would stop working.
Removing the using statement would fix this but from what I gather that is not good practice. Writing a Dispose / Disconnect method that I could call in the repositories would be something that would also work but it's not the repository's job to dispose of the connection.
How could I go about keeping the concerns separated and disposing of the items properly?
I think what here can help is a Unit of Work pattern. Basically, with UnitOfWork class you handle the connection, repository instances and transactions if needed (in case you are saving data to DB). You also have a flexibility to open a connection/transaction and then execute multiple commands across many repositories and in the end you either commit or rollback the transaction, or in case of pure reading you just close the connection using IDisposable pattern.
UnitOfWork class will handle the connection part, your BaseRepository (abstract class) will have your generic execute methods and it will handle the execution of query/command. The concrete repositories (A and B in your case) will inherit from BaseRepository, just prepare the commands/queries and call the Execute methods from a BaseRepository. Their responsibility is basically to prepare the query/command and to handle the result from Execute methods.
Please review the code because I don't have Postgres database and I can't test it 100%. I hope this is enough for you to give you a direction and the main idea behind the approach.
Here is the idea:
Implement UnitOfWork where you manange the connection, transaction if needed and repository instances.
public class UnitOfWork : IDisposable
{
private PostgreSqlRepositoryA _postgreSqlRepositoryA;
private PostgreSqlRepositoryB _postgreSqlRepositoryB;
private NpgsqlConnection _sqlConnection;
private NpgsqlTransaction _sqlTransaction;
private bool _disposed;
public UnitOfWork(string connectionString, bool withTransaction)
{
_sqlConnection = new NpgsqlConnection(connectionString);
_sqlConnection.Open();
if (withTransaction)
_sqlTransaction = _sqlConnection.BeginTransaction();
}
public PostgreSqlRepositoryA PostgreSqlRepositoryA
{
get
{
if(_postgreSqlRepositoryA == null)
{
_postgreSqlRepositoryA = new PostgreSqlRepositoryA(_sqlConnection);
}
return _postgreSqlRepositoryA;
}
}
public PostgreSqlRepositoryB PostgreSqlRepositoryB
{
get
{
if (_postgreSqlRepositoryB == null)
{
_postgreSqlRepositoryB = new PostgreSqlRepositoryB(_sqlConnection);
}
return _postgreSqlRepositoryB;
}
}
public void Commit()
{
// hanlde using try-catch
if(_sqlTransaction != null)
{
_sqlTransaction.Commit();
}
}
public void Rollback()
{
if (_sqlTransaction != null)
{
_sqlTransaction.Rollback();
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!this._disposed)
{
if (_sqlTransaction != null)
{
_sqlTransaction.Rollback(); // or throw an Exception for an opened transaction
}
if (_sqlConnection != null)
{
_sqlConnection.Close();
_sqlConnection.Dispose();
}
this._disposed = true;
}
}
}
Then what you need is a BaseRepository which will hold your "generic" methods for query/command execution:
public abstract class BaseRepository
{
protected NpgsqlConnection _sqlConnection;
public BaseRepository(NpgsqlConnection sqlConnection)
{
this._sqlConnection = sqlConnection;
}
public async Task<NpgsqlDataReader> ExecuteQueryCommand(NpgsqlCommand command)
{
command.Connection = _sqlConnection;
command.Prepare();
return await command.ExecuteReaderAsync();
}
public async Task ExecuteNonQueryCommand(NpgsqlCommand command)
{
command.Connection = _sqlConnection;
command.Prepare();
await command.ExecuteNonQueryAsync();
}
}
Your concrete repository implementation will look like this (I didn't bother with handling the reader, you can add that part):
public class PostgreSqlRepositoryB : BaseRepository
{
public PostgreSqlRepositoryB(NpgsqlConnection sqlConnection)
: base(sqlConnection)
{}
public async Task<int> GetCountB()
{
using (var sqlCommand = new NpgsqlCommand())
{
sqlCommand.CommandText = "select count(1) from TableB";
var reader = await ExecuteQueryCommand(sqlCommand);
// TODO: handle reader
}
}
}
And the in the end you will use it from your service or client method like this (if your are just reading from DB then set withTransaction to false, you don't need a transaction in that case):
using(var uow = new UnitOfWork("place_your_conn_string", withTransaction: true))
{
var countB = await uow.PostgreSqlRepositoryB.GetCountB();
uow.PostgreSqlRepositoryB.SaveSomethingToA("123456");
uow.Commit();
}

C#: How to Wrap my Unit of Work code in my repository classes

See my repository code
public abstract class AdoRepository<T> where T : class
{
private static SqlConnection _connection;
public AdoRepository(string connectionString)
{
_connection = new SqlConnection(connectionString);
}
public virtual T PopulateRecord(SqlDataReader reader)
{
return null;
}
public virtual void GetDataCount(int count)
{
}
protected IEnumerable<T> GetRecords(SqlCommand command)
{
var list = new List<T>();
command.Connection = _connection;
_connection.Open();
try
{
var reader = command.ExecuteReader();
try
{
while (reader.Read())
{
list.Add(PopulateRecord(reader));
}
reader.NextResult();
if (reader.HasRows)
{
while (reader.Read())
{
GetDataCount(Convert.ToInt32(reader["Count"].ToString()));
}
}
}
finally
{
// Always call Close when done reading.
reader.Close();
}
}
catch(Exception ex)
{
string Msg = ex.Message;
}
finally
{
_connection.Close();
}
return list;
}
protected T GetRecord(SqlCommand command)
{
T record = null;
command.Connection = _connection;
_connection.Open();
try
{
var reader = command.ExecuteReader();
try
{
while (reader.Read())
{
record = PopulateRecord(reader);
break;
}
}
finally
{
// Always call Close when done reading.
reader.Close();
}
}
finally
{
_connection.Close();
}
return record;
}
protected IEnumerable<T> ExecuteStoredProc(SqlCommand command)
{
var list = new List<T>();
command.Connection = _connection;
command.CommandType = CommandType.StoredProcedure;
_connection.Open();
try
{
var reader = command.ExecuteReader();
try
{
while (reader.Read())
{
var record = PopulateRecord(reader);
if (record != null) list.Add(record);
}
}
finally
{
// Always call Close when done reading.
reader.Close();
}
}
finally
{
_connection.Close();
}
return list;
}
}
public class StudentRepository : AdoRepository<Student>
{
public int DataCounter { get; set; }
public StudentRepository(string connectionString)
: base(connectionString)
{
}
public IEnumerable<Student> GetAll()
{
// DBAs across the country are having strokes
// over this next command!
using (var command = new SqlCommand("SELECT ID, FirstName,LastName,IsActive,StateName,CityName FROM vwListStudents"))
{
return GetRecords(command);
}
}
public Student GetById(string id)
{
// PARAMETERIZED QUERIES!
using (var command = new SqlCommand("SELECT ID, FirstName,LastName,IsActive,StateName,CityName FROM vwListStudents WHERE Id = #id"))
{
command.Parameters.Add(new ObjectParameter("id", id));
return GetRecord(command);
}
}
public IEnumerable<Student> GetStudents(int StartIndex, int EndIndex, string sortCol, string sortOrder)
{
string strSQL = "SELECT * FROM vwListStudents WHERE ID >=" + StartIndex + " AND ID <=" + EndIndex;
strSQL += " ORDER BY " + sortCol + " " + sortOrder;
strSQL += ";SELECT COUNT(*) AS Count FROM vwListStudents";
var command = new SqlCommand(strSQL);
return GetRecords(command);
}
public override Student PopulateRecord(SqlDataReader reader)
{
return new Student
{
ID = Convert.ToInt32(reader["ID"].ToString()),
FirstName = reader["FirstName"].ToString(),
LastName = reader["LastName"].ToString(),
IsActive = Convert.ToBoolean(reader["IsActive"]),
StateName = reader["StateName"].ToString(),
CityName = reader["CityName"].ToString()
};
}
public override void GetDataCount(int count)
{
DataCounter = count;
}
}
this way unit of work code added
public class UnitOfWorkFactory
{
public static IUnitOfWork Create()
{
var connection = new SqlConnection(ConfigurationManager.ConnectionStrings("MyDb").ConnectionString);
connection.Open();
return new AdoNetUnitOfWork(connection, true);
}
}
public class AdoNetUnitOfWork : IUnitOfWork
{
public AdoNetUnitOfWork(IDbConnection connection, bool ownsConnection)
{
_connection = connection;
_ownsConnection=ownsConnection;
_transaction = connection.BeginTransaction();
}
public IDbCommand CreateCommand()
{
var command = _connection.CreateCommand();
command.Transaction = _transaction;
return command;
}
public void SaveChanges()
{
if (_transaction == null)
throw new InvalidOperationException("Transaction have already been commited. Check your transaction handling.");
_transaction.Commit();
_transaction = null;
}
public void Dispose()
{
if (_transaction != null)
{
_transaction.Rollback();
_transaction = null;
}
if (_connection != null && _ownsConnection)
{
_connection.Close();
_connection = null;
}
}
}
using (var uow = UnitOfWorkFactory.Create())
{
var repos = new UserRepository(uow);
uow.SaveChanges();
}
public class UserRepository
{
private AdoNetUnitOfWork _unitOfWork;
public UserRepository(IUnitOfWork uow)
{
if (uow == null)
throw new ArgumentNullException("uow");
_unitOfWork = uow as AdoNetUnitOfWork;
if (_unitOfWork == null)
throw new NotSupportedException("Ohh my, change that UnitOfWorkFactory, will you?");
}
public User Get(Guid id)
{
using (var cmd = _unitOfWork.CreateCommand())
{
cmd.CommandText = "SELECT * FROM Users WHERE Id = #id");
cmd.AddParameter("id", id);
// uses an extension method which I will demonstrate in a
// blog post in a couple of days
return cmd.FirstOrDefault<User>();
}
}
}
but i want to add unit of work code in my repository class. may be in AdoRepository class or StudentRepository class as a result i do not have to write the below code again and again.
using (var uow = UnitOfWorkFactory.Create())
{
var repos = new UserRepository(uow);
uow.SaveChanges();
}
so when i will be working with any repository like UserRepository then i have to repeat again and again this lines of code UnitOfWorkFactory.Create() which i do not want rather i am looking for a way to embed unit of work code in some where centralize as a result i could reduce the repeated code.
so looking for idea and suggestion. if possible give me modified version of code.
thanks
I will be straightforward about this:
A UoW that contains repositories is an anti pattern at least if DDD is involved. If it isn't, then you're dealing with a very simple app so make you life easier and use a ORM which implements what you want.
As a thumb rule, a repository may contain a UoW as an implementation detail and you should be certain that you really need a repository. A repository inside a UoW is a very specific case which is valid ONLY with CRUD apps and you'll just be reinventing a very small part of an ORM.
Using ado.net directly is wasting time (and money). Either use an ORM or a data mapper aka micro-ORM. With the latter option the UoW is the Db transaction.
You don't gain anything significant using Ado.Net,the performance gain is so small it's not even funny (I know that because I've benchmarked it). The only thing you'll get is longer devel time and human errors.
Actually, if I take a better look at your code, you're basically building a micro ORM, without knowing it. It's still not a repository though, because the abstraction doesn't have business semantics and it's too tightly coupled to an implementation detail.

Encountering ObjectDisposedException when trying to read from SQLiteDataReader

I am trying to read through a stored SQLiteDataReader object. In theory, it "should" work because the object is stored in a variable before it is referenced (and doesn't hit an error until the reference line is reached), but maybe I have the wrong idea.
I'm trying to keep my application in a neatly layered architecture. So, each database table having its own C# class with its own methods for select, insert, update, and delete; only the data layer knows how to communicate with the database, etc.
I was running into connection issues earlier when I tried to make one static SQLiteConnection object that all the data layer classes could reference (so as to keep it open and minimize overhead, if any). So I'm trying to go with the using block to make sure the connection is properly disposed each time I need to access the database, and hoping that this won't cause performance issues.
So basically, here is the method in my DatabaseConnection class that handles basic query execution:
public SQLiteDataReader ExecuteQuery(string sql)
{
SQLiteDataReader rdr = null;
using(SQLiteConnection conn = new SQLiteConnection(ConnectionString))
{
conn.Open();
SQLiteCommand cmd = conn.CreateCommand();
cmd.CommandText = sql;
rdr = cmd.ExecuteReader();
}
return rdr;
}
And here is the code that calls that method. I'll use an object/record of the Associate table as an example.
public class Associate
{
public int RowId { get; private set; }
public int Id { get; set; }
public string Name { get; set; }
private string password;
public string Password
{
get
{
return password;
}
set
{
password = Hash(value); // external password hashing method
}
}
public Associate() { } // default constructor with default values
public Associate(int id)
{
this.Id = id;
Select();
}
// select, insert, update, delete methods
private void Select() { ... }
// these are non-queries and return true/false based on success
public bool Insert() { ... }
public bool Update() { ... }
public bool Delete() { ... }
/* Method that causes the error */
public static Associate[] GetAll()
{
DatabaseConnection con = new DatabaseConnection();
SQLiteDataReader rdr = con.ExecuteQuery("SELECT id FROM Associate");
List<Associate> list = new List<Associate>();
if (rdr != null)
{
while (rdr.Read()) /* this line throws the exception */
{
int next = rdr.GetInt32(0);
list.Add(new Associate(next));
}
}
return list.ToArray();
}
}
The idea here is that using the rdr object, I can access the column names directly so that if the database ever changes, I won't have to rewrite a bunch of code to adjust for the column indices (rdr["id"], rdr["name"], etc.)
So what I don't understand is why rdr in the calling method is having "object disposed" issues because it's stored in a variable before I reference it. I know the connection is disposed at the end of the called method, but since the returned result is stored, shouldn't it technically be able to "survive" outside the using block?
It is the connection that got disposed. The data reader can only read data while the connection still exists.
public SQLiteDataReader ExecuteQuery(string sql)
{
SQLiteDataReader rdr = null;
using(SQLiteConnection conn = new SQLiteConnection(ConnectionString))
{
conn.Open();
SQLiteCommand cmd = conn.CreateCommand();
cmd.CommandText = sql;
rdr = cmd.ExecuteReader();
}
// *** Connection gone at this stage ***
return rdr;
}
Your options are to either return a DataTable, e.g.
public DataTable ExecuteQuery(string sql)
{
SQLiteDataReader rdr = null;
using(SQLiteConnection conn = new SQLiteConnection(ConnectionString))
{
conn.Open();
SQLiteCommand cmd = conn.CreateCommand();
cmd.CommandText = sql;
rdr = cmd.ExecuteReader();
var dataTable = new DataTable();
dataTable.Load(rdr);
return dataTable;
}
}
otherwise, you could keep the connection alive inside the DatabaseConnection class:
class DatabaseConnection : IDisposable
{
private readonly IDbConnection _conn;
public DatabaseConnection()
{
_conn = new SQLiteConnection(ConnectionString);
}
public void Dispose()
{
_conn.Dispose();
}
public SQLDataReader ExecuteQuery(string sql)
{
...
}
}
// sample usage
using (var conn = new DatabaseConnection())
{
using (var reader = conn.ExecuteQuery("SELECT ...")
{
// do your work in here
}
}

Invalid attempt to Read when reader is closed

I have a common database class for my application and in that class i have a function
public MySqlDataReader getRecord(string query)
{
MySqlDataReader reader;
using (var connection = new MySqlConnection(connectionString))
{
connection.Open();
using (var cmd = new MySqlCommand(query, connection))
{
reader = cmd.ExecuteReader();
return reader;
}
}
return null;
}
and on my code behind pages I use
String sql = "SELECT * FROM `table`";
MySqlDataReader dr = objDB.getRecord(sql);
if (dr.Read())
{
// some code goes hear
}
and I am having error as Invalid attempt to Read when reader is closed.
I know access the reader after the database connection is closed is not possible
bot I am looking for a work around in which I need not to change in the codebehind
EDIT: I WILL LIKE THE SOLUTION IN WHICH THE READER IS ASSIGNED TO OTHER OBJECT (SIMILAR TO READER ) AND THEN RETURN THAT OBJECT so i need not to change in all the application pages
You can load the results of your query to memory, then close the connection and still return an IDataReader that works as expected. Note that this costs memory.
public IDataReader getRecord(string query)
{
MySqlDataReader reader;
using (var connection = new MySqlConnection(connectionString))
{
connection.Open();
using (var cmd = new MySqlCommand(query, connection))
{
reader = cmd.ExecuteReader();
var dt = new DataTable();
dt.Load( reader );
return dt.CreateDataReader();
}
}
return null;
}
In the callers:
String sql = "SELECT * FROM `table`";
var dr = objDB.getRecord(sql); // or DataTableReader dr = ...
if (dr.Read())
{
// some code goes here
}
When the scope of your call to using (var connection = new MySqlConnection(connectionString))
ends, the connection will be closed.
However, you are still returning a reader based on that connection. Once you try to use it in your caller method, you will get the error as closed connection can't be used.
Besides, your method is called GetRecord but it returns a reader.
One of the options is to do this:
public void processQuery(string query, Action<MySqlDataReader> fn)
{
using (var connection = new MySqlConnection(connectionString))
{
connection.Open();
using (var cmd = new MySqlCommand(query, connection))
{
using (var reader = cmd.ExecuteReader())
{
fn(reader);
}
}
}
}
// caller
String sql = "SELECT * FROM `table`";
objDB.procesQuery(sql, dr => {
if (dr.Read())
{
// some code goes here
}
});
Your idea of creating an object 'similar to reader', so you don't have to change the caller, would not work: the returned object would need to contain both reader and an open connection, so that you can use the reader. This means you would have to close the connection in the caller. In best case, the caller would need to be modified as follows:
String sql = "SELECT * FROM `table`";
using (MyWrapper wr = objDB.getRecord(sql))
{
if (wr.Reader.Read())
{
// some code goes here
}
}
You will not save that much work, but one missing using statement in the caller will result in your app not working after some time due to a connection leak.
What you want is possible, but it is not a nice solution, because you have to wrap all the functions of the MySqlDataReader class and forward it to the real MySqlDataReader.
See the ConnectedMySqlDataReader class (hint: it does not implement all functions of MySqlDataReader, if you really want to use it, you have to do it yourself) and how it would fit in your solution:
public ConnectedMySqlDataReader GetRecord(string query)
{
return new ConnectedMySqlDataReader(connectionString, query);
}
// ...
var sql = "SELECT * FROM `table`";
using(var dr = objDB.GetRecord(sql))
{
if (dr.Read())
{
// some code goes hear
}
}
i have not tested this class, it is just for demonstration!
using System;
using System.Collections;
using System.Data;
using System.Data.Common;
using MySql.Data.MySqlClient;
namespace MySqlTest
{
public class ConnectedMySqlDataReader : DbDataReader
{
private readonly MySqlConnection _connection;
private readonly Lazy<MySqlDataReader> _reader;
private MySqlCommand _command;
public ConnectedMySqlDataReader(MySqlConnection connection, string query)
{
if(connection == null)
throw new ArgumentNullException("connection");
_connection = connection;
_reader = new Lazy<MySqlDataReader>(() =>
{
_connection.Open();
_command = new MySqlCommand(query, _connection);
return _command.ExecuteReader();
});
}
public ConnectedMySqlDataReader(string connectionString, string query)
: this(new MySqlConnection(connectionString), query) { }
private MySqlDataReader Reader
{
get { return _reader.Value; }
}
public override void Close()
{
if (_reader.IsValueCreated)
_reader.Value.Close();
if(_command != null)
_command.Dispose();
_connection.Dispose();
}
public override DataTable GetSchemaTable()
{
return this.Reader.GetSchemaTable();
}
public override bool NextResult()
{
return this.Reader.NextResult();
}
public override bool Read()
{
return this.Reader.Read();
}
public override int Depth
{
get { return this.Reader.Depth; }
}
public override bool IsClosed
{
get { return this.Reader.IsClosed; }
}
public override int RecordsAffected
{
get { return this.Reader.RecordsAffected; }
}
public override bool GetBoolean(int ordinal)
{
return this.Reader.GetBoolean(ordinal);
}
public override byte GetByte(int ordinal)
{
return this.Reader.GetByte(ordinal);
}
public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, int length)
{
return this.Reader.GetBytes(ordinal, dataOffset, buffer, bufferOffset, length);
}
public override char GetChar(int ordinal)
{
return this.Reader.GetChar(ordinal);
}
public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, int length)
{
return this.Reader.GetChars(ordinal, dataOffset, buffer, bufferOffset, length);
}
public override Guid GetGuid(int ordinal)
{
return this.Reader.GetGuid(ordinal);
}
public override short GetInt16(int ordinal)
{
return this.Reader.GetInt16(ordinal);
}
public override int GetInt32(int ordinal)
{
return this.Reader.GetInt32(ordinal);
}
public override long GetInt64(int ordinal)
{
return this.Reader.GetInt64(ordinal);
}
public override DateTime GetDateTime(int ordinal)
{
return this.Reader.GetDateTime(ordinal);
}
public override string GetString(int ordinal)
{
return this.Reader.GetString(ordinal);
}
public override object GetValue(int ordinal)
{
return this.Reader.GetValue(ordinal);
}
public override int GetValues(object[] values)
{
return this.Reader.GetValues(values);
}
public override bool IsDBNull(int ordinal)
{
return this.Reader.IsDBNull(ordinal);
}
public override int FieldCount
{
get { return this.Reader.FieldCount; }
}
public override object this[int ordinal]
{
get { return this.Reader[ordinal]; }
}
public override object this[string name]
{
get { return this.Reader[name]; }
}
public override bool HasRows
{
get { return this.Reader.HasRows; }
}
public override decimal GetDecimal(int ordinal)
{
return this.Reader.GetDecimal(ordinal);
}
public override double GetDouble(int ordinal)
{
return this.Reader.GetDouble(ordinal);
}
public override float GetFloat(int ordinal)
{
return this.Reader.GetFloat(ordinal);
}
public override string GetName(int ordinal)
{
return this.Reader.GetName(ordinal);
}
public override int GetOrdinal(string name)
{
return this.Reader.GetOrdinal(name);
}
public override string GetDataTypeName(int ordinal)
{
return this.Reader.GetDataTypeName(ordinal);
}
public override Type GetFieldType(int ordinal)
{
return this.Reader.GetFieldType(ordinal);
}
public override IEnumerator GetEnumerator()
{
return this.Reader.GetEnumerator();
}
}
}
PS: this is what sealed in c# context means, and yes, MySqlDataReader is sealed.

How do I close a database connection being used to produce a streaming result in a WCF service?

I have been unable to find any documentation on properly closing database connections in WCF service operations. I have a service that returns a streamed response through the following method.
public virtual Message GetData()
{
string sqlString = BuildSqlString();
SqlConnection conn = Utils.GetConnection();
SqlCommand cmd = new SqlCommand(sqlString, conn);
XmlReader xr = cmd.ExecuteXmlReader();
Message msg = Message.CreateMessage(
OperationContext.Current.IncomingMessageVersion,
GetResponseAction(),
xr);
return msg;
}
I cannot close the connection within the method or the streaming of the response message will be terminated. Since control returns to the WCF system after the completion of that method, I don't know how I can close that connection afterwards. Any suggestions or pointers to additional documentation would be appreciated.
Good question, actually. Unfortunately, one for which I believe there is no good answer. There's actually an active Microsoft Connect ticket about this very issue.
Normally, if you want to stream results and you just need a regular SqlDataReader, you can use the ExecuteReader overload that takes a CommandBehavior, and specifically CommandBehavior.CloseConnection. If a reader is created using this command behavior, then when you Close (or Dispose) the reader, it also closes the underlying connection, so you never have to worry about disposing the SqlConnection.
Unfortunately, there's no equivalent overload of ExecuteXmlReader. You have to dispose the SqlConnection explicitly.
One way around this would be to implement your own XmlReader descendant, wrapping the real XmlReader obtained from ExecuteXmlReader and forcing the connection closed on close.
The basic idea is just to derive from XmlReader and wrap both the real XmlReader and the SqlConnection itself. Something like this:
class SqlXmlReader : XmlReader
{
private SqlConnection connection;
private XmlReader reader;
public SqlXmlReader(SqlCommand cmd)
{
if (cmd == null)
throw new ArgumentNullException("cmd");
this.connection = cmd.Connection;
this.reader = cmd.ExecuteXmlReader();
}
public override void Close()
{
reader.Close();
connection.Close();
}
}
This takes the connection and the reader directly from the SqlCommand so there's no chance of a connection/reader mismatch. You need to implement the rest of the XmlReader methods and properties too - it's just a lot of boring method proxying:
public override int AttributeCount
{
get { return reader.AttributeCount; }
}
public override string BaseURI
{
get { return reader.BaseURI; }
}
public override int Depth
{
get { return reader.Depth; }
}
public override bool EOF
{
get { return reader.EOF; }
}
public override string GetAttribute(int i)
{
return reader.GetAttribute(i);
}
public override string GetAttribute(string name, string namespaceURI)
{
return reader.GetAttribute(name, namespaceURI);
}
public override string GetAttribute(string name)
{
return reader.GetAttribute(name);
}
public override bool HasValue
{
get { return reader.HasValue; }
}
public override bool IsEmptyElement
{
get { return reader.IsEmptyElement; }
}
public override string LocalName
{
get { return reader.LocalName; }
}
public override string LookupNamespace(string prefix)
{
return reader.LookupNamespace(prefix);
}
public override bool MoveToAttribute(string name, string ns)
{
return reader.MoveToAttribute(name, ns);
}
public override bool MoveToAttribute(string name)
{
return reader.MoveToAttribute(name);
}
public override bool MoveToElement()
{
return reader.MoveToElement();
}
public override bool MoveToFirstAttribute()
{
return reader.MoveToFirstAttribute();
}
public override bool MoveToNextAttribute()
{
return reader.MoveToNextAttribute();
}
public override XmlNameTable NameTable
{
get { return reader.NameTable; }
}
public override string NamespaceURI
{
get { return reader.NamespaceURI; }
}
public override XmlNodeType NodeType
{
get { return reader.NodeType; }
}
public override string Prefix
{
get { return reader.Prefix; }
}
public override bool Read()
{
return reader.Read();
}
public override bool ReadAttributeValue()
{
return reader.ReadAttributeValue();
}
public override ReadState ReadState
{
get { return reader.ReadState; }
}
public override void ResolveEntity()
{
reader.ResolveEntity();
}
public override string Value
{
get { return reader.Value; }
}
Dull, dull, dull, but it works. This reader will close the connection for you when it's done, same as a SqlDataReader opened with CommandBehavior.CloseConnection.
Last thing to do would be to create an extension method to make this easier to use:
public static class SqlExtensions
{
public static XmlReader ExecuteSafeXmlReader(this SqlCommand cmd)
{
return new SqlXmlReader(cmd);
}
}
Once you have this, instead of writing:
XmlReader xr = cmd.ExecuteXmlReader();
You write:
XmlReader xr = cmd.ExecuteSafeXmlReader();
That's it. Now when WCF closes your reader, it will automatically close the underlying connection.
(Disclaimer: This hasn't officially been tested, but I can see no reason why it wouldn't work, unless WCF does not actually close the reader. Be sure to actually test this against a live SQL connection to make sure that it really doesn't leak connections.)
You could try some form of a Duplex Service or Session based service. This would allow you to make the request in one call and keep the SqlConnection opened until a Disconnect() style call is made. This disconnect could .Dispose() of the related SQL objects.
You might look into making your service class implement the IDispose

Categories

Resources