我理解这个需求不能直接满足,可能需要编写特定的帮助程序。我使用自定义基础抽象类TypeMap
来解决这个问题,可以通过所有类型的提供程序进行扩展,以实现API,这些API不能使用Dapper直接实现。下面是我针对SQL-Server的实现,其他符合ADO.Net标准的提供程序也可以采取类似方法:
namespace Dapper
{
#region NameSpaces
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
#endregion
internal abstract class TypeMap
{
public abstract Dictionary<string, object> NonInputParameterCollection { get; set; }
public abstract int Execute(IDbConnection connection,
string sql,
CommandType commandType,
IEnumerable<DapperParam> dapperParams );
public abstract IEnumerable<T> Query<T>(IDbConnection connection,
string sql,
CommandType commandType,
IEnumerable<DapperParam> dapperParams) where T : new();
public static TypeMap GetTypeMap(string provider)
{
TypeMap typeMap = null;
switch (provider)
{
case "System.Data.SqlClient":
typeMap = new SqlTypeMap();
break;
default:
typeMap = new SqlTypeMap();
break;
}
return (typeMap);
}
}
internal class SqlTypeMap : TypeMap
{
public SqlTypeMap()
{
NonInputParameterCollection = new Dictionary<string, object>();
}
public override sealed Dictionary<string, object> NonInputParameterCollection { get; set; }
public override int Execute(IDbConnection connection,
string sql,
CommandType commandType,
IEnumerable<DapperParam> dapperParams)
{
int returnValue = -1;
var sqlConnection = (connection as SqlConnection) ?? new SqlConnection();
using (sqlConnection)
{
SqlCommand sqlCommand = null;
sqlCommand = sqlConnection.CreateCommand();
using (sqlCommand)
{
foreach (var param in dapperParams)
{
sqlCommand.Parameters.Add(new SqlParameter
{
ParameterName = param.ParamName,
SqlValue = param.ParamValue ?? DBNull.Value,
SqlDbType = TypeToSqlDbType[param.ParamType],
Direction = Map.DirectionMap[param.ParamDirection]
});
}
sqlCommand.CommandText = sql;
sqlCommand.CommandType = commandType;
sqlCommand.Connection.Open();
returnValue = sqlCommand.ExecuteNonQuery();
foreach (SqlParameter param in sqlCommand.Parameters.Cast<SqlParameter>().Where(param => param.Direction != ParameterDirection.Input))
NonInputParameterCollection.Add(param.ParameterName, param.Value);
}
}
return (returnValue);
}
public override IEnumerable<T> Query<T>(IDbConnection connection,
string sql,
CommandType commandType,
IEnumerable<DapperParam> dapperParams)
{
IEnumerable<T> returnEnumerable = null;
var sqlConnection = (connection as SqlConnection) ?? new SqlConnection();
using (sqlConnection)
{
var sqlCommand = sqlConnection.CreateCommand();
using (sqlCommand)
{
foreach (var param in dapperParams)
{
sqlCommand.Parameters.Add(new SqlParameter
{
ParameterName = param.ParamName,
SqlValue = param.ParamValue ?? DBNull.Value,
SqlDbType = TypeToSqlDbType[param.ParamType],
Direction = Map.DirectionMap[param.ParamDirection]
});
}
sqlCommand.CommandText = sql;
sqlCommand.CommandType = commandType;
var sqlDataAdapter = new SqlDataAdapter(sqlCommand);
var returnDataTable = new DataTable();
sqlDataAdapter.Fill(returnDataTable);
returnEnumerable = Common.ToList<T>(returnDataTable);
foreach (SqlParameter param in sqlCommand.Parameters.Cast<SqlParameter>()
.Where(param => param.Direction != ParameterDirection.Input))
NonInputParameterCollection.Add(param.ParameterName, param.Value);
}
}
return (returnEnumerable);
}
public static readonly Dictionary<Type, SqlDbType> TypeToSqlDbType = new Dictionary<Type, SqlDbType>
{
{typeof (byte), SqlDbType.TinyInt},
{typeof (sbyte), SqlDbType.TinyInt},
{typeof (short), SqlDbType.SmallInt},
{typeof (ushort), SqlDbType.SmallInt},
{typeof (int), SqlDbType.Int},
{typeof (uint), SqlDbType.Int},
{typeof (long), SqlDbType.BigInt},
{typeof (ulong), SqlDbType.BigInt},
{typeof (float), SqlDbType.Float},
{typeof (double), SqlDbType.Float},
{typeof (decimal), SqlDbType.Decimal},
{typeof (bool), SqlDbType.Bit},
{typeof (string), SqlDbType.VarChar},
{typeof (char), SqlDbType.Char},
{typeof (Guid), SqlDbType.UniqueIdentifier},
{typeof (DateTime), SqlDbType.DateTime},
{typeof (DateTimeOffset), SqlDbType.DateTimeOffset},
{typeof (byte[]), SqlDbType.VarBinary},
{typeof (byte?), SqlDbType.TinyInt},
{typeof (sbyte?), SqlDbType.TinyInt},
{typeof (short?), SqlDbType.SmallInt},
{typeof (ushort?), SqlDbType.SmallInt},
{typeof (int?), SqlDbType.Int},
{typeof (uint?), SqlDbType.Int},
{typeof (long?), SqlDbType.BigInt},
{typeof (ulong?), SqlDbType.BigInt},
{typeof (float?), SqlDbType.Float},
{typeof (double?), SqlDbType.Float},
{typeof (decimal?), SqlDbType.Decimal},
{typeof (bool?), SqlDbType.Bit},
{typeof (char?), SqlDbType.Char},
{typeof (Guid?), SqlDbType.UniqueIdentifier},
{typeof (DateTime?), SqlDbType.DateTime},
{typeof (DateTimeOffset?), SqlDbType.DateTimeOffset},
{typeof (System.Data.Linq.Binary), SqlDbType.Binary},
{typeof (IEnumerable<>), SqlDbType.Structured},
{typeof (List<>), SqlDbType.Structured},
{typeof (DataTable), SqlDbType.Structured},
};
}
public static class Map
{
public static Dictionary<Type, DbType> TypeToDbType = new Dictionary<Type, DbType>()
{
{typeof (byte), DbType.Byte},
{typeof (sbyte), DbType.Byte},
{typeof (short), DbType.Int16},
{typeof (ushort), DbType.Int16},
{typeof (int), DbType.Int32},
{typeof (uint), DbType.Int32},
{typeof (long), DbType.Int64},
{typeof (ulong), DbType.Int64},
{typeof (float), DbType.Single},
{typeof (double), DbType.Double},
{typeof (decimal), DbType.Decimal},
{typeof (bool), DbType.Boolean},
{typeof (string), DbType.String},
{typeof (char), DbType.StringFixedLength},
{typeof (Guid), DbType.Guid},
{typeof (DateTime), DbType.DateTime},
{typeof (DateTimeOffset), DbType.DateTimeOffset},
{typeof (byte[]), DbType.Binary},
{typeof (byte?), DbType.Byte},
{typeof (sbyte?), DbType.Byte},
{typeof (short?), DbType.Int16},
{typeof (ushort?), DbType.Int16},
{typeof (int?), DbType.Int32},
{typeof (uint?), DbType.Int32},
{typeof (long?), DbType.Int64},
{typeof (ulong?), DbType.Int64},
{typeof (float?), DbType.Single},
{typeof (double?), DbType.Double},
{typeof (decimal?), DbType.Decimal},
{typeof (bool?), DbType.Boolean},
{typeof (char?), DbType.StringFixedLength},
{typeof (Guid?), DbType.Guid},
{typeof (DateTime?), DbType.DateTime},
{typeof (DateTimeOffset?), DbType.DateTimeOffset},
{typeof (System.Data.Linq.Binary), DbType.Binary}
};
public static readonly Dictionary<string, ParameterDirection> DirectionMap =
new Dictionary<string, ParameterDirection>(StringComparer.InvariantCultureIgnoreCase)
{
{ParamDirectionConstants.Input, ParameterDirection.Input},
{ParamDirectionConstants.Output, ParameterDirection.Output},
{ParamDirectionConstants.InputOutput, ParameterDirection.InputOutput},
{ParamDirectionConstants.ReturnValue, ParameterDirection.ReturnValue}
};
}
}
支持上述代码运行的类和API如下:
using System;
using System.Collections.Generic;
namespace Dapper
{
public class DapperParam
{
public DapperParam(string paramName,
Type paramType,
string paramDirection,
object paramValue)
{
ParamName = paramName;
ParamType = paramType;
ParamDirection = paramDirection;
ParamValue = paramValue;
}
public string ParamName { get; set; }
public Type ParamType { get; set; }
public string ParamDirection { get; set; }
public object ParamValue { get; set; }
}
internal static class DataConversionMap
{
private static object ConvertDbData(object obj, Func<object> func)
{
return (!Convert.IsDBNull(obj)) ? func() : null;
}
public static readonly Dictionary<Type, Func<object, object>> Map =
new Dictionary<Type, Func<object, object>>
{
{
typeof(Int16),
objectValue => ConvertDbData(objectValue, () => Convert.ToInt16(objectValue))
},
{
typeof(Int32),
objectValue => ConvertDbData(objectValue, () => Convert.ToInt32(objectValue))
},
{
typeof(Int64),
objectValue => ConvertDbData(objectValue, () => Convert.ToInt64(objectValue))
},
{
typeof(Boolean),
objectValue => ConvertDbData(objectValue, () => Convert.ToBoolean(objectValue))
},
{
typeof(string),
objectValue => ConvertDbData(objectValue, () => Convert.ToString(objectValue))
},
{
typeof(DateTime), objectValue =>
ConvertDbData(objectValue, () =>
{
DateTime dateTime = Convert.ToDateTime(objectValue);
if (dateTime.TimeOfDay.Equals(TimeSpan.Zero))
return dateTime.ToShortDateString();
return dateTime.ToString("MM/dd/yyyy HH:mm");
})
},
{
typeof(Byte),
objectValue => ConvertDbData(objectValue, () => Convert.ToByte(objectValue))
},
{
typeof(Double),
objectValue => ConvertDbData(objectValue, () => Convert.ToDouble(objectValue))
},
{
typeof(Decimal),
objectValue => ConvertDbData(objectValue, () => Convert.ToDecimal(objectValue))
},
{
typeof(TimeSpan),
objectValue => ConvertDbData(objectValue, () => TimeSpan.Parse(objectValue.ToString()))
},
{
typeof(Guid),
objectValue => ConvertDbData(objectValue, () => new Guid(objectValue.ToString()))
},
{
typeof(Byte[]),
objectValue => ConvertDbData(objectValue, () => (Byte[])(objectValue))
}
};
}
}
常用API
public static class Common
{
public static DataTable CreateTable<T>(this IEnumerable<T> collection)
{
var tableType = typeof(T);
var dataTable = new DataTable();
int columnCount = tableType.GetProperties().Count();
var columnNameMappingDictionary = new Dictionary<string, string>();
for (int counter = 0; counter < columnCount; counter++)
{
var propertyInfo = tableType.GetProperties()[counter];
var parameterAttribute = propertyInfo.GetParameterAttribute();
string columnName = (parameterAttribute != null) ? parameterAttribute.Name : propertyInfo.Name;
columnNameMappingDictionary.Add(propertyInfo.Name,
(parameterAttribute != null) ? parameterAttribute.Name : propertyInfo.Name);
dataTable.Columns.Add(columnName, tableType.GetProperties()[counter].PropertyType);
}
if (collection == null)
return dataTable;
foreach (var item in collection)
{
DataRow dataRow = dataTable.NewRow();
for (int counter = 0; counter < columnCount; counter++)
{
string columnName = columnNameMappingDictionary[tableType.GetProperties()[counter].Name];
dataRow[columnName] = item
.GetType().GetProperties()[counter]
.GetValue(item);
}
dataTable.Rows.Add(dataRow);
}
return (dataTable);
}
public static DataTable CreateTable(object paramValue)
{
Type tableType = paramValue.GetType().GetGenericArguments()[0];
var genericDataTable = new DataTable();
int fieldCount = tableType.GetProperties().Count();
for (int counter = 0; counter < fieldCount; counter++)
{
genericDataTable.Columns.Add(tableType.GetProperties()[counter].Name,
tableType.GetProperties()[counter].PropertyType);
}
foreach (var item in (IEnumerable)paramValue)
{
DataRow dataRow = genericDataTable.NewRow();
for (int counter = 0; counter < fieldCount; counter++)
{
string columnName = tableType.GetProperties()[counter].Name;
dataRow[columnName] = item
.GetType().GetProperties()[counter]
.GetValue(item);
}
genericDataTable.Rows.Add(dataRow);
}
return genericDataTable;
}
public static List<T> ToList<T>(DataTable dataTable) where T : new()
{
var convertedList = new List<T>();
foreach (DataRow row in dataTable.Rows)
{
var dataObject = new T();
foreach (DataColumn column in dataTable.Columns)
{
string fieldName = column.ColumnName;
var propertyInfo = dataObject.GetType()
.GetProperty(fieldName,
BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
propertyInfo = propertyInfo ?? Parameter.GetColumnAttribute(dataObject.GetType(), fieldName);
if (propertyInfo == null) continue;
var value = row[column];
propertyInfo.SetValue(dataObject,
DataConversionMap.Map[propertyInfo.PropertyType](value), null);
}
convertedList.Add(dataObject);
}
return (convertedList);
}
}