【问题标题】:Entity Framework Stored Procedures - Multiple Result sets with CodeFirst实体框架存储过程 - 使用 CodeFirst 的多个结果集
【发布时间】:2014-06-04 01:42:17
【问题描述】:

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

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

但是,Microsoft 的示例使用的是 ADO.NET。如果不使用 ADO.NET 而使用 EF,就不可能达到相同的结果吗?

谢谢

【问题讨论】:

  • 你想调用一个在 EF 中给出两个结果集的存储过程吗?
  • 没错,两个结果集。谢谢
  • 是的,您可以使用实体框架。您使用的是哪个版本的 .net 框架?
  • @sarin 我正在使用 EF 6.1,我相信是最新的。谢谢
  • @hugo.hilario .net 框架不是 EntityFramework! 4.5 版?

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


【解决方案1】:

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

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

【讨论】:

  • 我试过你的代码,当我尝试查询时,我收到Additional information: Cannot drop database "&lt;databasename&gt;" because it is currently in use.的错误为什么它试图删除数据库?单击下面的链接获取错误消息:> screencast.com/t/psYjKkM5SWK 任何建议都非常有帮助。谢谢
  • @jks 首先尝试运行简单的 select 语句,因此如果您的程序返回多个结果,请注释所有其余的并保留第一个。通常您的错误与您的类构造中的 DbContext 设置有关
  • 我喜欢有人在该链接中评论的修改版本:github.com/Icey88/RandomCodeSamples/tree/master/…,直接收到SqlCommand
  • 由于using(var connection = _db.Database.Connection),调用Execute后,你传递给这个类的DbContext不能使用。
  • 我认为Execute 中的using 可以做到这一点。我想在调用Execute 之后使用我现有的连接,但它失败了。我在调用 Execute 之前将我的代码移动了,它工作得很好。我将您的代码更改为只执行connection.Open()connection.Close()(在try-finally 中)并解决了问题。基本上,您的using 最后会调用connection.Dispose(),这就是导致问题的原因。
【解决方案2】:

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

专为处理 SP 设计的 Nuget 包应运而生。看看CodeFirstStoredProcs。出色的包装具有非常具体的重点,可以完美地完成工作。这将为存储过程的每个结果集返回一个对象集合,然后可以以任何所需的方式使用这些对象。它们对不同版本的 EF (包括版本 6)提供了良好且一致的支持。另请查看代码项目 Code First Stored Procedures 的说明。下载的源代码甚至还有一个 PDF,详细说明了如何使用它。

非常感谢作者 aureolin。

【讨论】:

    【解决方案3】:

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

    Entity Framework 是 ADO.NET 的一部分...您已链接的 this document 正在向您展示您想要使用 ADO.NET Entity Framework 的内容。该示例使用原始 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);
        }
    }      
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-08
      • 2016-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-06
      相关资源
      最近更新 更多