I'm using a IDbcontext to acces my database
public sealed class DbContext : IDbContext
{
private bool disposed;
private SqlConnection connection;
public DbContext(string connectionString)
{
connection = new SqlConnection(connectionString);
}
public IDbConnection Connection
{
get
{
if (disposed) throw new ObjectDisposedException(GetType().Name);
return connection;
}
}
public IDbTransaction CreateOpenedTransaction()
{
if (connection.State != ConnectionState.Open)
Connection.Open();
return Connection.BeginTransaction();
}
public IEnumerable<T> ExecuteProcedure<T>(string procedure, dynamic param = null, IDbTransaction transaction = null)
{
if (connection.State == ConnectionState.Closed)
{
connection.Open();
}
return Dapper.SqlMapper.Query<T>(connection, procedure, param, transaction,
commandType: CommandType.StoredProcedure);
}
public int ExecuteProcedure(string procedure, dynamic param = null, IDbTransaction transaction = null)
{
if (connection.State == ConnectionState.Closed)
{
connection.Open();
}
return Dapper.SqlMapper.Execute(connection, procedure, param, transaction,
commandType: CommandType.StoredProcedure);
}
public void Dispose()
{
Debug.WriteLine("** Disposing DbContext");
if (disposed) return;
if (connection != null)
{
connection.Dispose();
connection = null;
}
disposed = true;
}
}
I have 2 database "first" and "second"
In the resolution o depenancy of fist, i use
builder.Register<IDbContext>(c =>
new DbContext(ConfigurationManager.ConnectionStrings["first"].ConnectionString))
.InstancePerDependency();
So i need to add:
builder.Register<IDbContext>(c =>
new DbContext(ConfigurationManager.ConnectionStrings["second"].ConnectionString))
.InstancePerDependency();
I need to create a factory for Dbcontext and Use NamedResolve for with autofac, How can I do this??
public class DbContextFactory
{
private ILifetimeScope m_RootLifetimeScope;
public DbContextFactory(ILifetimeScope rootLifetimeScope)
{
m_RootLifetimeScope = rootLifetimeScope;
}
public IDbContext CreateDbContext()
{
if (logic for selection first dbcontext)
{
return m_RootLifetimeScope.ResolveNamed<IDbContext>("first");
}
else if (logic for selection second dbcontext)
{
return m_RootLifetimeScope.ResolveNamed<IDbContext>("second");
}
else
{
throw new NotSupportedException();
}
}
}
//registration
builder.RegisterType<DbContextFactory>().SingleInstance();
//using
var factory = yourContainer.Resolve<DbContextFactory>();
var context = factory.CreateDbContext();
Related
I was trying to implement a wrapper in C# for SQL Server.
The normal workflow without wrapper is fetching the data into a datatable using direct SQL query and then mapping the columns by names into entities.
But as a wrapper is better to accept a mapping function which describes which column maps to which fields of an enumerable.
So, something like this :
public class UserInfo
{
public string FirstName{ get; set; }
public string LastName{ get; set; }
}
enumerableList = dbManager.Execute("** sql query **", /* some method to specify mapping */);
The enumerable will then contain the result from the database, mapped by the execute method. But I am unsure how to specify the mapping?
Even if I do then how to deal with the different data types for each column in the mapping?
If I correct understand, you want something like this:
public static List<T> ReadRows<T>(this SqlHelper sql, string query, SqlParameter[]
parameters, Func<SqlDataReader, T> projection)
{
var command = GetSqlCommand(query, CommandType.StoredProcedure, parameters);
return sql.ExecuteReader(command, reader => reader.Select(projection).ToList());
}
And use like:
var members = _unitOfWork.SqlHelper.ReadRows("spGetMembersByUserCompanies", parameters, _memberProjection);
readonly Func<SqlDataReader, MemberVm> _memberProjection = (r) => new MemberVm
{
InvitationId = r.Get<int?>("InvitationId"),
UserName = r.Get<string>("UserName"),
RoleName = r.Get<string>("RoleName"),
InvitationStatus = (InvitationStatus)r.Get<int>("InvitationStatus"),
LogoUrl = r.Get<string>("LogoUrl")
};
It is a piece of my code. I hope it is start to resolve your problem.
Implementing such a wrapper from bare bones is not that easy. But it is possible. There is an ADO wrapper library in Github : ADOWrapper
The implementation is pretty straightforward.
Short Answer
How to specify mapping between columns? - Use Func
How to deal with the different data types? You can write an extension
Long Answer
Make a generic method that takes as input the query and a Func Delegate (and optional third parameter to pass query parameters as dictionary)
public ICollection<T> Execute<T>(string query, Func<IDataReader, T> map, IDictionary<string, object> parameters = null)
{
ICollection<T> collection = new List<T>();
using (SqlConnection connection = CreateConnection())
{
connection.Open();
using (SqlCommand command = CreateCommand(connection, query, parameters))
{
using (IDataReader reader = await command.ExecuteReader())
{
while(reader.Read())
{
collection.Add(map.Invoke(reader));
}
}
}
connection.Close();
}
return collection;
}
Implementation of AddParameter AND CreateCommand:
private void AddParameter(IDbCommand command, string parameter, object value)
{
IDbDataParameter param = command.CreateParameter();
param.ParameterName = parameter;
param.Value = value;
command.Parameters.Add(param);
}
private SqlCommand CreateCommand(SqlConnection connection, string query,
IDictionary<string, object> parameters = null)
{
SqlCommand command = connection.CreateCommand();
command.CommandText = query;
if(parameters != null && parameters.Count > 0)
{
foreach(KeyValuePair<string, object> parameter in parameters)
{
AddParameter(command, parameter.Key, parameter.Value);
}
}
return command;
}
You can call the method like this :
public class UserInfo
{
public string FirstName{ get; set; }
public string LastName{ get; set; }
}
var enumerableList = manager.Execute("** query **",
(reader) =>
{
return new UserInfo()
{
FirstName = reader.Get<string>("FirstName"),
LastName = reader.Get<string>("LastName "),
};
})
The Get method makes it easy to manage different data types being fetched from column. But it is not an inbuilt method. So you need to write an extension for Data Reader:
public static class DataReaderExtension
{
public static T Get<T>(this IDataReader reader, string column) where T : IComparable
{
try
{
int index = reader.GetOrdinal(column);
if (!reader.IsDBNull(index))
{
return (T)reader[index];
}
}
catch (IndexOutOfRangeException) { throw new Exception($"Column, '{column}' not found."); }
return default(T);
}
public static IEnumerable<string> GetColumns(this IDataReader reader)
{
IEnumerable<string> columns = new List<string>();
if (reader != null && reader.FieldCount > 0)
{
columns = Enumerable.Range(0, reader.FieldCount)
.Select(index => reader.GetName(index))
.ToList();
}
return columns;
}
}
I have this static class:
namespace Dapper
{
public static class SqlMapper
{
public static T ExecuteScalar<T>(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
{
CommandDefinition command = new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.Buffered, new CancellationToken());
return SqlMapper.ExecuteScalarImpl<T>(cnn, ref command);
}
public static Task<int> ExecuteAsync(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
{
return cnn.ExecuteAsync(new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.Buffered, new CancellationToken()));
}
//...
//Goes on for another 200 different static functions
}
}
I want to create some wrapper class that will hold a default value of commandTimeout. I don't want it as a global parameter, I want this class to be build in bootstrapper with this value:
using Dapper;
public class SqlWrapper : ISqlWrapper
{
private readonly ILogger _logger;
private readonly int _commandTimeoutInSec;
public SqlWrapper(ILogger logger, int commandTimeoutInSec)
{
_logger = logger;
_commandTimeoutInSec = commandTimeoutInSec;
}
public async ExecuteScalarAsync<T>(IDbConnection cnn, string sql, object param = null,
int? commandTimeout = null, CommandType? commandType = null)
{
try
{
using (var conn = new SqlConnection(cnn))
{
var commandGuid =
await conn.ExecuteScalarAsync<T>(sql, param, CommandType: CommandType, commandTimeout: commandTimeout ?? _commandTimeoutInSec);
return commandGuid;
}
}
catch (Exception ex)
{
_logger.WriteError(
$"Job execute failed with error:", ex);
throw;
}
}
public async int ExecuteAsync(IDbConnection cnn, string sql, object param = null, int? commandTimeout = null, CommandType? commandType = null)
{
try
{
using (var conn = new SqlConnection(cnn))
{
var commandGuid =
await
conn.ExecuteAsync(sql, param, CommandType: CommandType,
commandTimeout: commandTimeout ?? _commandTimeoutInSec);
return commandGuid;
}
}
catch (Exception ex)
{
_logger.WriteError(
$"Job execute failed with error:", ex);
throw;
}
}
//...
//Goes on for the rest 200 different static functions in SqlMapper
}
the thing is, I feel like it's stupid to implement a wrapper for 200 functions to to pass a default parameter, is there a way to receive the name of the function as a template of parameter and then pass on the call to the same name in SqlMapper?
You may apply the following technique to your scenario. Imagine we had this static class:
public static class Printer
{
public static void Print(string output, int numberOfTimes)
{
for (int i = 0; i < numberOfTimes; i++)
{
Console.WriteLine(output);
}
}
public static void Show(string output, int numberOfTimes)
{
for (int i = 0; i < numberOfTimes; i++)
{
Console.WriteLine(output);
}
}
}
And let's imagine we wanted to give the numberOfTimes a default value, then we can do this:
public class DefaultPrinter
{
private int defaultTimes = 10;
public void ExecuteMethod(Action<string, int> action, string output)
{
action(output, defaultTimes);
}
}
Now the users will not have to indicate how many times the print should occur because the above class will do it for a default of 10 times.
Usage
var printer = new DefaultPrinter();
printer.ExecuteMethod((a, b) => Printer.Print(a, b), "One");
printer.ExecuteMethod((a, b) => Printer.Show(a, b), "Two");
I have this class, in which I am wrapping dapper calls in order to do something like
var results = SqlWrapper.ExecuteQuery<Product,Customer>("SELECT id FROM Products; SELECT id FROM Customers;");
Where
results[0] = List<Product>
results[1] = List<Customer>
I support 1,2,3 output objects, but would like arbitrary. The class is also ugly and full of copy and pasted code. I account for if I want to reuse a connection by optionally passing a connection but the code just seems unclean. What I would really like is a way to define params T[] but as I understand that doesnt work. Is this any way this code can be cleaned/shortened?
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using Dapper;
namespace SqlWrapper
{
public static class SqlWrapper
{
private const string SqlConnectionString = "Server=localhost;Database=TTDS;User Id=sa;Password=sa;";
public static List<T> ExecuteQuery<T>(string sql, object param = null, SqlConnection sqlConnection = null)
{
if (sqlConnection != null)
{
return sqlConnection.Query<T>(sql, param).ToList();
}
using (var tempSqlConnection = new SqlConnection(SqlConnectionString))
{
tempSqlConnection.Open();
return tempSqlConnection.Query<T>(sql, param).ToList();
}
}
public static List<dynamic> ExecuteQuery<T1, T2>(string sql, object param = null, SqlConnection sqlConnection = null)
{
if (sqlConnection != null)
{
return MultiQuery<T1, T2>(sqlConnection, sql, param);
}
using (var tempSqlConnection = new SqlConnection(SqlConnectionString))
{
return MultiQuery<T1, T2>(tempSqlConnection, sql, param);
}
}
public static List<dynamic> ExecuteQuery<T1, T2, T3>(string sql, object param = null,
SqlConnection sqlConnection = null)
{
if (sqlConnection != null)
{
return MultiQuery<T1, T2, T3>(sqlConnection, sql, param);
}
using (var tempSqlConnection = new SqlConnection(SqlConnectionString))
{
return MultiQuery<T1, T2, T3>(tempSqlConnection, sql, param);
}
}
private static List<dynamic> MultiQuery<T1, T2>(SqlConnection sqlConnection, string sql, object param = null)
{
var rv = new List<dynamic>();
using (var grid = sqlConnection.QueryMultiple(sql, param))
{
rv.Add(grid.Read<T1>().ToList());
rv.Add(grid.Read<T2>().ToList());
}
return rv;
}
private static List<dynamic> MultiQuery<T1, T2, T3>(SqlConnection sqlConnection, string sql, object param = null)
{
var rv = new List<dynamic>();
using (var grid = sqlConnection.QueryMultiple(sql, param))
{
rv.Add(grid.Read<T1>().ToList());
rv.Add(grid.Read<T2>().ToList());
rv.Add(grid.Read<T3>().ToList());
}
return rv;
}
public static void ExecuteNonQuery(SqlConnection sqlConnection, string sql, object param, int? timeout = null)
{
if (sqlConnection != null)
{
sqlConnection.Execute(sql, param, commandTimeout: timeout);
}
else
{
using (var tempSqlConnection = new SqlConnection(SqlConnectionString))
{
tempSqlConnection.Open();
tempSqlConnection.Execute(sql, param, commandTimeout: timeout);
}
}
}
}
}
Here is some untested code that demonstrates a couple of ideas that I had.
While "using" is pretty awesome, you can pare down your code some if you optionally create the connection first and then, if necessary, dispose the sqlConnection in a finally block.
If you return a Tuple<List<T>,List<U>,List<V>> you can have strongly typed return values that you can easily use
If you call your most complex function from those that are less complex, you can minimize your duplicated code.
public static class SqlWrapper
{
private const string SqlConnectionString = "Server=localhost;Database=TTDS;User Id=sa;Password=sa;";
private class NoResult { }
public static List<T1> ExecuteQuery<T1>(string sql, object param = null, SqlConnection sqlConnection = null)
{
return ExecuteQuery<T1, NoResult, NoResult>(sql, param, sqlConnection).Item1;
}
public static Tuple<List<T1>, List<T2>> ExecuteQuery<T1, T2>(string sql, object param = null, SqlConnection sqlConnection = null)
{
var result = ExecuteQuery<T1, T2, NoResult>(sql, param, sqlConnection);
return Tuple.Create(result.Item1, result.Item2);
}
public static Tuple<List<T1>, List<T2>, List<T3>> ExecuteQuery<T1, T2, T3>(string sql, object param = null, SqlConnection sqlConnection = null)
{
List<T1> list1;
List<T2> list2 = null;
List<T3> list3 = null;
bool needsDisposed = false;
if (sqlConnection == null)
{
sqlConnection = new SqlConnection(SqlConnectionString);
sqlConnection.Open();
needsDisposed = true;
}
try
{
using (var grid = sqlConnection.QueryMultiple(sql, param))
{
list1 = grid.Read<T1>().ToList();
if (typeof(T2) != typeof(NoResult))
{
list2 = grid.Read<T2>().ToList();
}
if (typeof(T3) != typeof(NoResult))
{
list3 = grid.Read<T3>().ToList();
}
return Tuple.Create(list1, list2, list3);
}
}
finally { if (needsDisposed) sqlConnection.Dispose(); }
}
public static void ExecuteNonQuery(SqlConnection sqlConnection, string sql, object param, int? timeout = null)
{
bool needsDisposed = false;
if (sqlConnection == null)
{
sqlConnection = new SqlConnection(SqlConnectionString);
sqlConnection.Open();
needsDisposed = true;
}
try { sqlConnection.Execute(sql, param, commandTimeout: timeout); }
finally { if (needsDisposed) sqlConnection.Dispose(); }
}
}
I have been working with a project for last 4 months. We are using a custom framework for the development. The problem I am talking about, was working for all other classes. But for the first time I am facing this weird incident. Now Straight to break point.
My framework code is like
public static List<ViewNotSetBillableCoursesEntity> GetAllNotSetBillableCources()
{
try
{
List<ViewNotSetBillableCoursesEntity> entities = new List<ViewNotSetBillableCoursesEntity>();
string command = SELECT;
SqlConnection sqlConnection = MSSqlConnectionHandler.GetConnection();
SqlDataReader dataReader = QueryHandler.ExecuteSelect(command, sqlConnection);
entities = Maps(dataReader);
dataReader.Close();
return entities;
}
catch (Exception exception)
{
throw exception;
}
}
In the above method the dataReader is sent to Maps method.
The Maps method is ......
private static List<ViewNotSetBillableCoursesEntity> Maps(SqlDataReader theReader)
{
SQLNullHandler nullHandler = new SQLNullHandler(theReader);
// the incident is happening here, the SQLNullHandler is given below.
List<ViewNotSetBillableCoursesEntity> entities = null;
while (theReader.Read())
{
if (entities == null)
{
entities = new List<ViewNotSetBillableCoursesEntity>();
}
ViewNotSetBillableCoursesEntity entity = Mapper(nullHandler);
entities.Add(entity);
}
return entities;
}
The SQLNullHandler is given below:
puplic Class SQLNullHandler
{
private IDataReader _reader;
public SQLNullHandler(IDataReader reader)
{
_reader = reader;
}
#region Get Null value
public static object GetNullValue(int Value)
{
if(Value==0)
{
return null;
}
else
{
return Value;
}
}
public static object GetNullValue(double Value)
{
if (Value == 0)
{
return null;
}
else
{
return Value;
}
}
public static object GetNullValue(decimal Value)
{
if (Value == 0)
{
return null;
}
else
{
return Value;
}
}
public static object GetNullValue(DateTime Value)
{
if(DateTime.MinValue==Value)
{
return null;
}
else
{
return Value;
}
}
public static object GetNullValue(string Value)
{
if(Value.Length<=0)
{
return null;
}
else
{
return Value;
}
}
#endregion
public IDataReader Reader
{
get{return _reader;}
}
public bool IsNull(int index)
{
return _reader.IsDBNull(index);
}
public int GetInt32(int i)
{
return _reader.IsDBNull(i)? 0 : _reader.GetInt32(i);
}
public byte GetByte(int i)
{
return _reader.IsDBNull(i)? (byte)0 : _reader.GetByte(i);
}
//and so on for all possible type for this app
}
The Funny thing is for all these classes these methods and lines of code work very fine, but in this scenario after the line SQLNullHandler nullHandler = new SQLNullHandler(theReader); the datareder becomes empty.
My questions are
Why is this Happening and next,
what can be done to solve this problem?
I'm having a bit of a problem with a data serialization method I'm using to copy objects. Here's the method:
public static class ObjectDuplicator
{
public static T Clone<T>(T source)
{
if (!typeof(T).IsSerializable)
{
throw new ArgumentException("the Type must be serializable.", "source");
}
if (Object.ReferenceEquals(source, null)) //dont try to serialize a null object
{
return default(T);
}
IFormatter formatter = new BinaryFormatter();
Stream stream = new MemoryStream();
using (stream)
{
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
return (T)formatter.Deserialize(stream);
}
}
}
The problem is this: when I call this method using the code below
public void AddJob(Job job)
{
if (!Jobs.Contains(job))
{
Job newcopy = Utilities.ObjectDuplicator.Clone<Job>(job);
Jobs.Add(newcopy);
}
}
it throws this exception:
System.InvalidCastException was unhandled
Message=Unable to cast object of type 'KH.CharacterClasses.Freelancer' to type 'KH.CharacterClasses.Job'
Now, the type of job I'm adding is an inherited class from Job, (Freelancer) and the code for those two classes is below
[Serializable]
public class Job : Ability
{
protected JobCommand basecommand1;
protected JobCommand basecommand2;
protected JobCommand basecommand3;
protected JobCommand basecommand4;
protected JobCommand command1;
protected JobCommand command2;
protected JobCommand command3;
protected JobCommand command4;
bool mastered;
protected FFJob job;
protected string name;
int level;
public FFJob SetJob
{
get
{
return job;
}
}
public bool Mastered
{
get
{
return mastered;
}
}
public JobCommand Command1
{
get
{
return command1;
}
set
{
command1 = value;
}
}
public JobCommand DefaultCommand1
{
get
{
return basecommand1;
}
}
public JobCommand Command2
{
get
{
return command2;
}
set
{
command2 = value;
}
}
public JobCommand DefaultCommand2
{
get
{
return basecommand2;
}
}
public JobCommand Command3
{
get
{
return command3;
}
set
{
command3 = value;
}
}
public JobCommand DefaultCommand3
{
get
{
return basecommand3;
}
}
public JobCommand Command4
{
get
{
return command4;
}
set
{
command4 = value;
}
}
public JobCommand DefaultCommand4
{
get
{
return basecommand4;
}
}
public Job(string name, string description, int jobID)
: base(name, description, jobID, -1, -1, null, null, -1, -1)
{
}
public static bool operator ==(Job job1, Job job2)
{
if (System.Object.ReferenceEquals(job1, job2))
return true;
if (((object)job1 == null) || ((object)job2 == null))
return false;
return (job1.Name == job2.Name && job1.UID == job2.UID);
}
public static bool operator !=(Job job1, Job job2)
{
return !(job1 == job2);
}
// public abstract void CharacterModifier(BaseCharacter character);
// public abstract void CharacterDemodifier(BaseCharacter character);
}
[Serializable]
public class Freelancer : Job
{
public Freelancer()
: base("Freelancer", "A character not specializing in any class. Can combine the power of all mastered Jobs.", Globals.JobID.ID)
{
basecommand1 = JobCommand.Attack;
basecommand2 = JobCommand.Free;
basecommand3 = JobCommand.Free;
basecommand4 = JobCommand.Items;
command1 = basecommand1;
command2 = basecommand2;
command3 = basecommand3;
command4 = basecommand4;
job = FFJob.Freelancer;
}
}
I'm a bit stumped here because I know the ObjectDuplicator method does work. In fact, this code HAS worked before, but that was on a different computer, and I haven't looked at it in awhile. I'm a little stumped as to why the casting fails here. If someone could help me out with whats wrong thatd be great. If you need more details, just say what you need. I asked this question yesterday, but didn't get a workable answer.
Thanks
Try replacing
return (T)formatter.Deserialize(stream);
with
var result = formatter.Deserialize(stream);
for (Type now = result.GetType(); now != null; now = now.BaseType)
MessageBox.Show(now.FullName);
return result as T;
What does Clone<T> return? Do you see KH.CharacterClasses.Job in a list of base types? Seems like it's not the base type for Freelancer.
I would never place a return statement into a using clause! Do this instead:
object tClone = null;
using (Stream stream = new MemoryStream()) {
formatter.Serialize(stream, source);
stream.Seek(0, SeekOrigin.Begin);
tClone = formatter.Deserialize(stream);
}
return (T)tClone;
If the exception is still thrown, then your types are indeed incompatible...
Figured out the solution here:
Casting Error when using serialization