![]() |
#1 |
Участник
|
In .NET 3.5 Microsoft released LINQ (Language INtegrated Query) – which is a way of writing select statements strongly typed inside your C# code.
Wouldn’t it be nice if we could leverage this technology when connecting to NAV Web Services? I thought YES – so I set out to find out what it took… The goal was to be able to replace code like this: CustomerRef.Customer_Service cservice = new CustomerRef.Customer_Service(); cservice.UseDefaultCredentials = true; CustomerRef.Customer_Filter filter = new CustomerRef.Customer_Filter(); filter.Field = CustomerRef.Customer_Fields.Location_Code; filter.Criteria = "=YELLOW|=BLUE"; CustomerRef.Customer_Filter[] filters = new CustomerRef.Customer_Filter[] { filter }; CustomerRef.Customer[] customers = cservice.ReadMultiple(filters, null, 0); foreach (CustomerRef.Customer customer in customers) { // do stuff } with code like this: CustomerRef.Customer_Service cservice = new CustomerRef.Customer_Service(); cservice.UseDefaultCredentials = true; var cquery = from c in new FreddyK.NAVPageQuery<CustomerRef.Customer>(cservice) where c.Location_Code == "YELLOW" || c.Location_Code == "BLUE" select c; foreach (CustomerRef.Customer customer in cquery) { // do stuff } Which I personally find more readable and has a lot of other advantages:
Understanding LINQ Lets look at the above code. The following CustomerRef.Customer_Service cservice = new CustomerRef.Customer_Service(); cservice.UseDefaultCredentials = true; initializes the Service (as usually) and var cquery = from c in new FreddyK.NAVPageQuery<CustomerRef.Customer>(cservice) where c.Location_Code == "YELLOW" || c.Location_Code == "BLUE" select c; creates a query (which contains an expression tree of the where clause) and foreach (CustomerRef.Customer customer in cquery) { // do stuff } invokes the query (calls Web Services ReadMultiple) and returns the customers. FreddyK.NAVPageQuery is a class, which is capable of acting as a LINQ provider for all pages exposed as Web Services. The NAVPageQuery class is a generic class where you specify the type returned by the service – and you specify the service object as a parameter to the constructor. No need to say, that these two must match, else you will get an exception. For a class to be usable in a LINQ query it needs to implement IQueryable<type> (which is a combination of IQueryable, IEnumerable<type> and IEnumerable) – and typically you will then have another class, which implements IQueryProvider. In my sample, I have implemented both interfaces in the NAVPageQuery. Instead of listing the interfaces, I will list what happens when the query is build (when the line with var cquery = is executed):
When we then do a foreach on the cquery a little later, the following this happens:
decimal amount = 10000; var cquery = from c in new FreddyK.NAVPageQuery<CustomerRef.Customer>(cservice) where c.Balance_LCY > amount select c; In this case you want to separate building the query and creating the filters, since you could: foreach (CustomerRef.Customer customer in cquery) { // do stuff } amount = 100000; foreach (CustomerRef.Customer customer in cquery) { // do other stuff } this demonstrates the need for having the ability to decouple the query and the values used by the query and this is the reason for not creating the filter specs while building the query – but keeping the expression tree. Another sample: cquery = from c in new FreddyK.NAVPageQuery<CustomerRef.Customer>(cservice) where c.Last_Date_Modified >= DateTime.Today.AddDays(-5) select c; At the time of execution, this query will give you customers modified the last 5 days (imagine building that one with filters easy) Important things to notice It is important to realize that NAVPageQuery doesn’t give you any functionality you didn’t have before. With the original Filter class you cannot OR things together (unless it is the same field) – you also cannot do that using LINQ (towards NAV Web Services) If you try to type in a where clause like where c.Location_Code == "YELLOW" || c.Country_Region_Code == "US" It will fail – intentionally! Of course I could have done client side filtering – but this would only fool the developer, since you would take the performance hit when calling ReadMultiple and you wouldn’t really notice – I wanted the developer to be aware when he makes clever or not so clever choices. So – the where clause needs to be simple enough so that I can convert it into a filter specification. The error will be thrown when you try to use the query and it will typically be an exception stating “Query too complex” (even though it might not be complex at all). Another thing to notice is that AND’s and OR’s cannot be combined (as this isn’t possible in the filter spec): where c.Location_Code == "YELLOW" && (c.Balance_LCY > amount || c.Location_Code == "BLUE") is not allowed, but where c.Balance_LCY > amount && (c.Location_Code == "YELLOW" || c.Location_Code == "BLUE") && c.No != "" works just fine. What about updating, deleting and creating? Nothing new there – you still have your service object and the Customer objects that are returned from the LINQ query is the same object you would get from reading through Readmultiple directly. This class is only designed to make queries towards web services easier and more error safe. That’s all good – but how is it made? For once, I am not going to go through how NAVPageQuery is created in detail – I have commented the source code pretty detailed and if you need to understand how it is build, you can look at the sourcecode combined with the information on how things are executed (from above) and it should be possible to decrypt the source:-) If you download the solution .zip file here, you will find a class library with one class (NAVPageQuery) and a Console application called TestQuery. The console app. can be deleted if you don’t want it and it has a number of samples – some of which are listed above. When downloading the test app. – you need to update the web references (so that they match your exposed Pages) – I know that I added the Last_Date_Modified to the Customer Page in order to have access to that field through web services – there might be more. Questions and comments are welcome, I prefer to answer questions on mibuso instead of here – since it typically makes the answer available to more people. Enjoy Freddy Kristiansen PM Architect Microsoft Dynamics NAV Подробнее
__________________
Расскажите о новых и интересных блогах по Microsoft Dynamics, напишите личное сообщение администратору. |
|