RelationPredicateBucket as part of web service contract?

Posts   
 
    
Robert.W
User
Posts: 79
Joined: 19-Jun-2007
# Posted on: 14-Aug-2007 16:58:12   

Hi,

I am currently implementing a WCF service which through a business layer is using LLBLGen generated code in the data access layer.

Lets assume I have a Customer entity (not CustomerEntity generated by LLBLGen) defined as part of the data contract for the service. I also have a:

_GetCustomersResponse GetCustomers(GetCustomersRequest request) _

operation defined which is supposed to retrieve customers from the database. GetCustomersRequest message will accept filters, sorting expressions, pagination data etc. I have a couple of questions related to this scenario (for now focusing only on filtering part of the problem):

1) I assume I can use RelationPredicateBucket as part of web service contract (I would like to avoid that however)? 2) Is there any suggested way of addressing such scenario with LLBLGen? 3) Is there a way to construct a PredicateExpression using the names (string representation) of the entity properties? For example instead of

new PredicateExpression().Add(CustomerFields.FirstName == "One");

use

new PredicateExpression().Add("FirstName" == "One");

Best regards, Robert Wilczynski.

goose avatar
goose
User
Posts: 392
Joined: 06-Aug-2007
# Posted on: 14-Aug-2007 19:21:08   

Hi Robert, ‘hope this answer your questions:

1) I assume I can use RelationPredicateBucket as part of web service contract (I would like to avoid that however)?

Yes, _RelationPredicateBucket _has the _SerializableAttribute _applied to it.

2) Is there any suggested way of addressing such scenario with LLBLGen?

May be this other thread can be useful (http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=8174)

3) Is there a way to construct a PredicateExpression using the names (string representation) of the entity properties? For example instead of

new PredicateExpression().Add(CustomerFields.FirstName == "One");

use

new PredicateExpression().Add("FirstName" == "One");

Yes, using _Activator _(see this: http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=10790), but if I were you I’ll write as many overloads (in the business layer) need it to try to avoid this, for example GetCustomersByFirstName(string firstName), GetGustomersByLastName(string lastName), etc.

Regards,

Robert.W
User
Posts: 79
Joined: 19-Jun-2007
# Posted on: 14-Aug-2007 22:33:50   

Hi,

Yes, RelationPredicateBucket has the SerializableAttribute applied to it.

The other thread you've mentioned states RelationPredicateBucket is not XmlSerializable which would be a problem but anyway it's a solution I was trying to avoid.

Yes, using Activator (see this: http://www.llblgen.com/TinyForum/Messages.aspx?ThreadID=10790)

I would prefer not to use reflection for this. Any other way or is this a dead end?

[...] but if I were you I’ll write as many overloads (in the business layer) need it to try to avoid this, for example GetCustomersByFirstName(string firstName), GetGustomersByLastName(string lastName), etc.

This unfortunately is not an option. I believe that for a modern piece of software it's far too little to allow a user to only filter by one property at a time. It seems I will be forced to define my own filter classes for each data retrieval operation and translate them to RelationPredicateBucket on the service side. I was just hoping I can somehow avoid this or at least get some help from LLBLGen generated code in that matter. It seems however this is going to be a very manual process.

Anyone else has anything to add to this discussion?

Best regards, Robert Wilczynski.

jmeckley
User
Posts: 403
Joined: 05-Jul-2006
# Posted on: 15-Aug-2007 01:34:27   

Personally I would opt for the overloaded fetches as well. usually the adapter/service layer is created to control how/when/who can access data. So when the service is called like CustomerService.FetchByCustomerId(int id); the service will determine what exactly is returned.

if the user needs to access any data at anytime from anywhere your better off creating a fat client using the SS model.

also passing simple datatypes (numbers, string, date, bytes) is universal so you can communicate with non-.net platforms.

Robert.W
User
Posts: 79
Joined: 19-Jun-2007
# Posted on: 15-Aug-2007 12:56:31   

Hi Jason,

Thanks for picking up the discussion.

Personally I would opt for the overloaded fetches as well. usually the adapter/service layer is created to control how/when/who can access data. So when the service is called like CustomerService.FetchByCustomerId(int id); the service will determine what exactly is returned.

Now that doesn't fulfill the requirements outlined by the user, does it.

if the user needs to access any data at anytime from anywhere your better off creating a fat client using the SS model.

it's not "any data at anytime". The system is controlling who has access to data. I won't allow users to filter on complex relations and load hierarchies. I just need to allow them to filter by a set of fields (like give me all my customers of type Premium that registered withing last two months). The service may also get a fixed number of rows (100-200 maximum) and if there are more tell the consumer to narrow the search. Also, data retrieval operations are only a small part of the service - there are others that drive workflows, need auditing etc. I don't thing it will be wise to mix direct DB access with web service especially that we want this software to be accessible from remote locations.

also passing simple datatypes (numbers, string, date, bytes) is universal so you can communicate with non-.net platforms.

I would have agreed a couple of years ago but well designed contract (no matter how complex it is) doesn't stop Java (or other clients) from consuming the service. Big players (read Amazon, eBay) have complex services exposed that are consumable by pretty much any platform that has SOAP support.

Best regards, Robert WIlczynski.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 15-Aug-2007 13:00:44   

It's not XmlSerializable, as Xml webservices should be created with message based service contracts, not the more lower level REST approach (although the REST community thinks otherwise). If we made the classes xml serializable (which is a pain already, considering the fact that .NET thinks they're then datasets or a schema importer has to be added which is cumbersome to say the least) it wouldn't gain much as the client has to be LLBLGen Pro aware, and in that case, I'd opt for remoting.

As for the services you mention: the types exposed are all simple types, where the XML transported is the central point. This is key, however with a relation predicate bucket or predicate expression, the xml is following the class definition, not the other way around, so the XML can't be in the contract as it's not controlled by you: you then need a conversion, which precisely leads to message based services.

Frans Bouma | Lead developer LLBLGen Pro
Robert.W
User
Posts: 79
Joined: 19-Jun-2007
# Posted on: 15-Aug-2007 13:31:28   

Hi Frans,

As I mentioned using RelationPredicateBucket in the service data contract was something I wanted to avoid. It's probably better it isn't XmlSerializable as in a moment of weakness and despair I might have actually used it.

However the problem still remains - there is no apparent way to build a fairly generic RelationPredicateBucket construction code. Right now it has to be done manually for each service we are going to develop. What we would consider is creating a simple set of data contract classes that would allow us to define such filters (shared across all our services) and some generic code that would enable translation to RelationPredicateBucket. I was thinking of something along the lines of

IFieldInfoProvider.GetFieldInfo("Entity", "Field");

functionality to achieve this (can I get a hod of that through the public API somehow?). Having an IEntityField object we could then construct some concrete predicates like FieldBetweenPredicate to use in filtering expressions. I don't particularly like this solution (mainly because of complexity and a need to have matching LLBLGen entities and service contract classes in terms of names and property names) but it still seems better than nothing.

Since we have just started using LLBLGen (first project actually) we are trying to come up with a set of common practices / helper classes that will help us with such problems in the future.

Best regards, Robert Wilczynski.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39613
Joined: 17-Aug-2003
# Posted on: 16-Aug-2007 11:24:50   

If you want to obtain field objects, just use the entityfieldfactory for the particular entity for that simple_smile Every generated code project has these factories. These work on enums, so you have to send to the service the enum values you want to use. It's then also wise perhaps to send along the type description of the enum to use, so you can create a new enum value on the service from the int you send along. This then will allow you to call the factory to obtain the field.

Frans Bouma | Lead developer LLBLGen Pro