MVC 5 Entity Framework 6 Execute Stored Procedure

后端 未结 2 862
迷失自我
迷失自我 2021-02-06 15:46

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

2条回答
  •  轻奢々
    轻奢々 (楼主)
    2021-02-06 15:54

    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  }
    

提交回复
热议问题