在EF Core 3.1中使用可选的返回列执行存储过程

时间:2020-09-06 05:02:46

标签: c# stored-procedures entity-framework-core ef-core-3.1

我正在使用EF Core 3.1,并且具有以下存储过程调用:

public async Task<string> UserSessionGet(string user, string password, string ip)
{
    IList<SessionItem> lst = null;

    try
    {
        // Processing.  
        string sqlQuery = "EXEC [dbo].[GetUserSession] @UserLogin, @UserPassword, @IP, @ErrorCode OUTPUT, @ErrorText OUTPUT";
        int errorCode = 0;
        string errorText = string.Empty;
        lst = await this.Set<SessionItem>().FromSqlRaw(sqlQuery,
            new SqlParameter("@UserLogin", user ?? (object)DBNull.Value),
            new SqlParameter("@UserPassword", password ?? (object)DBNull.Value),
            new SqlParameter("@IP", ip ?? (object)DBNull.Value),
            new SqlParameter("@ErrorCode", errorCode) { Direction = ParameterDirection.Output},
            new SqlParameter("@ErrorText", errorText) { Direction = ParameterDirection.Output }
            ).ToListAsync();
    }
    catch (Exception ex)
    {
        throw ex;
    }

    // Info.  
    return lst.FirstOrDefault()?.Session;
}

实体:

public class SessionItem
{
    [NotMapped]
    public string Session { get; set; }
}

设置:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<SessionItem>().HasNoKey();
}

当我尝试执行此代码时,会引发异常:

序列不包含任何元素

当我在失败登录尝试中删除NotMapped注释时,我会收到异常

“ FromSql”操作的结果中没有所需的“会话”列

当用户输入错误的密码或用户名时,仅返回@ErrorCode@ErrorText,但根本没有数据。

https://i.ibb.co/pf4KzHz/Failure-Login.png

成功调用后,仅返回一列,称为Session

https://i.ibb.co/tL3CR1H/2020-09-06-07-57-12.png

我该怎么办?

我还有另一个存储过程,具有不同的列集和相同的行为。

1 个答案:

答案 0 :(得分:0)

我写了自己的扩展类:

    public static class DataModelExtensions
    {
        public static DbCommand LoadStoredProc(
          this DbContext context, string storedProcName)
        {
            var cmd = context.Database.GetDbConnection().CreateCommand();
            cmd.CommandText = storedProcName;
            cmd.CommandType = System.Data.CommandType.StoredProcedure;
            return cmd;
        }

        public static DbCommand LoadSql(this DbContext context, string sql)
        {
            var cmd = context.Database.GetDbConnection().CreateCommand();
            cmd.CommandText = sql;
            cmd.CommandType = System.Data.CommandType.Text;
            return cmd;
        }

        public static DbCommand WithSqlParam(this DbCommand cmd, string paramName, object paramValue, ParameterDirection direction = ParameterDirection.Input, int ?size = null)
        {
            if (string.IsNullOrEmpty(cmd.CommandText))
                throw new InvalidOperationException(
                  "Call LoadStoredProc before using this method");
            var param = cmd.CreateParameter();
            param.ParameterName = paramName;
            param.Value = paramValue;
            param.Direction = direction;
            if (size.HasValue)
                param.Size = size.Value;
            cmd.Parameters.Add(param);
            return cmd;
        }

        private static List<T> MapToList<T>(this DbDataReader dr, int? errorCode, string errorText)
        {
            var objList = new List<T>();
            var props = typeof(T).GetRuntimeProperties();

            var colMapping = dr.GetColumnSchema()
              .Where(x => props.Any(y => y.Name.ToLower() == x.ColumnName.ToLower()))
              .ToDictionary(key => key.ColumnName.ToLower());

            int rowNumber = 1;

            T obj = Activator.CreateInstance<T>();
            
            if (dr.HasRows)
            {
                while (dr.Read())
                {
                    obj = Activator.CreateInstance<T>();
                    foreach (var prop in props)
                    {
                        if (colMapping.ContainsKey(prop.Name.ToLower()))
                        {
                            object val;
                            var propName = prop.Name.ToLower();
                            val = dr.GetValue(colMapping[propName].ColumnOrdinal.Value);
                            prop.SetValue(obj, val == DBNull.Value ? null : val);
                        }
                    }
                    rowNumber++;
                    objList.Add(obj);
                }
            }
            else
            {
                foreach (var prop in props)
                {
                    var propName = prop.Name;
                    if (propName == "ErrorCode")
                        prop.SetValue(obj, errorCode);
                    else if (propName == "ErrorText")
                        prop.SetValue(obj, errorText);
                }
                objList.Add(obj);
            }
            return objList;
        }

        public static async Task<List<T>> Execute<T>(this DbCommand command)
        {
            using (command)
            {
                //System.Diagnostics.Debug.WriteLine(command.ToLog());
                if (command.Connection.State == System.Data.ConnectionState.Closed)
                    command.Connection.Open();
                try
                {
                    using (var reader = await command.ExecuteReaderAsync())
                    {
                        return reader.MapToList<T>((int?)command.Parameters["@ErrorCode"].Value, (string)command.Parameters["@ErrorText"].Value);
                    }
                }
                catch (Exception e)
                {
                    throw (e);
                }
                finally
                {
                    command.Connection.Close();
                }
            }
        }

        public static async Task<bool> HasRows(this DbCommand command)
        {
            using (command)
            {
                if (command.Connection.State == System.Data.ConnectionState.Closed)
                    command.Connection.Open();
                try
                {
                    using (var reader = await command.ExecuteReaderAsync())
                    {
                        return reader.HasRows;
                    }
                }
                catch (Exception e)
                {
                    throw (e);
                }
                finally
                {
                    command.Connection.Close();
                }
            }
        }

        public static async Task<bool> QueryHasRows(this DbContext context, string sql)
        {
            return await context.LoadSql(sql).HasRows();
        }

        public static string ToLog(this DbCommand command)
        {
            //-- @p5: Input Int (Size = -1; Prec = 0; Scale = 0) [194]
            StringBuilder sb = new StringBuilder();
            sb.AppendLine();
            sb.AppendLine(command.CommandText);
            foreach(DbParameter parameter in command.Parameters)
            {
                if(parameter.Value != null && !string.IsNullOrEmpty(parameter.Value.ToString()))
                {
                    sb.AppendLine($"{parameter.ParameterName}={parameter.Value}");
                    //sb.AppendLine($"-- {parameter.ParameterName}: {parameter.Direction} {parameter.DbType} (Size = {parameter.Size}; Prec = {parameter.Precision}; Scale = {parameter.Scale}) [{parameter.Value}]");
                }
            }
            return sb.ToString();
        }
    }

使用示例:

        public async Task<string> UserSessionGet(string user, string password, string ip)
        {
            //SessionItem lst = null;
            IList<SessionItem> lst = null;
            try
            {
                int errorCode = 0;
                string errorText = string.Empty;

                List<SessionItem> result = new List<SessionItem>();
                result = await this.LoadStoredProc("UserSessionGet")
                .WithSqlParam("@UserLogin", user)
                .WithSqlParam("@UserPassword", password)
                .WithSqlParam("@IP", ip)
                .WithSqlParam("@ErrorCode", errorCode, ParameterDirection.Output)
                .WithSqlParam("@ErrorText", errorText, ParameterDirection.Output, 255)
                .Execute<SessionItem>();
                if (result.Count == 1)
                {
                    return result[0].Session;
                }
                //return result.FirstOrDefault(x => x.iscurrent.ToLower() == "да");
                return string.Empty;
            }
            catch (Exception ex)
            {
                throw ex;
            }

       }