Linq to SQL Stored Procedures with Multiple Result

2019-01-22 08:32发布

问题:

We have followed the approach below to get the data from multiple results using LINQ To SQL

CREATE PROCEDURE dbo.GetPostByID
(
    @PostID int
)
AS
    SELECT    *
    FROM      Posts AS p
    WHERE     p.PostID = @PostID

    SELECT    c.*
    FROM      Categories AS c
    JOIN      PostCategories AS pc
    ON        (pc.CategoryID = c.CategoryID)
    WHERE     pc.PostID = @PostID

The calling method in the class the inherits from DataContext should look like:

[Database(Name = "Blog")]
public class BlogContext : DataContext
{
    ... 

    [Function(Name = "dbo.GetPostByID")]
    [ResultType(typeof(Post))]
    [ResultType(typeof(Category))]
    public IMultipleResults GetPostByID(int postID)
    {
        IExecuteResult result = 
            this.ExecuteMethodCall(this, 
                  ((MethodInfo)(MethodInfo.GetCurrentMethod())), 
                  postID);

        return (IMultipleResults)(result.ReturnValue);
    }
}

Notice that the method is decorated not only with the Function attribute that maps to the stored procedure name, but also with the ReturnType attributes with the types of the result sets that the stored procedure returns. Additionally, the method returns an untyped interface of IMultipleResults:

public interface IMultipleResults : IFunctionResult, IDisposable
{
    IEnumerable<TElement> GetResult<TElement>();
}

so the program can use this interface in order to retrieve the results:

BlogContext ctx = new BlogContext(...);

IMultipleResults results = ctx.GetPostByID(...);

IEnumerable<Post> posts = results.GetResult<Post>();

IEnumerable<Category> categories = results.GetResult<Category>();

In the above stored procedures we had two select queries 1. Select query without join 2. Select query with Join

But in the above second select query the data which is displayed is from one of the table i.e. from Categories table. But we have used join and want to display the data table with the results from both the tables i.e. from Categories as well as PostCategories.

  1. Please if anybody can let me know how to achieve this using LINQ to SQL
  2. What is the performance trade-off if we use the above approach vis-à-vis implement the above approach with simple SQL

回答1:

Scott Guthrie (the guy who runs the .Net dev teams at MS) covered how to do this on his blog some months ago much better than I ever could, link here. On that page there is a section titled "Handling Multiple Result Shapes from SPROCs". That explains how to handle multiple results from stored procs of different shapes (or the same shape).

I highly recommend subscribing to his RSS feed. He is pretty much THE authoritative source on all things .Net.



回答2:

Heya dude - does this work?

IEnumerable<Post> posts;
IEnumerable<Category> categories;

using (BlogContext ctx = new BlogContext(...))
{
    ctx.DeferredLoadingEnabled = false; // THIS IS IMPORTANT.
    IMultipleResults results = ctx.GetPostByID(...);
    posts = results.GetResult<Post>().ToList();
    categories = results.GetResult<Category>().ToList();
}
// Now we need to associate each category to the post.
// ASSUMPTION: Each post has only one category (1-1 mapping).
if (posts != null)
{
    foreach(var post in posts)
    {
        int postId = post.PostId;
        post.Category = categories
            .Where(p => p.PostId == postId)
            .SingleOrDefault();
    }
}

Ok. lets break this down.

First up, a nice connection inside a using block (so it's disposed of nicely).

Next, we make sure DEFERRED LOADING is off. Otherwise, when u try and do the set (eg. post.Category == blah) it will see that it's null, lazy-load the data (eg. do a rountrip the database) set the data and THEN override the what was just dragged down from the db, with the result of there Where(..) method. phew! Summary: make sure deferred loading is off for the scope of the query.

Last, for each post, iterate and set the category from the second list.

does that help?

EDIT

Fixed it so that it doesn't throw an enumeration error by calling the ToList() methods.



回答3:

Just curious, if a Post have have one or many Categories, is it possible to instead of using the for loop, to load the Post.PostCategories with the list of Categories (one to many), all in one shot, using a JOIN?

var rslt = from p in results.GetResult<Post>()
           join c in results.GetResult<Category>() on p.PostId = c.PostID
           ...
           p.Categories.Add(c)