I'm getting an odd issue where I'm able to return results from a call to a stored procedure, but the code retrospectively fails.
public IEnumerable<T> ExecuteStoredProcedure<T>(string storedProcedureName, IDataMapper<T> mapper, IDictionary<string, object> parameters)
{
using (var connection = new SqlConnection(connectionString))
{
using (var cmd = new SqlCommand(storedProcedureName, connection))
{
cmd.CommandType = CommandType.StoredProcedure;
foreach (var key in parameters.Keys)
{
cmd.Parameters.AddWithValue(key, parameters[key]);
}
connection.Open();
SqlDataReader reader = cmd.ExecuteReader();
//return MapRecordsToDTOs(reader, mapper);
//let's test:
IEnumerable<T> result = MapRecordsToDTOs(reader, mapper);
var x = (new List<T>(result)).Count;
System.Diagnostics.Debug.WriteLine(x);
return result;
}
}
}
private static IEnumerable<T> MapRecordsToDTOs<T>(SqlDataReader reader, IDataMapper<T> mapper)
{
if (reader.HasRows)
{
while (reader.Read())
{
System.Diagnostics.Debug.WriteLine(reader["Id"]); //what's going on...
yield return mapper.MapToDto((IDataRecord)reader);
}
}
}
Calling this code shows that variable x always represents the number of rows I'd expect to see from a call to my stored procedures.
Additionally my debug output shows the ID values I'd expect to see.
However, after those results are returned, I get the error An exception of type 'System.InvalidOperationException' occurred in System.Data.dll but was not handled in user code from the line if (reader.HasRows) (i.e. which has already been executed). The browser from which I invoke this request shows HTTP Error 502.3 - Bad Gateway.
I suspect the reason is the system's calculating the ID and X values for debug separately to how it would return the real user output. As such, it performs a lazy operation to get the IEnumerable values at the point at which it has to return them; only by this point the using statements have caused the dispose methods to be called, and thus the reader's connection is null (this is what I see when I inspect the reader variable's properties whilst debugging).
Has anyone seen behaviour like this before / is it a bug; or have I just missed something obvious?
Additional Code:
public interface IDataMapper<T>
{
T MapToDto(IDataRecord record);
}
public class CurrencyMapper: IDataMapper<CurrencyDTO>
{
const string FieldNameCode = "Code";
const string FieldNameId = "Id";
const string FieldNameName = "Name";
const string FieldNameNum = "Num";
const string FieldNameE = "E";
const string FieldNameSymbol = "Symbol";
public CurrencyMapper() { }
public CurrencyDTO MapToDto(IDataRecord record)
{
var code = record[FieldNameCode] as string;
var id = record[FieldNameId] as Guid?;
var name = record[FieldNameName] as string;
var num = record[FieldNameNum] as string;
var e = record[FieldNameE] as int?;
var symbol = record[FieldNameSymbol] as char?;
return new CurrencyDTO(id, code, num, e, name, symbol);
}
}
public class CurrencyRepository
{
const string SPReadAll = "usp_CRUD_Currency_ReadAll";
readonly SqlDatabase db;
public CurrencyRepository()
{
db = new SqlDatabase(); //stick to SQL only for the moment for simplicity
}
public IEnumerable<CurrencyDTO> GetCurrencyCodes()
{
var mapper = new CurrencyMapper();
return db.ExecuteStoredProcedure(SPReadAll, mapper);
}
}
public class CurrencyDTO
{
readonly Guid? id;
readonly string code;
readonly string num;
readonly int? e;
readonly string name;
readonly char? symbol;
public CurrencyDTO(Guid? id,string code,string num,int? e,string name, char? symbol)
{
this.id = id;
this.code = code;
this.num = num;
this.e = e;
this.name = name;
this.symbol = symbol;
}
public Guid? Id { get { return id; } }
public string Code { get { return code; } }
public string Num { get { return num; } }
public int? E { get { return e; } }
public string Name { get { return name; } }
public char? Symbol { get { return symbol; } }
}
I've temporarily implemented a workaround which resolves this issue.
This works:
private static IEnumerable<T> MapRecordsToDTOs<T>(SqlDataReader reader, IDataMapper<T> mapper)
{
var list = new List<T>(); //use a list to force eager evaluation
if (reader.HasRows)
{
while (reader.Read())
{
list.Add(mapper.MapToDto((IDataRecord)reader));
}
}
return list.ToArray();
}
As opposed to the original:
private static IEnumerable<T> MapRecordsToDTOs<T>(SqlDataReader reader, IDataMapper<T> mapper)
{
if (reader.HasRows)
{
while (reader.Read())
{
yield return mapper.MapToDto((IDataRecord)reader);
}
}
}
The difference being I move the code affected by the iterator such that it only iterates through the results in the list; and doesn't rely on the compiler sensibly understanding the requirements related to IDisposable objects.
It's my understanding that the compiler should be able to handle this for me (confirmed here: https://stackoverflow.com/a/13504789/361842), so I suspect it's a bug in the compiler.
Reported here: https://connect.microsoft.com/VisualStudio/feedback/details/3113138
Additional demo code here:
https://gist.github.com/JohnLBevan/a910d886df577e442e2f5a9c2dd41293/
Related
I have strange behaviour that my code at specific place is not stepping in specific method. There is no error, no nothing. It is just reaching the line without stepping into it. I was debugging and stepping into each tep to found that issue. I have no idea what's going on, that's first time i face such an issue. Below find my code and at the end explained exactly where it happens.
static class Program
{
private static UnityContainer container;
[STAThread]
private static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Bootstrap();
Application.Run(container.Resolve<FrmLogin>());
}
private static void Bootstrap()
{
container = new UnityContainer();
container.RegisterType<IRepositoryDal<User>, UserRepositoryDal>();
container.RegisterType<IRepositoryDal<Order>, OrderRepositoryDal>();
container.RegisterType<IDbManager, DbManager>(new InjectionConstructor("sqlserver"));
container.RegisterType<IGenericBal<User>, UserBal>();
container.RegisterType<IGenericBal<Order>, OrderBal>();
}
}
public partial class FrmLogin : Form
{
private readonly IGenericBal<User> _userBal;
public FrmLogin(IGenericBal<User> userBal)
{
InitializeComponent();
_userBal = userBal;
}
private void btnSearch_Click(object sender, EventArgs e)
{
try
{
var a = _userBal.SearchByName("John");
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
public class UserBal : IGenericBal<User>
{
private readonly IRepositoryDal<User> _userRepositoryDal;
public UserBal(IRepositoryDal<User> userRepositoryDal)
{
_userRepositoryDal = userRepositoryDal ?? throw new ArgumentNullException(nameof(userRepositoryDal));
}
public IEnumerable<User> SearchByName(string name)
{
return _userRepositoryDal.SearchByName(name);
}
}
public interface IGenericBal<out T> where T : IEntity
{
IEnumerable<T> SearchByName(string name);
}
public class UserRepositoryDal: IRepositoryDal<User>
{
private readonly IDbManager _dbManager;
public UserRepositoryDal(IDbManager dbManager)
{
_dbManager = dbManager;
}
public IEnumerable<User> SearchByName(string username)
{
var parameters = new List<IDbDataParameter>
{
_dbManager.CreateParameter("#Name", 50, username, DbType.String),
};
username = "JUSTyou";
var userDataTable = _dbManager.GetDataTable("SELECT * FROM T_Marke WHERE Name=#Name", CommandType.Text, parameters.ToArray());
foreach (DataRow dr in userDataTable.Rows)
{
var user = new User
{
Id = int.Parse(dr["Id"].ToString()),
Firstname = dr["Name"].ToString(),
};
yield return user;
}
}
}
public interface IRepositoryDal<T> where T : IEntity
{
IEnumerable<T> SearchByName(string username);
T SearchById(string id);
void Update(T entity);
void Remove(T entity);
void Add(T entity);
}
What happens here is:
When i start to debug using breakpoints i start to click button which raises btnSearch_Click handler you can find in my code. When it happens it goes to: var a = _userBal.SearchByName("John"); then to UserBal's code SearchByName method. When it reaches: return _userRepositoryDal.SearchByName(name); it's not going into in this case UserRepositoryDal's SerachByName method. It's just highlight this line of code and going next but not inside. No error, no nothing... Why it happens?
This is called "Lazy evaluation": https://blogs.msdn.microsoft.com/pedram/2007/06/02/lazy-evaluation-in-c/
In brief, you use yield return to return the method's results, which means that the code does not get evaluated immediately, but the actual method execution gets postponed up to until you actually use some results of the evaluation.
Update:
If you want to evaluate your code immediately, you need to use it somehow. The simplest way would be to return the whole result set to create a new array or list from it. You can do it, for instance, by replacing:
return _userRepositoryDal.SearchByName(name);
with:
return _userRepositoryDal.SearchByName(name).ToList();
While this might be good for debugging, it will also remove the performance gains you obtain by using lazy evaluation.
This bit of code is an Lazy Enumeration:
public IEnumerable<User> SearchByName(string username)
{
var parameters = new List<IDbDataParameter>
{
_dbManager.CreateParameter("#Name", 50, username, DbType.String),
};
username = "JUSTyou";
var userDataTable = _dbManager.GetDataTable("SELECT * FROM T_Marke WHERE Name=#Name", CommandType.Text, parameters.ToArray());
foreach (DataRow dr in userDataTable.Rows)
{
var user = new User
{
Id = int.Parse(dr["Id"].ToString()),
Firstname = dr["Name"].ToString(),
};
yield return user;
}
}
By using yield return user; your telling .Net to only run this code when it's enumerated. At no point are you accessing the result of SearchByName. So it won't step into it:
public IEnumerable<User> SearchByName(string name)
{
//this doesn't access the result
return _userRepositoryDal.SearchByName(name);
//this would
//return _userRepositoryDal.SearchByName(name).ToList();
}
The easiest way to "fix" this is to remove the enumeration as I don't think this is what you want:
public IEnumerable<User> SearchByName(string username)
{
List<User> response = new List<User>();
var parameters = new List<IDbDataParameter>
{
_dbManager.CreateParameter("#Name", 50, username, DbType.String),
};
username = "JUSTyou";
var userDataTable = _dbManager.GetDataTable("SELECT * FROM T_Marke WHERE Name=#Name", CommandType.Text, parameters.ToArray());
foreach (DataRow dr in userDataTable.Rows)
{
var user = new User
{
Id = int.Parse(dr["Id"].ToString()),
Firstname = dr["Name"].ToString(),
};
//Add to a collection
response.Add(user);
}
//return result
return response;
}
I have already searched for the error
the connection string property has not been initialized.
on Google as well as on Stack Overflow but couldn't find the solution. I have created a database class for interaction with database all related code is written in this file. The problem is same code runs fine on other pages and it just don't work on a page called "addevent.aspx" I don't understand the reason why it is not running properly.
Here are the methods that I created in database.cs file
public void CreateConnection()
{
var ConfiguredString = ConfigurationManager.ConnectionStrings[ConnectionString].ConnectionString;
obj_sqlconnection = new SqlConnection(ConfiguredString);
}
//This property will set the connection string for database
public string ConnectionString
{
get
{ //if _connectionString is already created or set, only then it will return the value of _connectionString
if (_connectionString != string.Empty && _connectionString != "" && _connectionString != null)
return _connectionString;
else
return string.Empty;
}
// When you want to set the connection string set block is called.
set
{ // this line sets the connection string to the _connectionString data member for the first time.
if (_connectionString == string.Empty || _connectionString == "" || _connectionString == null)
_connectionString = value;
}
}
// Open database connection.
public void OpenConnection()
{
obj_sqlconnection.Open();
}
// Close database connection.
public void CloseConnection()
{
obj_sqlconnection.Close();
obj_sqlconnection.Dispose();
}
public SqlConnection GetCurrentConnection
{
get { return obj_sqlconnection; }
set { obj_sqlconnection = value; }
}
I simply don't understand the logic of this error and its occurrence. I get this error when I open the connection
How do I call these methods, I have already created a object of database.cs class outside the method AddEvent with object name mydb
public int AddEvent(string _title, string _description, string _place, int _eventTypeID, string _startingTime, string _endingTime, string _startingDate, string _endingDate, string _creatorID, string _picture)
{
string[] blacklist = { _title, _description, _place, _picture };
if (Jvalidate.FilterBlackLIstKeywords(blacklist))
{
int eventid = Convert.ToInt32(mydb.GetLastValueByColumnName("event_id", "tbl_events"));
int rowsaffected = 0;
mydb.CreateConnection();
mydb.InitializeSQLCommandObject(mydb.GetCurrentConnection, "spAddEvent", true);
mydb.obj_sqlcommand.Parameters.AddWithValue("#eventID", eventid + 1);
mydb.obj_sqlcommand.Parameters.AddWithValue("#title", _title);
mydb.obj_sqlcommand.Parameters.AddWithValue("#description", _description);
mydb.obj_sqlcommand.Parameters.AddWithValue("#place", _place);
mydb.obj_sqlcommand.Parameters.AddWithValue("#eventType", _eventTypeID);
mydb.obj_sqlcommand.Parameters.AddWithValue("#startingTime", _startingTime);
mydb.obj_sqlcommand.Parameters.AddWithValue("#endingTime", _endingTime);
mydb.obj_sqlcommand.Parameters.AddWithValue("#startDate", _startingDate);
mydb.obj_sqlcommand.Parameters.AddWithValue("#endDate", _endingDate);
mydb.obj_sqlcommand.Parameters.AddWithValue("#schoolID", SchoolID);
mydb.obj_sqlcommand.Parameters.AddWithValue("#eventCreatorID", _creatorID);
mydb.obj_sqlcommand.Parameters.AddWithValue("#eventPicture", _picture);
try
{
//mydb.obj_sqlconnection.ConnectionString = ConfigurationManager.ConnectionStrings["cesConnectionString"].ToString();
mydb.OpenConnection();
rowsaffected = mydb.obj_sqlcommand.ExecuteNonQuery();
}
finally
{
mydb.CloseConnection();
mydb.obj_sqlcommand.Dispose();
}
return rowsaffected;
}
return 0;
}
it's too complicated solution... this will solve your problem of understanding and unnecessary code lines:
solution:
namespace Stackoverflow
{
public static class Solution
{
static readonly string _connectionStringName =
#"mainConnectionStringName";
static readonly string _connectionString =
_connectionStringName.getConnectionString();
// string extended method like .ToLower() or .Trim()
public static string getConnectionString(
this string connectionStringName)
{
return
System.
Configuration.
ConfigurationManager.
ConnectionStrings[connectionStringName].
ConnectionString;
}
public static object SqlExecute(
string connectionStringName,
string storedProcedureName,
System
.Collections
.Generic
.Dictionary<string, object> parameters,
bool isScalar)
{
object result = null;
using (System
.Data
.SqlClient
.SqlConnection connection =
new System.Data.SqlClient.SqlConnection(
string.IsNullOrWhiteSpace(connectionStringName)
? _connectionString
: connectionStringName.getConnectionString()))
if (connection != null)
using (System.Data.SqlClient.SqlCommand command =
new System.Data.SqlClient.SqlCommand()
{
CommandText = storedProcedureName,
CommandType = System
.Data
.CommandType
.StoredProcedure,
Connection = connection
})
if (command != null)
{
if (parameters != null)
foreach (System
.Collections
.Generic
.KeyValuePair<string, object>
pair in parameters)
command.Parameters.AddWithValue(
pair.Key, pair.Value);
command.Connection.Open();
result = isScalar
? command.ExecuteScalar()
: command.ExecuteNonQuery();
if (command.Connection.State ==
System.Data.ConnectionState.Open)
command.Connection.Close();
}
return result;
}
}
}
usage:
namespace SomeNamespace
{
public sealed class SomeClass
{
public int Example()
{
return (int)Stackoverflow
.Solution
.SqlExecute(
#"anyConnectionStringName", // or null for main connection string
#"anyStoredProcedureName",
new System
.Collections
.Generic
.Dictionary<string, object>()
{
{ #"field0", "value" },
{ #"field1", -1.5 },
{ #"field2", System.DateTime.Now },
{ #"field3", 3.5 },
{ #"field4", 7 },
},
false // for ExecuteNonQuery or true for ExecuteScalar
);
}
}
}
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.
Looking for design guidelines for the following problem.
I'm receiving two string values - action and message and have to call appropriate method which processes string message (processM1MessageVer1, processM1MessageVer2, processM2MessageVer1...). The method I have to call depends on the given string action. There are 2 versions (but in future there might be more) of each processing method. The version of method I have to call is determined by global variable version. Every method returns object of different type (ResultObject1, ResultObject2...). The result has to be serialized, converted to base64 and returned back.
Is there more elegant way of writing this (eliminate duplicate code, make possible future changes easier, reduce code...):
string usingVersion = "ver1";
public string processRequest(string action, string message)
if (usingVersion == "ver1"){
processRequestVer1(action, message);
}
else{
processRequestVer2(action, message);
}
}
//version 1
public string processRequestVer1(string action, string message){
string result = "";
switch (action){
case "m1":
ResultObject1 ro = processM1MessageVer1(message);
result = serialize(ro);
result = convertToB64(result);
case "m2":
ResultObject2 ro = processM2MessageVer1(message);
result = serialize(ro);
result = convertToB64(result);
case "m3":
ResultObject3 ro = processM3MessageVer1(message);
result = serialize(ro);
result = convertToB64(result);
}
return result;
}
//version 2
public string processRequestVer2(string action, string message){
string result = "";
switch (action){
case "m1":
ResultObject1 ro = processM1MessageVer2(message);
result = serialize(ro);
result = convertToB64(result);
case "m2":
ResultObject2 ro = processM2MessageVer2(message);
result = serialize(ro);
result = convertToB64(result);
case "m3":
ResultObject3 ro = processM3MessageVer2(message);
result = serialize(ro);
result = convertToB64(result);
}
return result;
}
It would be simplier if messages that have to be processed are of different object types instead of strings so that appropriate method could be called polymorphically. The fact that every process method returns different object type also complicates things even more. But these don't depend on me and I cannot change it.
My approach (make it more object oriented, and you should justify whether it's appropriate to create class structure depending on how complex your processing logic is. If your processing logic is only little then maybe this is over-engineering):
For serialize and convert to base 64, I assume you have some logic to do those tasks in a generic way. If not, move those to sub class also
public interface IRequestProcessorFactory
{
IRequestProcessor GetProcessor(string action);
}
public class FactoryVersion1 : IRequestProcessorFactory
{
public IRequestProcessor GetProcessor(string action)
{
switch(action)
{
case "m1":
return new M1Ver1RequestProcessor();
case "m2":
return new M2Ver1RequestProcessor();
case "m3":
return new M3Ver1RequestProcessor();
default:
throw new NotSupportedException();
}
}
}
public class FactoryVersion2 : IRequestProcessorFactory
{
public IRequestProcessor GetProcessor(string action)
{
switch(action)
{
case "m1":
return new M1Ver2RequestProcessor();
case "m2":
return new M2Ver2RequestProcessor();
case "m3":
return new M3Ver2RequestProcessor();
default:
throw new NotSupportedException();
}
}
}
public interface IRequestProcessor
{
string ProcessRequest(string message);
}
public class RequestProcessorBase<T>
{
public string ProcessRequest(string message)
{
T result = Process(message);
string serializedResult = Serialize(result);
return ConvertToB64(serializedResult);
}
protected abstract T Process(string message);
private string Serialize(T result)
{
//Serialize
}
private string ConvertToB64(string serializedResult)
{
//Convert
}
}
public class M1Ver1RequestProcessor : RequestProcessorBase<ResultObject1>
{
protected ResultObject1 Process(string message)
{
//processing
}
}
public class M2Ver1RequestProcessor : RequestProcessorBase<ResultObject2>
{
protected ResultObject2 Process(string message)
{
//processing
}
}
public class M3Ver1RequestProcessor : RequestProcessorBase<ResultObject3>
{
protected ResultObject3 Process(string message)
{
//processing
}
}
public class M1Ver2RequestProcessor : RequestProcessorBase<ResultObject1>
{
protected ResultObject1 Process(string message)
{
//processing
}
}
public class M2Ver2RequestProcessor : RequestProcessorBase<ResultObject2>
{
protected ResultObject2 Process(string message)
{
//processing
}
}
public class M3Ver2RequestProcessor : RequestProcessorBase<ResultObject3>
{
protected ResultObject3 Process(string message)
{
//processing
}
}
Usage:
string action = "...";
string message = "...";
IRequestProcessorFactory factory = new FactoryVersion1();
IRequestProcessor processor = factory.GetProcessor(action);
string result = processor.ProcessRequest(message);
The switch is still there in factory class, but it only returns processor and doesn't do actual work so it's fine for me
First - define interface that suit you best, like this
public interface IProcessMessage
{
string ActionVersion { get; }
string AlgorithmVersion { get; }
string ProcessMessage(string message);
}
Then create as many implementation as you need
public class processorM1Ver1 : IProcessMessage
{
public string ProcessMessage(string message)
{
ResultObject1 ro1 = processM1MessageVer1(message);
var result = serialize(ro1);
result = convertToB64(result);
return result;
}
public string ActionVersion {get { return "m1"; }}
public string AlgorithmVersion {get { return "ver1"; }}
}
public class processorM2Ver1 : IProcessMessage
{
public string ActionVersion {get { return "m2"; }}
public string AlgorithmVersion {get { return "ver1"; }}
public string ProcessMessage(string message)
{
ResultObject1 ro1 = processM2MessageVer1(message);
var result = serialize(ro1);
result = convertToB64(result);
return result;
}
}
public class processorM1Ver2 : IProcessMessage
{
public string ActionVersion {get { return "m1"; }}
public string AlgorithmVersion {get { return "ver2"; }}
public string ProcessMessage(string message)
{
ResultObject1 ro1 = processM1MessageVer2(message);
var result = serialize(ro1);
result = convertToB64(result);
return result;
}
}
Now you need something that know which implementation is best in current context
public class MessageProcessorFactory
{
private MessageProcessorFactory() { }
private static readonly MessageProcessorFactory _instance = new MessageProcessorFactory();
public static MessageProcessorFactory Instance { get { return _instance; }}
private IEnumerable<IProcessMessage> _processorCollection;
IEnumerable<IProcessMessage> ProcessorCollection
{
get
{
if (_processorCollection == null)
{
//use reflection to find all imlementation of IProcessMessage
//or initialize it manualy
_processorCollection = new List<IProcessMessage>()
{
new processorM1Ver1(),
new processorM2Ver1(),
new processorM1Ver2()
};
}
return _processorCollection;
}
}
internal IProcessMessage GetProcessor(string action)
{
var algorithVersion = ReadAlgorithVersion();
var processor = ProcessorCollection.FirstOrDefault(x => x.AlgorithmVersion == algorithVersion && x.ActionVersion == action);
return processor;
}
private string ReadAlgorithVersion()
{
//read from config file
//or from database
//or where this info it is kept
return "ver1";
}
}
It can be use in such way
public class Client
{
public string ProcessRequest(string action, string message)
{
IProcessMessage processor = MessageProcessorFactory.Instance.GetProcessor(action);
return processor.ProcessMessage(message);
}
}
I have a repository like this :
public abstract class DbRepository : IDbRepository
{
public TEntity Insert<TEntity>(TEntity entity) where TEntity : class
{
_context.Entry(entity).State = EntityState.Added;
return entity;
}
public TEntity Update<TEntity>(TEntity entity) where TEntity : class
{
_context.Entry(entity).State = EntityState.Modified;
return entity;
}
}
Service Contract is like :
[ServiceContract]
public interface IDbRepository
{
[OperationContract]
TEntity Insert<TEntity>(TEntity entity) where TEntity : class;
[OperationContract]
TEntity Update<TEntity>(TEntity entity) where TEntity : class;
}
Now I know I cant send this via wcf, I have to make the open generic class clossed.
But the problem is I have many entities in my Domain data repository and I want it should be decided by the client what entity it is needed may be via reflection or predefined known types.
So my question :
Is there a smart or fake way to send these generics service via wcf ?
My Goal is I dont want to write this servicecontract for each and every entity.
Many thanks.
Edit: Guys have you seen this Here Tweak in app.config file below:
<endpoint
address="myAddress" binding="basicHttpBinding"
bindingConfiguration="myBindingConfiguration1"
contract="Contracts.IEntityReadService`1[[Entities.mySampleEntity, Entities]], Service.Contracts" />
Can somebody please explain this how this contract has been implemented.
Has anybody tried to implement this tweak in app.config file. I have tried but not working for me for now. Need helpful answer !
Have you look into WCF Data Services? This seems to be the route you want to go down without hand crafting the interfaces and plumbing yourself.
As you have stated, interfaces are not good over WCF. One particular flaw is the expectation of the IQueryable<T> over WCF, which does not work at all. Even IEnumerable<T> doesn't give the expected results all of the time.
Is there a smart or fake way to send these generics service via wcf ?
My Goal is I dont want to write this servicecontract for each and
every entity. Many thanks.
hmm, why not?
Lets us try the following :
This interface is necessary as it will identify which objects can be used by your Repository.I don't know what your implementation of your T Entity is or how your CRUD operations work; however, just in case you don't have it covered, we're also going to add the methid GetPrimaryKeys.
public interface IRepositoryEntry
{
IList<String> GetPrimaryKeys();
}
So now we need a repository, since your biggest concern is that you don't want to rewrite code you should try something like this:
This implementation means that whatever our database entries are, they must support the default constructor. This is important for the implementation of this this interface :
public interface IRepository<T> where T : IRepositoryEntry, new()
{
event EventHandler<RepositoryOperationEventArgs> InsertEvent;
event EventHandler<RepositoryOperationEventArgs> UpdateEvent;
event EventHandler<RepositoryOperationEventArgs> DeleteEvent;
IList<String> PrimaryKeys { get; }
void Insert(T Entry);
void Update(T Entry);
void Delete(Predicate<T> predicate);
bool Exists(Predicate<T> predicate);
T Retrieve(Predicate<T> predicate);
IEnumerable<T> RetrieveAll();
}
Now we will make our service:
[ServiceContract]
public interface IDbRepository
{
[OperationContract]
object Insert(object entity);
[OperationContract]
object Update(object entity);
}
Notice no generics? That's important. Now we need to have a creative implementation for our Repository. I'm going to give two, one for Memory so unit testing can be done and the other for a database.
public class OracleRepository
{
const string User = "*";
const string Pass = "*";
const string Source = "*";
const string ConnectionString = "User Id=" + User + ";" + "Password=" + Pass + ";" + "Data Source=" + Source + ";";
public static IDbConnection GetOpenIDbConnection(){
//Not really important; however, for this example I Was using an oracle connection
return new OracleConnection(ConnectionString).OpenConnection();
}
protected IEnumerable<String> GetEntryPropertyNames(Type type){
foreach (var propInfo in type.GetProperties())
yield return propInfo.Name;
}
}
public class OracleRepository<T> : OracleRepository,IDisposable, IRepository<T> where T : IRepositoryEntry, new()
{
#region Public EventHandlers
public event EventHandler<RepositoryOperationEventArgs> InsertEvent;
public event EventHandler<RepositoryOperationEventArgs> UpdateEvent;
public event EventHandler<RepositoryOperationEventArgs> DeleteEvent;
#endregion
#region Public Properties
public IList<String> PrimaryKeys{ get { return primaryKeys.AsReadOnly(); } }
public IList<String> Properties { get; private set; }
public String InsertText { get; private set; }
public String UpdateText { get; private set; }
public String DeleteText { get; private set; }
public String SelectText { get; private set; }
#endregion
#region Private fields
List<String> primaryKeys;
IDbConnection connection;
IDbTransaction transaction;
bool disposed;
#endregion
#region Constructor(s)
public OracleRepository()
{
primaryKeys = new List<String>(new T().GetPrimaryKeys());
Properties = new List< String>(GetEntryPropertyNames(typeof(T))).AsReadOnly();
SelectText = GenerateSelectText();
InsertText = GenerateInsertText();
UpdateText = GenerateUpdateText();
DeleteText = GenerateDeleteText();
connection = GetOpenIDbConnection();
}
#endregion
#region Public Behavior(s)
public void StartTransaction()
{
if (transaction != null)
throw new InvalidOperationException("Transaction is already set. Please Rollback or commit transaction");
transaction = connection.BeginTransaction();
}
public void CommitTransaction()
{
using(transaction)
transaction.Commit();
transaction = null;
}
public void Rollback()
{
using (transaction)
transaction.Rollback();
transaction = null;
}
public void Insert(IDbConnection connection, T entry)
{
connection.NonQuery(InsertText, Properties.Select(p => typeof(T).GetProperty(p).GetValue(entry)).ToArray());
if (InsertEvent != null) InsertEvent(this, new OracleRepositoryOperationEventArgs() { Entry = entry, IsTransaction = (transaction != null) });
}
public void Update(IDbConnection connection, T entry)
{
connection.NonQuery(UpdateText, Properties.Where(p => !primaryKeys.Any(k => k == p)).Concat(primaryKeys).Select(p => typeof(T).GetProperty(p).GetValue(entry)).ToArray());
if (UpdateEvent != null) UpdateEvent(this, new OracleRepositoryOperationEventArgs() { Entry = entry, IsTransaction = (transaction != null) });
}
public void Delete(IDbConnection connection, Predicate<T> predicate)
{
foreach (var entry in RetrieveAll(connection).Where(new Func<T, bool>(predicate)))
{
connection.NonQuery(DeleteText, primaryKeys.Select(p => typeof(T).GetProperty(p).GetValue(entry)).ToArray());
if (DeleteEvent != null) DeleteEvent(this, new OracleRepositoryOperationEventArgs() { Entry = null, IsTransaction = (transaction != null) });
}
}
public T Retrieve(IDbConnection connection, Predicate<T> predicate)
{
return RetrieveAll(connection).FirstOrDefault(new Func<T, bool>(predicate));
}
public bool Exists(IDbConnection connection, Predicate<T> predicate)
{
return RetrieveAll(connection).Any(new Func<T, bool>(predicate));
}
public IEnumerable<T> RetrieveAll(IDbConnection connection)
{
return connection.Query(SelectText).Tuples.Select(p => RepositoryEntryBase.FromPlexQueryResultTuple(new T(), p) as T);
}
#endregion
#region IRepository Behavior(s)
public void Insert(T entry)
{
using (var connection = GetOpenIDbConnection())
Insert(connection, entry);
}
public void Update(T entry)
{
using (var connection = GetOpenIDbConnection())
Update(connection, entry);
}
public void Delete(Predicate<T> predicate)
{
using (var connection = GetOpenIDbConnection())
Delete(connection, predicate);
}
public T Retrieve(Predicate<T> predicate)
{
using (var connection = GetOpenIDbConnection())
return Retrieve(connection, predicate);
}
public bool Exists(Predicate<T> predicate)
{
using (var connection = GetOpenIDbConnection())
return Exists(predicate);
}
public IEnumerable<T> RetrieveAll()
{
using (var connection = GetOpenIDbConnection())
return RetrieveAll(connection);
}
#endregion
#region IDisposable Behavior(s)
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
#region Protected Behavior(s)
protected virtual void Dispose(Boolean disposing)
{
if(disposed)
return;
if (disposing)
{
if(transaction != null)
transaction.Dispose();
if(connection != null)
connection.Dispose();
}
disposed = true;
}
#endregion
#region Private Behavior(s)
String GenerateInsertText()
{
String statement = "INSERT INTO {0}({1}) VALUES ({2})";
//Do first entry here becasse its unique input.
String columnNames = Properties.First();
String delimiter = ", ";
String bph = ":a";
String placeHolders = bph + 0;
//Start # 1 since first entry is already done
for (int i = 1; i < Properties.Count; i++)
{
columnNames += delimiter + Properties[i];
placeHolders += delimiter + bph + i;
}
statement = String.Format(statement, typeof(T).Name, columnNames, placeHolders);
return statement;
}
String GenerateUpdateText()
{
String bph = ":a";
String cvpTemplate = "{0} = {1}";
String statement = "UPDATE {0} SET {1} WHERE {2}";
//Can only set Cols that are not a primary Keys, Get those Columns
var Settables = Properties.Where(p => !PrimaryKeys.Any(k => k == p)).ToList();
String cvp = String.Format(cvpTemplate, Settables.First(), bph + 0);
String condition = String.Format(cvpTemplate, PrimaryKeys.First(), bph + Settables.Count);
//These are the values to be set | Start # 1 since first entry is done above.
for (int i = 1; i < Settables.Count; i++)
cvp += ", " + String.Format(cvpTemplate, Settables[i], bph + i);
//This creates the conditions under which the values are set. | Start # 1 since first entry is done above.
for (int i = Settables.Count + 1; i < Properties.Count; i++)
condition += " AND " + String.Format(cvpTemplate, PrimaryKeys[i - Settables.Count], bph + i);
statement = String.Format(statement, typeof(T).Name, cvp, condition);
return statement;
}
String GenerateDeleteText()
{
String bph = ":a";
String cvpTemplate = "{0} = {1}";
String statement = "DELETE FROM {0} WHERE {1}";
String condition = String.Format(cvpTemplate, PrimaryKeys.First(), bph + 0);
for (int i = 1; i < PrimaryKeys.Count; i++)
condition += " AND " + String.Format(cvpTemplate, PrimaryKeys[i], bph + i);
statement = String.Format(statement, typeof(T).Name, condition);
return statement;
}
String GenerateSelectText()
{
String statement = "SELECT * FROM {0}";
statement = String.Format(statement, typeof(T).Name);
return statement;
}
#endregion
#region Destructor
~OracleRepository()
{
Dispose(false);
}
#endregion
}
The second implementation for in memory operation is this :
public class InMemoryRepository<T> : IRepository<T> where T : IRepositoryEntry, new()
{
//RepositoryEntryBase,
public event EventHandler<RepositoryOperationEventArgs> InsertEvent;
public event EventHandler<RepositoryOperationEventArgs> UpdateEvent;
public event EventHandler<RepositoryOperationEventArgs> DeleteEvent;
public IList<String> PrimaryKeys { get; protected set; }
List<T> data;
public InMemoryRepository()
{
PrimaryKeys = new List<String>(new T().GetPrimaryKeys());
data = new List<T>();
}
public void Insert(T Entry)
{
if (Get(Entry) != null)
throw new Exception("Duplicate Entry - Identical Key already exists");
data.Add(Entry);
if (InsertEvent != null)
InsertEvent(this, new RepositoryOperationEventArgs() { Entry = Entry });
}
public void Update(T Entry)
{
var obj = Get(Entry);
if (obj == null)
throw new Exception("Object does not exist");
obj = Entry;
if (UpdateEvent != null)
UpdateEvent(this, new RepositoryOperationEventArgs() { Entry = obj });
}
public void Delete(Predicate<T> predicate)
{
data.RemoveAll(predicate);
if (DeleteEvent != null)
DeleteEvent(this, new RepositoryOperationEventArgs() { Entry = null });
}
public bool Exists(Predicate<T> predicate)
{
return data.Exists(predicate);
}
public T Retrieve(Predicate<T> predicate)
{
return data.FirstOrDefault(new Func<T, bool>(predicate));
}
public IEnumerable<T> RetrieveAll()
{
return data.ToArray();
}
T Get(T Entry)
{
//Returns Entry based on Identical PrimaryKeys
Type entryType = typeof(T);
var KeyPropertyInfo = entryType.GetProperties().Where(p => PrimaryKeys.Any(p2 => p2 == p.Name));
foreach (var v in data)
{
//Assume the objects are identical by default to prevent false positives.
Boolean AlreadyExists = true;
foreach (var property in KeyPropertyInfo)
if (!property.GetValue(v).Equals(property.GetValue(Entry)))
AlreadyExists = false;
if (AlreadyExists)
return v;
}
return default(T);
}
}
Whew, that was a lot of code. Now there are a few non standard functions. These are what they all are:
public static class IDbConnectionExtensions
{
public static IDbCommand CreateCommand(this IDbConnection Conn, string CommandText, params object[] Parameters)
{
var Command = Conn.CreateCommand();
Command.CommandText = CommandText;
foreach (var p in Parameters ?? new object[0])
Command.Parameters.Add(Command.CreateParameter(p));
return Command;
}
public static IDbDataParameter CreateParameter(this IDbCommand Command, object Value)
{
var Param = Command.CreateParameter();
Param.Value = Value;
return Param;
}
public static PlexQueryResult Query(this IDbConnection conn, String CommandText, params object[] Arguments)
{
using (var Comm = conn.CreateCommand(CommandText, Arguments))
using (var reader = Comm.ExecuteReader(CommandBehavior.KeyInfo))
return new PlexQueryResult(reader);
}
public static int NonQuery(this IDbConnection conn, String CommandText, params object[] Arguments)
{
using (var Comm = conn.CreateCommand(CommandText, Arguments))
return Comm.ExecuteNonQuery();
}
public static IDbConnection OpenConnection(this IDbConnection connection)
{
connection.Open();
return connection;
}
}
Now, how we tie everything together is simple, this one I'm writing off the top of my head with no editor so please bear with me :
Lets say we have the following class which inherits from IRepostoryEntry:
//Feel free to ignore RepostoryEntryBase
public class COMPANIES : RepositoryEntryBase, IRepositoryEntry
{
public string KEY { get; set; } //KEY VARCHAR2(20) N
public int COMPANY_ID { get; set; } //COMPANY_ID NUMBER(10) N
public string DESCRIPTION { get; set; }//DESCRIPTION VARCHAR2(100) N
public COMPANIES() : base ()
{
primaryKeys.Add("COMPANY_ID");
}
}
public abstract class DbRepository : IDbRepository
{
public Dictionary<Type,IRepository> Repositories { get;set; }
public DbRepository(){
Repositories = new Dictionary<Type,IRepository>();
Repositories .add(typeof(COMPANIES)),new OracleRepository<COMPANIES>());
}
public object Insert(object entity)
{
if(!(entity is IRepositoryEntry))
throw new NotSupportedException("You are bad and you should feel bad");
if(!Repositories.ContainsKey(entity.GetType()))
throw new NotSupportedException("Close but no cigar");
Dictionary[entity.GetType()].Insert(entity);
}
//You can add additional operations here:
}
That had to be the longest answer I ever wrote:
I built this DLL to get my jump started on this method of storing data; however, its really intended for Oracle. That said, its easy to adapt to your needs.
My suggestion is to not fight WCF constraints and possibly making your solution more complex than necessary. Instead, try using code generators or rollout your own to generate the numerous service contracts your application requires.
In your current implementation, you do not have your OperationContract attributes set on your contract interface.
Try something like this:
public abstract class DbRepository : IDbRepository
{
[OperationalContract(Name="Insert")]
public TEntity Insert<TEntity>(TEntity entity) where TEntity : class
{
_context.Entry(entity).State = EntityState.Added;
return entity;
}
[OperationalContract(Name="Update")]
public TEntity Update<TEntity>(TEntity entity) where TEntity : class
{
_context.Entry(entity).State = EntityState.Modified;
return entity;
}
}
It probably seems redundant, but I believe the generics mess with operational names accidentally and you are required to specify them.
Will WCF generate a WSDL for that contract and allow you to host the service? Is the problem you have just down to serialization and known types? If so you may want to look at the SharedTypeResolver in this blog post. It's a pretty simple and awesome piece of magic that allows you to transparently pass any subclass of a data contract without having to declare it, so long as the type is shared between both client and server.
You could then dispense with the generics and simply talk about things as TEntity. Internally in the service you could map the call to your generic service implementations; think of the WCF service as a non-generic facade to expose your generic classes. The caller will know what type to expect because they gave it to you in the first place, so could cast. You could provide a client that puts a generic wrapper around this if casting offends.
Since you are using a BasicHttpBinding, I am going to assume you are sending this over the web. I'm also going to assume that you are using SOAP/XML. If this is the case try something like this:
[ServiceContract]
public interface IDbRepository
{
[OperationContract]
XElement Insert(XElement entity);
[OperationContract]
XElement Update(XElement entity);
}
Now all you have to do is parse the XML you receive and return whatever XML you see fit! I did something similar where I had an abstract base class that has 2 methods one to generate XML to represent the object and one to parse XML to populate the object's properties. One drawback to this is that your interface implementation will still need to know about all the types of objects in the class hierarchy.