I have some problem with writing a Method in the template pattern style. But first my code:
My Base class I am using looks like this:
public abstract class DbBase<T> where T : new()
{
protected abstract Value CommandValue { get; }
protected abstract CommandType CommandType { get; }
protected abstract Mapper<T> GetMapper();
protected abstract IDbConnection GetConnection();
protected abstract Collection<IDataParameter> GetParameters(IDbCommand command);
public Collection<IDataParameter> Paramaters { get; set; }
#region Public Methods
public T Single(int id)
{
return ExecuteReader(id).SingleOrDefault();
}
public Collection<T> All()
{
return ExecuteReader();
}
#endregion
#region Private Methods
private Collection<T> ExecuteReader(int? id = null)
{
var collection = new Collection<T>();
using (var connection = GetConnection())
{
var command = connection.CreateCommand();
command.Connection = connection;
command.CommandType = CommandType;
if (id.HasValue && id.Value > 0)
command.CommandText = CommandValue.Single;
else
command.CommandText = CommandValue.All;
var parameters = GetParameters(command);
if (parameters != null)
{
foreach (var param in GetParameters(command))
command.Parameters.Add(param);
}
try
{
connection.Open();
using (var reader = command.ExecuteReader())
{
try
{
var mapper = GetMapper();
collection = mapper.MapAll(reader);
return collection;
}
finally
{
if (!reader.IsClosed)
reader.Close();
}
}
}
catch (Exception ex)
{
throw new DbBaseException(ex.Message, ex);
}
finally
{
if (connection.State != ConnectionState.Closed)
connection.Close();
}
}
}
#endregion
}
So now for every piece of code which might get changed I have a details class which is inheriting:
public class UserDb : DbBase<User>
{
private static readonly string ALL = "SELECT * FROM [USER]"; //don't use star!
private static readonly string SINGLE = "SELECT * FROM [USER] WHERE USER_ID = @USER_ID";
private static readonly CommandType commandType = CommandType.Text;
protected override Value CommandValue
{
get
{
var value = new Value
{
Single = SINGLE,
All = ALL
};
return value;
}
}
protected override CommandType CommandType
{
get { return commandType; }
}
protected override Mapper<User> GetMapper()
{
return new UserMapper();
}
protected override Collection<IDataParameter> GetParameters(IDbCommand command)
{
var parameters = new Collection<IDataParameter>();
var param = command.CreateParameter();
param.ParameterName = "@USER_ID";
param.Value = 2;
parameters.Add(param);
return parameters;
}
}
Calling Code:
var userDb = new UserDb();
var user = userDb.Single(1);
if (user != null)
Console.WriteLine(string.Format("{0}, {1}, {2}", user.UserId, user.Username, user.Password));
As you can see I have implemented a method called Single which gives me one specific row by id. My problem is how can I push the id into my ExecuteReader method without breaking the template pattern?
I hope you can help me out guys.
Thx
Why are you not using a parameter that has the same name for all your entities like @id
. Then you will not need the GetParameters
stuff any more. Simply call
command.Parameters.AddWithValue("@id", id);
UPDATE
If you want to be able to use a different number of parameters, you can use the params
keyword, which enables you to pass a varying number of parameters (including zero).
public T Single(params int[] id)
{
return ExecuteReader(id).SingleOrDefault();
}
and
private Collection<T> ExecuteReader(params int[] id)
{
...
for (int i = 0; i < id.Length; i++) {
command.Parameters.AddWithValue("@id" + i, id[i]);
}
...
}
And you will have to name your parameters @id0, @id1, @id2, ...
var coll = ExecuteReader();
var coll = ExecuteReader(2);
var coll = ExecuteReader(5, 77);
...
var result = db.Single(1);
var result = db.Single(4, 13);
var result = db.Single(5, 100, 1);
...
UPDATE #2
You can also extract parameter names from the SQL text
private Collection<T> ExecuteReader(params object[] p)
{
...
var matches = Regex.Matches(sql, @"@\w+");
if (matches.Count != p.Length) {
throw new ArgumentException("The # of parameters does not match ...");
}
for (int i = 0; i < p.Length; i++) {
command.Parameters.AddWithValue(matches[i].Value, p[i]);
}
...
}