I\'m stuck. I have an existing application with an extremely large database and extensive library of stored procedures and functions. All I want to do is use a DbContext to ex
This code is better than SqlQuery() because SqlQuery() doesn't recognise the [Column] attribute. Here it is on a silver platter.
public static class StoredProcedureExtensions {
///
/// Execute Stored Procedure and return result in an enumerable object.
///
/// Type of enumerable object class to return.
/// SQL query.
/// SQL parameters.
/// Determines whether to attach and track changes for saving. Defaults to true and entities will not be tracked and thus a faster call.
/// IEnumerable of entity type.
public static IEnumerable GetStoredProcedureResults(this DbContext dbContext, string query, Dictionary parameters, bool readOnly = true) where TEntity : class, new()
{
SqlParameter[] sqlParameterArray = DbContextExtensions.DictionaryToSqlParameters(parameters);
return dbContext.GetStoredProcedureResults(query, sqlParameterArray, readOnly);
}
///
/// Execute Stored Procedure and return result in an enumerable object.
///
/// Type of enumerable object class to return.
/// SQL query.
/// SQL parameters.
/// Determines whether to attach and track changes for saving. Defaults to true and entities will not be tracked and thus a faster call.
/// IEnumerable of entity type.
public static IEnumerable GetStoredProcedureResults(this DbContext dbContext, string commandText, SqlParameter[] sqlParameterArray = null, bool readOnly = true) where TEntity : class, new()
{
string infoMsg = commandText;
try
{
//---- For a better error message
if (sqlParameterArray != null)
{
foreach (SqlParameter p in sqlParameterArray)
{
infoMsg += string.Format(" {0}={1}, ", p.ParameterName, p.Value == null ? "(null)" : p.Value.ToString());
}
infoMsg = infoMsg.Trim().TrimEnd(',');
}
///////////////////////////
var reader = GetReader(dbContext, commandText, sqlParameterArray, CommandType.StoredProcedure);
///////////////////////////
///////////////////////////
List results = GetListFromDataReader(reader);
///////////////////////////
if(readOnly == false)
{
DbSet entitySet = dbContext.Set(); // For attaching the entities so EF can track changes
results.ForEach(n => entitySet.Attach(n)); // Add tracking to each entity
}
reader.Close();
return results.AsEnumerable();
}
catch (Exception ex)
{
throw new Exception("An error occurred while executing GetStoredProcedureResults(). " + infoMsg + ". Check the inner exception for more details.\r\n" + ex.Message, ex);
}
}
//========================================= Private methods
#region Private Methods
private static DbDataReader GetReader(DbContext dbContext, string commandText, SqlParameter[] sqlParameterArray, CommandType commandType)
{
var command = dbContext.Database.Connection.CreateCommand();
command.CommandText = commandText;
command.CommandType = commandType;
if (sqlParameterArray != null) command.Parameters.AddRange(sqlParameterArray);
dbContext.Database.Connection.Open();
var reader = command.ExecuteReader(CommandBehavior.CloseConnection);
return reader;
}
private static List GetListFromDataReader(DbDataReader reader) where TEntity : class, new()
{
PropertyInfo[] entityProperties = typeof(TEntity).GetProperties();
IEnumerable readerColumnNames = (reader.GetSchemaTable().Select()).Select(r => r.ItemArray[0].ToString().ToUpper()); // uppercase reader column names.
List propertyToColumnMappings = GetPropertyToColumnMappings(); // Maps the entity property names to the corresponding names of the columns in the reader
var entityList = new List(); // Fill this
while (reader.Read())
{
var element = Activator.CreateInstance();
foreach (var entityProperty in entityProperties)
{
MappingPropertyToColumn mapping = propertyToColumnMappings._Find(entityProperty.Name);
if (mapping == null) // This property has a [Not Mapped] attribute
{
continue; // Skip this one
}
var o = (object)reader[mapping.ColumnName]; // mapping must match all mapped properties to columns. If result set does not contain a column, then throw error like EF would.
bool hasValue = o.GetType() != typeof(DBNull);
if (mapping.IsEnum && hasValue) // Enum
{
entityProperty.SetValue(element, Enum.Parse(mapping.UnderlyingType, o.ToString()));
}
else
{
if (hasValue)
{
entityProperty.SetValue(element, ChangeType(o, entityProperty.PropertyType));
}
}
}
entityList.Add(element);
}
return entityList;
}
public static object ChangeType(object value, Type conversion)
{
var t = conversion;
if (t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
if (value == null)
{
return null;
}
t = Nullable.GetUnderlyingType(t);
}
return Convert.ChangeType(value, t);
}
private static List GetPropertyToColumnMappings() where TEntity : new()
{
var type = typeof(TEntity);
List databaseMappings = new List();
foreach (var entityProperty in type.GetProperties())
{
bool isEnum = entityProperty.PropertyType.IsEnum;
// [Not Mapped] Not Mapped Attribute
var notMapped = entityProperty.GetCustomAttributes(false).FirstOrDefault(attribute => attribute is NotMappedAttribute);
if (notMapped != null) // This property has a [Not Mapped] attribute
{
continue; // Skip this property
}
// Determine if property is an enum
Type underlyingType = null;
if (entityProperty.PropertyType.IsGenericType && entityProperty.PropertyType.GetGenericTypeDefinition().Equals(typeof(Nullable<>)))
{
underlyingType = Nullable.GetUnderlyingType(entityProperty.PropertyType); ;
if (underlyingType != null && underlyingType.IsEnum)
{
isEnum = true;
}
}
// [Column("tbl_columnname")] Column Name Attribute for mapping
var columnMapping = entityProperty.GetCustomAttributes(false).FirstOrDefault(attribute => attribute is ColumnAttribute);
if (columnMapping != null)
{
databaseMappings.Add(new MappingPropertyToColumn { PropertyName = entityProperty.Name, ColumnName = ((ColumnAttribute)columnMapping).Name.ToUpper(), IsEnum = isEnum, UnderlyingType = underlyingType }); // SQL case insensitive
}
else
{
databaseMappings._AddProperty(entityProperty.Name, entityProperty.Name, isEnum); // C# case sensitive
}
}
return databaseMappings;
}
//====================================== Class for holding column mappings and other info for each property
private class MappingPropertyToColumn
{
private string _propertyName;
public string PropertyName
{
get { return _propertyName; }
set { _propertyName = value; }
}
private string _columnName;
public string ColumnName
{
get { return _columnName; }
set { _columnName = value; }
}
private bool _isNullableEnum;
public bool IsEnum
{
get { return _isNullableEnum; }
set { _isNullableEnum = value; }
}
private Type _underlyingType;
public Type UnderlyingType
{
get { return _underlyingType; }
set { _underlyingType = value; }
}
}
//======================================================= List Extension methods
#region List Extension methods
private static bool _ContainsKey(this List list, string key) where T : MappingPropertyToColumn
{
return list.Any(x => x.PropertyName == key);
}
private static MappingPropertyToColumn _Find(this List list, string key) where T : MappingPropertyToColumn
{
return list.Where(x => x.PropertyName == key).FirstOrDefault();
}
private static void _AddProperty(this List list, string propertyName, string columnName, bool isEnum, Type underlyingType = null) where T : MappingPropertyToColumn
{
list.Add((T)new MappingPropertyToColumn { PropertyName = propertyName, ColumnName = columnName, IsEnum = isEnum, UnderlyingType = underlyingType }); // C# case sensitive
}
#endregion
#endregion }