实体框架存储过程 - 具有代码优先的多个结果集

时间:2014-04-19 16:58:30

标签: c# asp.net-mvc entity-framework

我使用以下代码从存储过程中获取常规结果:

var paramUserId = new SqlParameter
{
    ParameterName = "userId",
    Value = userId
};

string query = string.Format("{0} {1}",
              "SpSetFoo",
              "@userId");

var results = context.Database.SqlQuery<FooModel>(query,
                                             paramUserId);

result = results.ToList();

同时我需要从另一个存储过程中检索多个结果集,根据此文档我发现它是可能的:http://msdn.microsoft.com/en-us/data/jj691402.aspx

然而,微软的例子是使用ADO.NET。如果没有使用EF的ADO.NET,它不可能实现相同的结果吗?

由于

3 个答案:

答案 0 :(得分:14)

查看此链接。这将首先与EF 6.0 Code一起使用。

http://www.khalidabuhakmeh.com/entity-framework-6-multiple-result-sets-with-stored-procedures

我自己的扩展基于上面的链接,C#6.0,添加参数并使用多个选择不必要的程序。

 public static class MultipleResultSets
{

    #region Public Methods
    public static MultipleResultSetWrapper MultipleResults(this DbContext db,string query,IEnumerable<SqlParameter> parameters=null) => new MultipleResultSetWrapper(db: db,query: query,parameters: parameters);
    #endregion Public Methods

    #region Public Classes
    public class MultipleResultSetWrapper
    {

        #region Public Fields
        public List<Func<DbDataReader,IEnumerable>> _resultSets;
        #endregion Public Fields

        #region Private Fields
        private readonly IObjectContextAdapter _Adapter;
        private readonly string _CommandText;
        private readonly DbContext _db;
        private readonly IEnumerable<SqlParameter> _parameters;
        #endregion Private Fields

        #region Public Constructors
        public MultipleResultSetWrapper(DbContext db,string query,IEnumerable<SqlParameter> parameters = null)
        {
            _db = db;
            _Adapter = db;
            _CommandText = query;
            _parameters = parameters;
            _resultSets = new List<Func<DbDataReader,IEnumerable>>();
        }
        #endregion Public Constructors

        #region Public Methods
        public MultipleResultSetWrapper AddResult<TResult>()
        {
            _resultSets.Add(OneResult<TResult>);
            return this;
        }

        public List<IEnumerable> Execute()
        {
            var results = new List<IEnumerable>();

            using(var connection = _db.Database.Connection)
            {
                connection.Open();
                var command = connection.CreateCommand();
                command.CommandText = _CommandText;
                if(_parameters?.Any() ?? false) { command.Parameters.AddRange(_parameters.ToArray()); }
                using(var reader = command.ExecuteReader())
                {
                    foreach(var resultSet in _resultSets)
                    {
                        results.Add(resultSet(reader));
                    }
                }

                return results;
            }
        }
        #endregion Public Methods

        #region Private Methods
        private IEnumerable OneResult<TResult>(DbDataReader reader)
        {
            var result = _Adapter
                .ObjectContext
                .Translate<TResult>(reader)
                .ToArray();
            reader.NextResult();
            return result;
        }
        #endregion Private Methods

    }
    #endregion Public Classes

}

这是一个如何调用它的例子

var Policy = "123";
var Results=   db
        .MultipleResults($"EXEC GetPolicyInfo '{Policy}'")
        .AddResult<Driver>()
        .AddResult<Address>()
        .AddResult<Phone>()
        .AddResult<Email>()
        .AddResult<Vehicle>()
        .Execute();
        var Output= new clsPolicyInfo
        {
            Drivers = Results[0] as Driver[],
            Addresses = Results[1] as Address[],
            Phones = Results[2] as Phone[],
            Emails = Results[3] as Email[],
            Vehicles = Results[4] as Vehicle[]
        };

答案 1 :(得分:9)

这是一个老话题,但在这里添加评论以防万一有人需要它。我需要使用从不同数据库返回两个表的存储过程,然后处理返回的数据存储到我们的应用程序数据库中。提到标准文档并按照步骤但不喜欢它。首先是存在问题,代码暴露了一些不可靠性,从可维护性的角度来看这不是一个好主意。

专门为处理SP而设计的Nuget包就会出现。看看CodeFirstStoredProcs。精美的包装,非常专注,完美地完成工作。这将返回存储过程的每个结果集的对象集合,然后可以按任何方式使用它们。它们是对不同版本的EF(包括版本6)的良好且一致的支持。还要检查代码项目Code First Stored Procedures的说明。下载的源代码甚至还有一个PDF,解释了如何在详细步骤中使用它。

非常感谢作者aureolin。

答案 2 :(得分:0)

  

但是,来自Microsoft的示例正在使用ADO.NET。这是不可能的   在没有使用EF的ADO.NET的情况下实现相同的结果?

已链接的

实体框架是ADO.NET的一部分 ... this document,它向您显示使用ADO.NET实体框架的需求。该示例使用Raw sql命令执行存储过程(如果您已经编写了SQL过程,则编写LINQ查询是没有意义的)。 See here

  

即将发布的ADO.NET版本的主要目标是提高   数据编程的抽象级别,从而有助于消除   数据模型之间以及语言之间的阻抗不匹配   应用程序开发人员将不得不处理。二   语言集成查询   和 ADO.NET实体框架。实体框架作为新的框架存在   ADO.NET技术家族的一部分。 ADO.NET将启用LINQ   许多数据访问组件:LINQ to SQL,LINQ to DataSet和LINQ to   实体。


代码:

不清楚您要从过程中返回什么结果集:SpSetFoo ...名称SpSetFoo表示过程更新了数据库中的Foo。为简化起见,我假设您有一个名为GetFooAndBarForUser的过程:

CREATE PROCEDURE [dbo].[GetFooAndBarForUser] (@userId int)
AS
    SELECT * FROM Foo F WHERE F.UserId = @userId
    SELECT * FROM Bar B WHERE B.UserId = @userId

这是您读取两种模型的方法:

public void GetTwoResultSetsForUserId(int userId)
{
    using (var db = new MyDbContext())
    {
        // Create a SQL command and add parameter
        var cmd = db.Database.Connection.CreateCommand();
        cmd.CommandText = "[dbo].[GetFooAndBarForUser]";
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.Add(new SqlParameter("@userId", userId));

        // execute your command
        db.Database.Connection.Open();
        var reader = cmd.ExecuteReader();

        // Read first model --> Foo
        var blogs = ((IObjectContextAdapter)db)
            .ObjectContext
            .Translate<Foo>(reader, "Foo", MergeOption.AppendOnly);

        // move to next result set
        reader.NextResult();

        // Read second model --> Bar
        var bar = ((IObjectContextAdapter)db)
            .ObjectContext
            .Translate<Post>(reader, "Bar", MergeOption.AppendOnly);
    }
}