简体   繁体   English

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

[英]Execute stored procedure with optional returned columns in EF Core 3.1

I am using EF Core 3.1 and I have this stored procedure call:我正在使用 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;
}

Entities:实体:

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

Settings:设置:

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

When I try to execute this code, an exception is thrown:当我尝试执行此代码时,抛出异常:

Sequence contains no elements序列不包含任何元素

When I remove NotMapped annotation on failure login attempt I receive exception当我在失败登录尝试中删除NotMapped注释时,我收到异常

The required column 'Session' was not present in the results of a 'FromSql' operation 'FromSql' 操作的结果中不存在所需的列 'Session'

When user enters incorrect password or username only @ErrorCode and @ErrorText are returned, but there is no data at all.当用户输入错误的密码或用户名时,只返回@ErrorCode@ErrorText ,但根本没有数据。

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

On success call only one column is returned and it's called Session .成功调用时只返回一列,它被称为Session

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

What shall I do?我该怎么办?

I have another stored procedure with different column set and same behaviour.我有另一个具有不同列集和相同行为的存储过程。

I wrote my own extension class:我写了自己的扩展类:

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

Using example:使用示例:

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

       }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在 EF Core 3.1 中包含 FromSqlRaw 和存储过程 - Include with FromSqlRaw and stored procedure in EF Core 3.1 Entity Framework Core 3.1 存储过程可选参数不起作用 - Entity Framework Core 3.1 stored procedure optional parameters not working EF Core 3.1:Sequence 不包含带有 FromSqlRaw 查询存储过程的元素 - EF Core 3.1: Sequence contains no elements with FromSqlRaw query a stored procedure 使用 EF Core 中返回的多个结果集映射存储过程 - Map stored procedure with multiple resultset returned in EF Core 如何执行在 EF 6 中返回可变列数的存储过程 - How to execute a stored procedure that returns variable number of columns in EF 6 与存储过程一起使用时的 FromSql 方法无法在 EF Core 3.1 中组合 - FromSql method when used with stored procedure cannot be composed in EF Core 3.1 如何从存储过程 EF Core 3.1 中获取多个 OUTPUT 参数 - How to get Multiple OUTPUT parameters from stored procedure EF Core 3.1 如何在 Dot Net Core 3.1 中使用 FromSqlInterpolated/Database.ExecuteSqlInterpolated 执行带输出参数的存储过程? - How to execute stored procedure with Output parameters using FromSqlInterpolated/Database.ExecuteSqlInterpolated in Dot Net Core 3.1? 通过ssms返回的存储过程数据,但不是ef? - Stored procedure data returned through ssms but not ef? 获取EF6中存储过程的返回值 - Get the returned value of Stored Procedure in EF6
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM