Implementing an SQL wrapper with entity mapping - c#

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;
}
}

Related

C# convert list to Generic List

I am using vs2022 and C# language and oracle as backend. oracle procedure gets input as facility type and based on type it returns the list of data and each facility type have common columns with few additional columns per type. on the below code, MaptoList is an extension class method which maps the reader column and value to class. the below logic is not working and says cannot convert from non generic list to generic. please help me to solve the compile error.
public class Common
{
public string Name { get; set; }
public string Address { get; set; }
public string State{ get; set; }
}
public class Employee : Common
{
public string SSN{ get; set; }
}
public class Vendor : Common
{
public string TaxNumber{ get; set; }
}
public async Task<List<T>> GetFacility(string Facility_Type)
{
using (EMPDB dbConnect = new EMPDB())
{
DbCommand dbCommand = dbConnect.Database.GetDbConnection().CreateCommand();
dbCommand.CommandType = CommandType.StoredProcedure;
dbCommand.CommandText = "GetFacilityReport";
dbCommand.BuildSqlParameter("Facility_Type", OracleDbType.Varchar2, Facility_Type, ParameterDirection.Input);
List<T> lstFacility_Response= new List<T>();
if (dbCommand.Connection.State != ConnectionState.Open)
{
dbCommand.Connection.Open();
}
using (var reader = await dbCommand.ExecuteReaderAsync())
{
switch (Facility_Type)
{
case "Employee":
lstFacility_Response= reader.MapToList<Employee>();
break;
case "Vendor":
lstFacility_Response= reader.MapToList<Vendor>();
break;
}
}
}
return lstFacility_Response
}
public static List<T> MapToList<T>(this DbDataReader dr) where T : new()
{
List<T> RetVal = null;
var Entity = typeof(T);
var PropDict = new Dictionary<string, PropertyInfo>();
try
{
if (dr != null && dr.HasRows)
{
RetVal = new List<T>();
var Props = Entity.GetProperties(BindingFlags.Instance | BindingFlags.Public);
PropDict = Props.ToDictionary(p => p.Name.ToUpper(), p => p);
while (dr.Read())
{
T newObject = new T();
for (int Index = 0; Index < dr.FieldCount; Index++)
{
if (PropDict.ContainsKey(dr.GetName(Index).ToUpper()))
{
var Info = PropDict[dr.GetName(Index).ToUpper()];
if ((Info != null) && Info.CanWrite)
{
var Val = dr.GetValue(Index);
Info.SetValue(newObject, (Val == DBNull.Value) ? null : Val, null);
}
}
}
RetVal.Add(newObject);
}
}
}
catch (Exception)
{
throw;
}
return RetVal;
}
If generics are appropriate here then this:
public async Task<List<T>> GetFacility(string Facility_Type)
should be this:
public async Task<List<T>> GetFacility<T>()
and then your switch statement should be replaced with this:
lstFacility_Response = reader.MapToList<T>();
You would then call it something like so:
var list = await GetFacility<Employee>();
When declaring the method, the method itself has to be generic and then you should be able to simply use the generic type parameter as a type in the method. If you can't do that then the method almost certainly shouldn't be generic in the first place.
If you want to limit T to be only type Common or its derived types then you can add a constraint:
public async Task<List<T>> GetFacility<T>() where T : Common
Given that your MapToList method has the new constraint, this method would have to as well, in order to call MapToList<T>():
public async Task<List<T>> GetFacility<T>() where T : Common, new()

Generic function to get data from SqlDataReader

I'm creating a generic application to get all the data from different SQL Server tables.
I have a generic function that convert the SqlDataReader to a list:
public static List<T> MapToList<T>(this SqlDataReader dr) where T : new()
{
List<T> RetVal = null;
var Entity = typeof(T);
var PropDict = new Dictionary<string, PropertyInfo>();
try
{
if (dr != null && dr.HasRows)
{
RetVal = new List<T>();
var Props = Entity.GetProperties(BindingFlags.Instance | BindingFlags.Public);
PropDict = Props.ToDictionary(p => p.Name.ToUpper(), p => p);
while (dr.Read())
{
T newObject = new T();
for (int Index = 0; Index < dr.FieldCount; Index++)
{
if (PropDict.ContainsKey(dr.GetName(Index).ToUpper()))
{
var Info = PropDict[dr.GetName(Index).ToUpper()];
if ((Info != null) && Info.CanWrite)
{
var Val = dr.GetValue(Index);
Info.SetValue(newObject, (Val == DBNull.Value) ? null : Val, null);
}
}
}
RetVal.Add(newObject);
}
}
}
catch (Exception)
{
throw;
}
return RetVal;
}
Now suppose I have this class for my data:
public partial class User
{
public int Id { get; set; }
public string Name { get; set; }
}
I can fetch my data from the table something like this:
const string GetAreasQuery = "select id, name from dbo.user";
SqlDataReader dr = DoQueryToDB(GetAreasQuery);
List<User> userList = dr.MapToList<User>();
Now, I have n different classes like User (Classroom and so on) and I don't want to write the code above for each class I have. I would like to create a generic GetData to retrieve those information:
public List<T> GetData<T> (string Query_)
{
SqlDataReader dr = DataReader(Query_);
List<T> data = new List<T>();
data = dr.MapToList<T>();
return data;
}
where T can be User, Classroom and so on...
I tried this solution but I always have to specify the type:
public object GetData(string Query_, Type type)
{
SqlDataReader dr = DataReader(Query_);
if (type == typeof(User))
{
List<User> data = new List<User>();
data = dr.MapToList<User>();
return data;
}
else if (..)
{}
return null;
}
I'm trying different possibilities but I always obtain an error in GetData<T> function. More precisely in MapToList<T> like: T should be a non abstract type or public constructor without parameters.
You should add a contraint to the method GetData in order to achieve the same constraint level that is found in MapToList, which requires T to have a empty constructor
public List<T> GetData<T>(string Query_) where T : new()
{
SqlDataReader dr = DataReader(Query_);
return dr.MapToList<T>();
}

How to instantiate and set properties of dynamic type using System.Linq.Expressions

I'm writing an SQL Data Mapper that would map queries to dynamic types (I've already experienced mapping sql to strongly typed objects through reflection)
This time I want to make a sql call then return an Ienumerable.
This is what I have so far:
public class DynamicMapper
{
private class ReaderColumnMap
{
public string Column { get; }
public Type Type { get; }
public ReaderColumnMap(string column, Type type)
{
Column = column;
Type = type;
}
}
private Func<IDataReader, dynamic> MappingMethod;
public DynamicMapper(IDataReader schemaSource)
{
MappingMethod = CreateMapper(schemaSource);
}
private Func<IDataReader, dynamic> CreateMapper(IDataReader reader)
{
IDictionary<int, ReaderColumnMap> propertyMappingsCache = new Dictionary<int, ReaderColumnMap>();
//The schema of sql table involves the column name and data type for each columns.
DataTable schema = reader.GetSchemaTable();
DataColumnCollection dataColumns = schema.Columns;
//Gets the column name ordinal and data type ordinal
int nameColumn = dataColumns["ColumnName"].Ordinal;
int typeColumn = dataColumns["DataType"].Ordinal;
int ordinalColumn = dataColumns["ColumnOrdinal"].Ordinal;
DataRowCollection schemaRows = schema.Rows;
int schemaRowCount = schemaRows.Count - 1;
//Populates the property mappings
for (int i = schemaRowCount; i != -1; --i)
{
DataRow row = schemaRows[i];
string name = row[nameColumn].ToString();
Type type = Type.GetType(row[typeColumn].ToString());
int ordinal = Convert.ToInt32(row[ordinalColumn]);
propertyMappingsCache.Add(ordinal, new ReaderColumnMap(name, type));
}
//Use expression tree here to create dynamic object (new() { Prop1 = "prop1", Prop2 = prop2... })
//Dictionary sample:
//Id = System.Int64
//Name = System.String
//Birthdate = System.DateTime
//Target Expression:
//dynamic instance = new (){
// Id = (System.Int64)reader[0];
// Name = (System.String)reader[1];
// Birthdate = (System.DateTime)reader[2];
//}
var expressions = new List<Expression>();
var fxParameterExpression = Expression.Parameter(typeof(IDataReader), "reader");
//Compiler error: the typeof operator cannot be used on the dynamic type
var variableExpression = Expression.Variable(typeof(dynamic), "instance"); //dynamic instance = new ()
//Compiler error: the typeof operator cannot be used on the dynamic type
var instantiationExpression = Expression.New(typeof(dynamic));
var assignExpression = Expression.Assign(variableExpression, instantiationExpression);
var indexer = typeof(IDataReader).GetProperty("Item", new[] { typeof(int) }); // []
expressions.Add(assignExpression);
int propertyCount = propertyMappingsCache.Count - 1;
for(int i = propertyCount; i != -1; --i)
{
//Get property details from mappingsCache (string property name and Type of property)
//to create property
}
return null;
}
public dynamic CreateMappedInstance(IDataReader reader)
{
return MappingMethod.Invoke(reader);
}
}
But the compiler is not letting me to instantiate dynamic type: the typeof operator cannot be used on the dynamic type
I plan to call it in sql module like:
public class SqlCaller
{
private readonly ISqlProvider sqlProvider;
public SqlCaller(ISqlProvider sqlProvider)
{
this.sqlProvider = sqlProvider ?? throw new ArgumentNullException("sqlProvider");
}
public DataTable Query(string queryString)...
public DataTable Query(DbCommand command)...
public DataTable GetSchema(string queryString)...
public int ExecuteNonQuery(DbCommand command)...
public int ExecuteNonQuery(string commandString)...
public object ExecuteScalar(string queryString)...
public object ExecuteScalar(DbCommand command)...
public bool ExecuteTransaction(IEnumerable<DbCommand> commands)...
[Obsolete]
public IEnumerable<T> Get<T>(Func<IDataReader, List<T>> mappingMethod, string query)...
[Obsolete]
public IEnumerable<T> Get<T>(Func<IDataReader, List<T>> mappingMethod, DbCommand command)...
public IEnumerable<T> Get<T>(string query) where T : class, new()...
public IEnumerable<T> Get<T>(DbCommand command) where T : class, new()...
public IEnumerable<T> Get<T>(IDataMapper<T> dataMapper, DbCommand command) where T : class, new()...
public IEnumerable<dynamic> GetDynamic(string commandString)
{
return GetDynamic(sqlProvider.CreateCommand(commandString));
}
public IEnumerable<dynamic> GetDynamic(DbCommand command)
{
List<dynamic> temp = new List<dynamic>();
using (DbConnection connection = sqlProvider.CreateConnection())
{
command.Connection = connection;
try
{
command.Connection.Open();
IDataReader reader = command.ExecuteReader();
var watch = Stopwatch.StartNew();
DynamicMapper mapper = new DynamicMapper(reader);
while(reader.Read()) temp.Add(mapper.CreateMappedInstance(reader));
watch.Stop();
}
catch
{
throw;
}
finally
{
command.Connection.Close();
}
}
return temp;
}
}
How to instantiate a dynamic type inside an expression tree
How to set property values of a dynamic type inside an expression tree

Entity Framework Core - Mapping Raw Query(DTO Class without ID field) , Scalar

I am using EFCore with ASPNETCore, everything looks great, but I find these two situations:
I have created a DTO classes that are not tables in the database, these classes that will map a raw SQL query or procedure.
So I need to map the results of an SQL procedure in a list classs DTO, the question I have is if EFCore requires that all classes of DTO have a field named ID?, for more than my procedure does not necessarily have that field ?, there a way to specify to EFCore that my query need not map any field named ID?.
Likewise, what it is the correct way to map a scalar value or a unique result of a raw SQL query or procedure?
Thank you very much for your support.
Here is a class that will solve your needs.
Usage var listOfEntity = DatabaseServices.ExecuteStoredProcedure(Stored Procedure Name, List of SqlParameter)
public class DatabaseServices : IDatabaseServices
{
private readonly ApplicationDbContext _applicationDbContext;
public DatabaseServices(ApplicationDbContext applicationDbContext)
{
_applicationDbContext = applicationDbContext;
}
public List<T> ExecuteStoreProcedure<T>(string storedProcedure, List<SqlParameter> parameters) where T : new()
{
using (var cmd = _applicationDbContext.Database.GetDbConnection().CreateCommand())
{
cmd.CommandText = storedProcedure;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandTimeout = 300;
// set some parameters of the stored procedure
foreach (var parameter in parameters)
{
parameter.Value = parameter.Value ?? DBNull.Value;
cmd.Parameters.Add(parameter);
}
if (cmd.Connection.State != ConnectionState.Open)
cmd.Connection.Open();
using (var dataReader = cmd.ExecuteReader())
{
var test = DataReaderMapToList<T>(dataReader);
return test;
}
}
}
private static List<T> DataReaderMapToList<T>(DbDataReader dr)
{
List<T> list = new List<T>();
if (dr.HasRows)
{
while (dr.Read())
{
var obj = Activator.CreateInstance<T>();
foreach (PropertyInfo prop in obj.GetType().GetProperties())
{
if (!Equals(dr[prop.Name], DBNull.Value))
{
prop.SetValue(obj, dr[prop.Name], null);
}
}
list.Add(obj);
}
return list;
}
return new List<T>();
}
}

DBMS-independent queries

My masters thesis is about discovering bad database design by analyzing metadata and the data stored. We do this by extracting a metadata model from a given DBMS and then running a set of rules on this metadata.
To extend this process with data analysis, we need to allow rules to query the database directly, but we must retain DBMS independence, such that queries can be applied to PostgreSQL, MSSQL and MySQL.
We have discussed a sort of functional construction of queries such as:
new Query(new Select(columnID), new From(tableID), new Where(new Equality(columnID1, columnID2)))
And then using a DBMS-specific serializer.
Another approach is to let rules handle it all by themselves:
public Query QueryDatabase(DBMS dbms)
{
if (dbms == PostgreSQL) { return "select count(1) from Users"}
if (dbms == MSSQL) {return ....}
}
Are we missing something? Does all this in fact exist in a nice library somewhere? And yes, we have looked at Entity frameworks, but they seem to rely on a statically types model of the database, which for obvious reasons cannot be created.
I should mention that we maintain an extensible rule architecture, allowing end users to implement their own rules.
To clarify what we want to achieve, look at the following query (mssql), it needs two parameters, the name of the table (#table) and the name of the column (#column):
DECLARE #TotalCount FLOAT;
SELECT #TotalCount = COUNT(1) FROM [#table];
SELECT SUM(pcount * LOG10(#TotalCount / pcount)) / (LOG10(2) * #TotalCount)
FROM (SELECT (Count([#column])) as pcount
FROM [#table]
GROUP BY [#column]) as exp1
The query measures the amount of information stored in a given attribute, by estimating the entropy. It needs to access all rows in the table. To avoid extracting all rows from the database and transferring them over a slow network connection it is better to express them in SQL an only transfer a single number.
NOTE: We DO have all the metadata we need. This question is only for accessing data!
I was not very sure of whether to add this to my already long question, edit an existing answer or what todo. Please feel free to advise. ;)
Building on mrnye answer:
new Query()
.Variable(varname => FLOAT)
.Set(varname => new Query().Count(1).From(table) )
.Select(new Aggregate().Sum(varname => "pcount * LOG10(varname / pcount)"))
.From(
new Query()
.Select(pcount => new Aggregate().Count(column)
.From(table)
.GroupBy(column)
)
Syntax errors and misuse of lambda statements aside, i played with the idea of using some extension methods for building queries. It does seem as a fairly complex approach. How would you think about such an approach?
Building on the LINQ answer:
let totalCount = Table.Count
from uv un from r in Table
group r by r["attr"]
select r.Count
select r.Count * Log2((totalCount / r.Count))
Seems fairly nice, but a helluva lot to implement...
You could achieve the same by implementing a custom LINQ provider infrastructure. The queries are generic, but the AST tree visitors that generate the SQL queries can be made pluggable. You can even mock a database using a in memory data store and translating your custom LINQ query to a LINQ to objects query!
You would need to create a provider that would know how to extract the column name from the object's indexer. Here is a basic framework that you can extend:
// Runs in LinqPad!
public class TableQueryObject
{
private readonly Dictionary<string, object> _data = new Dictionary<string, object>();
public string TableName { get; set; }
public object this[string column]
{
get { return _data.ContainsKey(column) ? _data[column] : null; }
set { if (_data.ContainsKey(column)) _data[column] = value; else _data.Add(column, value); }
}
}
public interface ITableQuery : IEnumerable<TableQueryObject>
{
string TableName { get; }
string ConnectionString { get; }
Expression Expression { get; }
ITableQueryProvider Provider { get; }
}
public interface ITableQueryProvider
{
ITableQuery Query { get; }
IEnumerable<TableQueryObject> Execute(Expression expression);
}
public interface ITableQueryFactory
{
ITableQuery Query(string tableName);
}
public static class ExtensionMethods
{
class TableQueryContext : ITableQuery
{
private readonly ITableQueryProvider _queryProvider;
private readonly Expression _expression;
public TableQueryContext(ITableQueryProvider queryProvider, Expression expression)
{
_queryProvider = queryProvider;
_expression = expression;
}
public string TableName { get { return _queryProvider.Query.TableName; } }
public string ConnectionString { get { return _queryProvider.Query.ConnectionString; } }
public Expression Expression { get { return _expression; } }
public ITableQueryProvider Provider { get { return _queryProvider; } }
public IEnumerator<TableQueryObject> GetEnumerator() { return Provider.Execute(Expression).GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
public static MethodInfo MakeGeneric(MethodBase method, params Type[] parameters)
{
return ((MethodInfo)method).MakeGenericMethod(parameters);
}
public static Expression StaticCall(MethodInfo method, params Expression[] expressions)
{
return Expression.Call(null, method, expressions);
}
public static ITableQuery CreateQuery(this ITableQueryProvider source, Expression expression)
{
return new TableQueryContext(source, expression);
}
public static IEnumerable<TableQueryObject> Select<TSource>(this ITableQuery source, Expression<Func<TSource, TableQueryObject>> selector)
{
return source.Provider.CreateQuery(StaticCall(MakeGeneric(MethodBase.GetCurrentMethod(), typeof(TSource)), source.Expression, Expression.Quote(selector)));
}
public static ITableQuery Where(this ITableQuery source, Expression<Func<TableQueryObject, bool>> predicate)
{
return source.Provider.CreateQuery(StaticCall((MethodInfo)MethodBase.GetCurrentMethod(), source.Expression, Expression.Quote(predicate)));
}
}
class SqlTableQueryFactory : ITableQueryFactory
{
class SqlTableQuery : ITableQuery
{
private readonly string _tableName;
private readonly string _connectionString;
private readonly ITableQueryProvider _provider;
private readonly Expression _expression;
public SqlTableQuery(string tableName, string connectionString)
{
_connectionString = connectionString;
_tableName = tableName;
_provider = new SqlTableQueryProvider(this);
_expression = Expression.Constant(this);
}
public IEnumerator<TableQueryObject> GetEnumerator() { return Provider.Execute(Expression).GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
public string TableName { get { return _tableName; } }
public string ConnectionString { get { return _connectionString; } }
public Expression Expression { get { return _expression; } }
public ITableQueryProvider Provider { get { return _provider; } }
}
class SqlTableQueryProvider : ITableQueryProvider
{
private readonly ITableQuery _query;
public ITableQuery Query { get { return _query; } }
public SqlTableQueryProvider(ITableQuery query) { _query = query; }
public IEnumerable<TableQueryObject> Execute(Expression expression)
{
//var connecitonString = _query.ConnectionString;
//var tableName = _query.TableName;
// TODO visit expression AST (generate any sql dialect you want) and execute resulting sql
// NOTE of course the query can be easily parameterized!
// NOTE here the fun begins, just return some dummy data for now :)
for (int i = 0; i < 100; i++)
{
var obj = new TableQueryObject();
obj["a"] = i;
obj["b"] = "blah " + i;
yield return obj;
}
}
}
private readonly string _connectionString;
public SqlTableQueryFactory(string connectionString) { _connectionString = connectionString; }
public ITableQuery Query(string tableName)
{
return new SqlTableQuery(tableName, _connectionString);
}
}
static void Main()
{
ITableQueryFactory database = new SqlTableQueryFactory("SomeConnectionString");
var result = from row in database.Query("myTbl")
where row["someColumn"] == "1" && row["otherColumn"] == "2"
where row["thirdColumn"] == "2" && row["otherColumn"] == "4"
select row["a"]; // NOTE select executes as linq to objects! FTW
foreach(var a in result)
{
Console.WriteLine(a);
}
}
I think the LINQ route is the way to go, but for fun I tried to think of a solution. It needs some work, but the general idea is to have the query interface fluent and hide the implementation logic behind interfaces. Just throwing it out there as food for thought...
public interface IDBImplementation
{
public void ProcessQuery(Select query);
}
public class SqlServerImplementation : IDBImplementation
{
public void ProcessQuery(Select query)
{
string sqlQuery = "SELECT " + String.Join(", ", query.Columns)
+ " FROM " + query.TableName + " WHERE " + String.Join(" AND ", query.Conditions);
// execute query...
}
}
public class Select
{
public Select(params string[] columns)
{
Columns = columns;
}
public string[] Columns { get; set; }
public string TableName { get; set; }
public string[] Conditions { get; set; }
}
public static class Extensions
{
public static Select From(this Select select, string tableName)
{
select.TableName = tableName;
return select;
}
public static Select Where(this Select select, params string[] conditions)
{
select.Conditions = conditions;
return select;
}
}
public static class Main
{
public static void Example()
{
IDBImplementation database = new SqlServerImplementation();
var query = new Select("a", "b", "c").From("test").Where("c>5", "b<10");
database.ProcessQuery(query);
}
}
The most DBMS-independent way to retrieve information about a database is iNFORMATION_SCHEMA. See MySQL, SQL Server, PostgreSQL.
I'm kind of curious what type of rules you're thinking of. :)
What about NHibernate? http://community.jboss.org/wiki/NHibernateforNET

Categories

Resources