3
votes

Maybe no need to tell you, that I have been Googling, looking at the docs & specs, and testing around for couple of days now with no success.

What I'm trying to achieve: I want to bind the rows of my table not to a concrete entity (called Contragent in my case) of the model, but to a service method within the same model, which accepts a parameter, filters the Contragents and returns an IQueryable. I remember yesterday reading in a stackoverflow question - somebody was asking is it possible to implement an sql 'in' clause using only OData url parameters. They had replied, that the only way is to build a string of the form $filter=(Name eq 'test1' and Name eq 'test2' and ...). So I thought it would be nice if I could add a custom method to my OData service, where I can comfortably use LINQ in C#. I saw in the OData docs, that it is possible to ask not for an entity of the model, but to query the data from such a method again just writing the proper URL. So basically, this is what I want - to bind my SAP table to select * from Contragent where Name in (select Name from ...) using the OData service I have created.

What I've done so far: First of all, I learned how to add a custom service method to the OData service. It looks like it has to be placed in the same class where you initialize the service:

public class MyService : EntityFrameworkDataService<MyEntities>
{
    // This method is called only once to initialize service-wide policies.
    public static void InitializeService(DataServiceConfiguration config) {
        // TODO: set rules to indicate which entity sets and service operations are visible, updatable, etc.
        // Examples:
        config.SetEntitySetAccessRule("*", EntitySetRights.AllRead | EntitySetRights.AllWrite);
        //https://debugmode.net/2012/01/13/how-to-work-with-custom-method-in-wcf-data-service/
        config.SetServiceOperationAccessRule("*", ServiceOperationRights.All); // for the custom method to be accessible via the service
        config.UseVerboseErrors = true;
        config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
    }

    private MyEntities entities = new MyEntities();
    //https://docs.microsoft.com/en-us/dotnet/framework/data/wcf/service-operations-wcf-data-services
    [WebGet] // this attribute is required - ^see the link above^
    public IQueryable<Contragent> GetContragentsByIsContained(int isContained)
    {
        IQueryable<Contragent> result = entities.Contragent;
        List<string> neededNames = GetNeededNames();
        if (isContained == 0)
        {
            result = result.Where(x => !neededNames.Contains(x.Name));
        }
        else if (isContained == 1)
        {
            result = result.Where(x => neededNames.Contains(x.Name));
        }
        // else - no filtering

        return result;
    }
}

After that, I did some testing and I see, that both URLs http://localhost:port/Services/MyService.svc/GetContragentsByIsContained?isContained=1 and http://localhost:port/Services/MyService.svc/GetContragentsByIsContained?isContained=0 work well. After that, I tried to

.bindRows({
    path: '/GetContragentsByIsContained?isContained=' + (typeof (isContained) === 'string' && isContained !== '' ? isContained: 2));
    //...
});

of my table to this result and it does not work.

Opening up Fiddler to see what's going on told me what's the problem. I see, that SAP UI 5 sends the following requests: 400 HTTP localhost:port/Services/MyService.svc/GetContragentsByIsContained and 400 HTTP localhost:port/Services/MyService.svc/GetContragentsByIsContained?$skip=0&$top=25. I am not surprised at all, that the server 400's to them, because... hey, SAP, where is the isContained parameter gone!? This made me do another test in Fiddler or the browser - doesn't matter: http://localhost:port/Services/MyService.svc/GetContragentsByIsContained?isContained=1&$filter=(Name eq 'Test123'). There is a Contragent named Test123, which is among the desired names, so this query with isContained=1 returns 1 item as it should, while the one with isContained=0 returns no items - again as expected. So it shouldn't be a problem for UI5 library to append the special parameters beginning with $ to the end of my URL with no need to remove my parameter isContained.

So, finally, the question itself: Is it normal for the SAP library to remove everything after ? in the binding path before loading the data? How can I preserve this parameter? Thanks in advance!

edit: I forgot to tell you, that I tried to add

urlParameters: {
    "isContained": (typeof (isContained) === 'string' && isContained !== '' ? isContained : 2 )
}

just like I would do if I wanted to make a read():

model.read("/GetContragentsByIsContained" {
    urlParameters: {
        "isContained": (typeof (isContained) === 'string' && isContained !== '' ? isContained : 2 )
    },
    success: function (count, args) {
        debugger;
        table.rows = count.results; // this doesn't work as well
    },
    error: function (args) {
        debugger;
        console.log(args);
    }
});

It doesn't work.

1

1 Answers

1
votes

Below you can find the valid synax of the declarative aggregation binding in XML view:

items="{
    path: '/MySuperSet',
    parameters: {
        custom: {
            foo: 'bar'
        }
    }
}"

Be aware that it's not possible to pass the custom parameters starting with "$". See the ODataListBinding class.