How to use Stored Procedure that accepts Paging Parameters

前端 未结 2 1910
情深已故
情深已故 2021-01-28 01:28

Any links samples on how to use stored procedure when using breeze, mainly interested in finding out how to extract Paging parameters and set inlinecount value, since stored pro

相关标签:
2条回答
  • 2021-01-28 02:15

    To query the information from the breeze request, try this as your GET method. This assumes GetMyEntityType returns an entity in your data context.

    [HttpGet]
    [EnableBreezeQuery(MaxExpansionDepth = 0)]
    public QueryResult GetMyEntityType(ODataQueryOptions<MyEntityType> options)
    {
    ... your server method
    }
    

    Everything you need to know about a breeze request is in this object. At first I was wondering how to include other parameters with this object, but there was no need - the parameters were inside the object.

    To get your skip and take, try this...

    foreach (var queryParam in options.Request.Properties.FirstOrDefault(x => x.Key == "MS_QueryNameValuePairs").Value as System.Collections.Generic.KeyValuePair<string, string>[])
            {
                if (queryParam.Key == "$skip")
                    int.TryParse(queryParam.Value, out skip);
                if (queryParam.Key == "$top")
                    int.TryParse(queryParam.Value, out top);
            }
    

    Tips: If $skip is 0, it may not be in the request parameters. Default it to 0. Breeze uses $top, not $take.

    You can call a stored procedure using

    DB.ObjectContext.ExecuteStoreQuery<MyEntityType>(query).ToList()
    

    As long as query returns an entity set of type MyEntityType, query can be anything. EXEC MyStoredProc param1, param2, or you could even build dynamic SQL.

    To return your own inlineCount, use the QueryResult return type:

            return new QueryResult()
            {
                InlineCount = myInlineCount,
                Results = DB.ObjectContext.ExecuteStoreQuery<MyEntityType>(query).ToList()
            };
    

    Extracting your filters is no easy task. I found a solution and modified it to add recursion and tweaked it to handle Breeze requests. These are the helper methods and a helper class that you'll need to extract them.

        private void ProcessUnaryOperator(UnaryOperatorNode unaryOperator, List<ODataFilter> filterList)
        {
            if (unaryOperator != null)
            {
                if (unaryOperator.Operand != null && unaryOperator.Operand.GetType().FullName == "Microsoft.Data.OData.Query.SemanticAst.BinaryOperatorNode")
                {
                    ProcessBinaryOperator(unaryOperator.Operand as BinaryOperatorNode, filterList);
                }
            }
        }
    
        private void ProcessBinaryOperator(BinaryOperatorNode binaryOperator, List<ODataFilter> filterList)
        {
            if (binaryOperator != null)
            {
                if (binaryOperator.Left != null && binaryOperator.Left.GetType().FullName == "Microsoft.Data.OData.Query.SemanticAst.BinaryOperatorNode")
                    ProcessBinaryOperator(binaryOperator.Left as BinaryOperatorNode, filterList);
    
                if (binaryOperator.Right != null && binaryOperator.Right.GetType().FullName == "Microsoft.Data.OData.Query.SemanticAst.BinaryOperatorNode")
                    ProcessBinaryOperator(binaryOperator.Right as BinaryOperatorNode, filterList);
    
                if (binaryOperator.Left != null && binaryOperator.Left.GetType().FullName == "Microsoft.Data.OData.Query.SingleValueFunctionCallNode")
                {
                    var singleValueFunctionCallNode = binaryOperator.Left as SingleValueFunctionCallNode;
    
                    var property = (singleValueFunctionCallNode.Arguments.FirstOrDefault() as SingleValuePropertyAccessNode ?? singleValueFunctionCallNode.Arguments.LastOrDefault() as SingleValuePropertyAccessNode) ;
                    var constant = (singleValueFunctionCallNode.Arguments.FirstOrDefault() as ConstantNode ?? singleValueFunctionCallNode.Arguments.LastOrDefault() as ConstantNode);
    
                    var lt = string.Empty;
                    switch (singleValueFunctionCallNode.Name)
                    {
                        case "startswith":
                            lt = constant.Value.ToString() + "%";
                            break;
                        case "endswith":
                            lt = "%" + constant.Value.ToString();
                            break;
                        case "substringof":
                            lt = "%" + constant.Value.ToString() + "%";
                            break;
                        case "equal":
                            lt = constant.Value.ToString();
                            break;
                    }
    
                    if (property != null && property.Property != null && constant != null && constant.Value != null)
                        filterList.Add(new ODataFilter(property.Property.Name, binaryOperator.OperatorKind.ToString(), lt, property, constant));
                }
    
                if (binaryOperator.Left != null && binaryOperator.Left.GetType().FullName == "Microsoft.Data.OData.Query.SingleValuePropertyAccessNode")
                {
                    var property = binaryOperator.Left as SingleValuePropertyAccessNode ?? binaryOperator.Right as SingleValuePropertyAccessNode;
                    var constant = binaryOperator.Left as ConstantNode ?? binaryOperator.Right as ConstantNode;
                    var lt = constant.Value.ToString();
    
                    if (property != null && property.Property != null && constant != null && constant.Value != null)
                        filterList.Add(new ODataFilter(property.Property.Name, binaryOperator.OperatorKind.ToString(), lt, property, constant));
                }
            }
        }
    
        public class ODataFilter
        {
            public ODataFilter(string prop, string op, string lt, SingleValuePropertyAccessNode pn, ConstantNode cn)
            {
                this.Property = prop;
                this.Operator = op;
                this.LiteralText = lt;
                this.PropertyNode = pn;
                this.ConstantNode = cn;
            }
    
            public string Property { get; set; }
            public string Operator { get; set; }
            public string LiteralText { get; set; }
            public SingleValuePropertyAccessNode PropertyNode { get; set; }
            public ConstantNode ConstantNode { get; set; }
        }
    

    This is the calling code to extract your filters:

            List<ODataFilter> filterList = new List<ODataFilter>();
    
            // Not Equal (Unary Operators)
            if (options.Filter != null && options.Filter.FilterClause != null && options.Filter.FilterClause.Expression.GetType().Name == "UnaryOperatorNode")
                ProcessUnaryOperator(options.Filter.FilterClause.Expression as UnaryOperatorNode, filterList);
    
            // Equal (Binary Operators)
            if (options.Filter != null && options.Filter.FilterClause != null && options.Filter.FilterClause.Expression.GetType().Name == "BinaryOperatorNode")
                ProcessBinaryOperator(options.Filter.FilterClause.Expression as BinaryOperatorNode, filterList);
    

    Here's a subset of USING's

    using Microsoft.Data.OData.Query; 
    using System.Web.Http.OData.Query; 
    using Microsoft.Data.OData.Query.SemanticAst;
    

    Did you know you can execute a stored procedure that returns multiple result sets and consume those results using MARS? I currently run a stored procedure that returns 23 IQueryable's in one SQL round trip.

    Good luck!

    (Edit)

    If you are manually applying the $skip and $top in your stored procedure, Breeze will attempt to apply them against the result set a second time. In this case, I simply sent skip and top as parameters instead of using $skip and $top. And btw, this bug took 7 hours to find.

    This link: https://www.stevefenton.co.uk/2015/07/getting-the-sql-query-from-an-entity-framework-iqueryable/ shows how to translate an IQueryable object into T-SQL. Apply the breeze filter to a blank IQueryable of MyEntityType, using options.Filter.ApplyTo, then use the code from the link above to render the breeze request into TSQL.

    // Determine the where clause 
    var whereClause = options.Filter == null ? "" : ToTraceString<MyEntityType>(
        options.Filter.ApplyTo(
            (from x in DB.Context.MyEntityTypes select x),
            new ODataQuerySettings()) as IQueryable<MyEntityType>)
        .Split(new[] { "\r", "\n" }, StringSplitOptions.RemoveEmptyEntries).Where(x => x.Trim().StartsWith("WHERE")).FirstOrDefault().Trim();
    
    /* Steve Fenton, https://www.stevefenton.co.uk/2015/07/getting-the-sql-query-from-an-entity-framework-iqueryable/
     * July 24, 2015
     * */
    private static string ToTraceString<T>(IQueryable<T> query)
    {
        var internalQueryField = query.GetType().GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Where(f => f.Name.Equals("_internalQuery")).FirstOrDefault();
        var internalQuery = internalQueryField.GetValue(query);
        var objectQueryField = internalQuery.GetType().GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Where(f => f.Name.Equals("_objectQuery")).FirstOrDefault();
        var objectQuery = objectQueryField.GetValue(internalQuery) as System.Data.Entity.Core.Objects.ObjectQuery<T>;
        return ToTraceStringWithParameters<T>(objectQuery);
    }
    private static string ToTraceStringWithParameters<T>(System.Data.Entity.Core.Objects.ObjectQuery<T> query)
    {
        System.Text.StringBuilder sb = new StringBuilder();
        string traceString = query.ToTraceString() + Environment.NewLine;
        foreach (var parameter in query.Parameters)
            traceString = traceString.Replace("@" + parameter.Name, "'" + parameter.Value.ToString() + "'");
        return traceString;
    }
    /* */
    
    0 讨论(0)
  • 2021-01-28 02:18

    It's a good question, but we don't yet have any examples of using stored procedures with breeze, although it is quite doable. Please add this to Breeze User Voice and vote for it. We take your feedback seriously.

    0 讨论(0)
提交回复
热议问题