web-dev-qa-db-fra.com

Procédures stockées Entity Framework - Plusieurs ensembles de résultats avec le code en premier

J'utilise le code ci-dessous pour obtenir un résultat régulier à partir d'une procédure stockée:

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

En attendant, je dois récupérer plusieurs jeux de résultats à partir d'une autre procédure stockée, ce que j'ai trouvé possible selon cette documentation: http://msdn.Microsoft.com/en-us/data/jj691402.aspx

Cependant, l'exemple de Microsoft utilise ADO.NET. Il n'est pas possible d'obtenir le même résultat sans ADO.NET utilisant EF à la place?

Merci

13
Hugo Hilário

Voir ce lien. cela fonctionnera d'abord avec EF 6.0 Code.

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

J'ai ma propre extension basée sur le lien ci-dessus, C # 6.0, ajouter un paramètre et utiliser plusieurs procédures non nécessaires.

 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

}

et ceci est un exemple comment l'appeler 

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[]
        };
13
Waleed A.K.

C'est un sujet ancien, mais il faut ajouter des commentaires au cas où quelqu'un en aurait besoin. Je devais utiliser un processus stocké qui renvoyait deux tables d'une base de données différente, puis après avoir traité les données renvoyées stockées dans notre base de données d'application. Référé à la documentation standard et suivi les étapes mais ne l'a pas aimé. D'abord, il y a eu des problèmes et le code a mis au jour des dessous qui n'étaient pas une bonne idée du point de vue de la maintenabilité. 

C’est là qu’un package Nuget spécialement conçu pour la gestion des SP entre en scène. Jetez un oeil à CodeFirstStoredProcs . Emballage brillant avec une orientation très spécifique et fait le travail parfaitement. Cela retourne une collection d'objets pour chaque ensemble de résultats de proc stocké, qui peuvent ensuite être utilisés de la manière souhaitée. Il s’agit d’un support efficace et cohérent pour différentes versions de EF, y compris la version 6. Consultez également l’explication fournie dans le projet de code Code First Stored Procedures . Le code source téléchargé contient même un PDF qui explique comment l'utiliser dans les étapes détaillées.

Un grand merci à l'auteur aureolin.

8
Shashank

Cependant, l'exemple de Microsoft utilise ADO.NET. Ce n'est pas possible obtenir le même résultat sans ADO.NET en utilisant EF à la place?

Entity Framework fait partie d’ADO.NET ... ce document que vous avez lié, vous montre ce que vous souhaitez utiliser avec ADO.NET Entity Framework. L'exemple utilise une commande sql brute pour exécuter une procédure stockée (il est inutile d'écrire une requête LINQ si vous avez déjà écrit votre procédure SQL). Vois ici :

Un objectif principal de la prochaine version de ADO.NET est de générer le niveau d'abstraction pour la programmation de données, contribuant ainsi à éliminer le décalage d'impédance entre les modèles de données et entre les langues qui les développeurs d’applications seraient autrement confrontés. Deux Les innovations qui rendent ce mouvement possible sont Query-Integrated Query et le ADO.NET Entity Framework. Entity Framework existe en tant que nouveau fichier partie de la famille de technologies ADO.NET. ADO.NET activera LINQ nombreux composants d'accès aux données: LINQ to SQL, LINQ to DataSet et LINQ to Entités.


Le code:

Les résultats que vous retournez de votre procédure ne sont pas clairs: SpSetFoo... le nom SpSetFoo suggère que la procédure met à jour Foo dans la base de données. Pour simplifier les choses, je suppose que vous avez une procédure appelée GetFooAndBarForUser:

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

Voici comment vous pouvez lire les deux modèles:

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);
    }
}      
0
Hooman Bahreini