Hi there and happy new year.  2011 promises to be quite an interesting year, and I hope that I can continue to contribute here at Sanders Technology.  To kick off the new year, I decided to revisit the Windows Workflow article I started late last year.

A little while ago I wrote a post at entitled “A quick and dirty Rules Engine using Windows Workflow (Part 1)” which has evidently been fairly popular.  Unfortunately, it seems that I forgot to follow it up with a part 2!  Now, welcoming in the new year, I’m putting together the second part.

Honestly though, folks, this could easily be a multi part mini project, because the uses of this Windows Workflow Foundation (WF) rules engine are immense!

I’ve managed to extend the scope of the code displayed in part 1 to include some dummy data items and I’ve crafted some more reusable and general purpose code (for example purposes), but you really ought to be able to see for yourselves how powerful and multi-purpose this really is.

I was going to write a quick and dirty WinForms UI, but I ended up ditching it in favour of a bunch of unit tests instead.  You really should be able to see the potential here, I don’t want to spoil the magic by adding an inept user interface.

Let’s take a look at the sample solution.  I’ve added some terribly (and perhaps insultingly) simple “objects” which, of course, you would substitute for your own DTOs/Entities/BusinessObjects.  It’s a basic class with some public properties, nothing terribly complex (it’s a demo after all).  You can see it uses an Enum just for fun on one of the properties.  I’ve also included a screenshot of the Solution structure – nothing too scary here.

imageimage

The Class View for the “BusinessObjects” | The Solution Structure

Basically the entire solution consists of two class libraries and a Unit Test project.  I’m trying to keep this very simple.  You could plug a WinForms UI or a website or a WCF Web Service Application underneath this very easily!

The main fun is in the “RuleManager” class, which is basically just a wrapper for the main WF workflow engine parts.  I’ve put in an extremely vanilla implementation which allows a few interesting parts of functionality.  I think if you use your imagination, you’ll be able to come up with some much more interesting ways to play with the options.

So why don’t we have a look at the RuleManager class?  It is defined to take a generic type, so you can work upon different source object types.

For the purpose of this post, we have just the one main object defined “Employee”.  It also doesn’t so anything to ensure the rules loaded are explicit for the data type – I’ll do an expanded implementation later to show how we can account for this.

My sincere apologies for the crappiness of the format of the posted code!  I’m having a bit of a fight with my copy of Live Writer and the plugins for inserting code snippets are not working very well with the site layout theme.  I’ll try and get it looking right.. soon.

#region Using Directives
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Workflow.Activities.Rules.Design;
using System.Workflow.Activities.Rules;
using System.Windows.Forms;
using System.Workflow.ComponentModel.Serialization;
using System.Xml;
using System.IO;
using BusinessObjects;
using System.Collections.ObjectModel;
#endregion

namespace WorkFlowProvider
{
    /// Implements a wrapper around the Windows Workflow Foundation Rules Engine
    /// A Data Object type to process
    public static class RulesManager<T> where T : new()
    {
        #region Rules Editor Support

        /// Launch the Rules Form to create a new rule
        public static RuleSet LaunchNewRulesDialog(string ruleName, string outputPath)
        {
            return LaunchRulesDialog(null, ruleName, outputPath);
        }

        /// Launch the Rules Editor with an existing rule (for editing),
        /// or to create a new rule (pass NULL to create a new rule)
        ///
The rule name (for the file name)
        ///
The path to save rules to
        /// A rule (if one is saved/edited)
        public static RuleSet LaunchRulesDialog(RuleSet ruleSet, string ruleName, string outputPath)
        {
            // You could pass in an existing ruleset object for editing if you 


            // wanted to, we're creating a new rule, so it's set to null
            RuleSetDialog ruleSetDialog = new RuleSetDialog(typeof(T), null, ruleSet);

            if (ruleSetDialog.ShowDialog() == DialogResult.OK)
            {
                // grab the ruleset
                ruleSet = ruleSetDialog.RuleSet;

                // We're going to serialize it to disk so it can be reloaded 


                WorkflowMarkupSerializer serializer = new WorkflowMarkupSerializer();

                string fileName = String.Format("{0}.rules", ruleName);
                string fullName = Path.Combine(outputPath, fileName);

                if (File.Exists(fullName))
                {
                    File.Delete(fullName); //delete existing rule
                }

                using (XmlWriter rulesWriter = XmlWriter.Create(fullName))
                {
                    serializer.Serialize(rulesWriter, ruleSet);
                    rulesWriter.Close();
                }
            }
            return ruleSet;
        }

        #endregion

        #region Rule Processing

        /// Applies a set of rules to a specified data object
        public static T ProcessRules(T objectToProcess, ReadOnlyCollection rules)
        {
            RuleValidation validation = new RuleValidation(typeof(T), null);
            RuleExecution execution = new RuleExecution(validation, objectToProcess);

            foreach (RuleSet rule in rules)
            {
                rule.Execute(execution);
            }

            return objectToProcess;
        }

        /// Execute a single rule on a single data object
        public static T ProcessRule(T objectToProcess, RuleSet rule)
        {
            RuleValidation validation = new RuleValidation(typeof(T), null);
            RuleExecution execution = new RuleExecution(validation, objectToProcess);
            rule.Execute(execution);
            return objectToProcess;
        }

        #endregion

        #region Rules Management

        /// Loads a single rule given a path and file name
        public static RuleSet LoadRule(string rulesLocation, string fileName)
        {
            RuleSet ruleSet = null;

            // Deserialize from a .rules file.
            using (XmlTextReader rulesReader = new XmlTextReader(Path.Combine(rulesLocation, fileName)))
            {
                WorkflowMarkupSerializer serializer = new WorkflowMarkupSerializer();
                ruleSet = (RuleSet)serializer.Deserialize(rulesReader);
            }

            return ruleSet;
        }

        /// Loads a set of rules from disk
        public static ReadOnlyCollection LoadRules(string rulesLocation)
        {
            RuleSet ruleSet = null;
            List rules = new List();

            foreach (string fileName in Directory.GetFiles(rulesLocation, "*.rules"))
            {
                // Deserialize from a .rules file.
                using (XmlTextReader rulesReader = new XmlTextReader(fileName))
                {
                    WorkflowMarkupSerializer serializer = new WorkflowMarkupSerializer();
                    ruleSet = (RuleSet)serializer.Deserialize(rulesReader);
                    rules.Add(ruleSet);
                    rulesReader.Close();
                }
            }
            return rules.AsReadOnly();
        }

        #endregion
    }
}

This one class pretty much gives you all you need to create, load and save rules.  It’s a bit basic at this point in time, I will try to create a more robust and tolerant class in subsequent posts on this topic.  For now though, I think it adequately demonstrates the sort of functionality which can be gleaned from the Rules Engine.

You can create or edit a rule by using the LaunchNewRulesDialog or LaunchRulesDialog methods with minimal user input.  I’ve written a very basic Unit Test which proves how efficient this can be, but I’m sure you’ll be able to have some fun with it.

Next up, there are some functions to load existing rule files from disk, the aptly named LoadRule and LoadRules methods.  They are pretty self explanatory, I don’t think we need to go into too much detail about the loading of rules files.

Finally, there are some functions which can be called to execute rules against data objects.  At this stage I’m supporting the execution of a single rule against a single data object, or a collection of rules against a single data object.  Obviously you could easily expand upon this.  You may wish to consider a multi-threaded approach, I may be persuaded to implement a more robust solution which allows for concurrent multiple item/multiple rule processing if you leave a comment for me.

Finally, here’s the Unit Test which allows you to create a new rule and apply it to the test data defined in the test:

[TestMethod]
public void CreateNewRule()
{
    Employee testEmployee = new Employee();
    testEmployee.FirstName = "Joe";
    testEmployee.Surname = "Smith";
    testEmployee.Location = StateEnum.ACT;
    testEmployee.Manager = null;
    testEmployee.DateHired = DateTime.Now.AddYears(-1);
    testEmployee.EmployeeNumber = 99;

    string ruleName = String.Format("{0}UnitTestRule", DateTime.Now.Millisecond);
    string path = Assembly.GetExecutingAssembly().Location.Replace(Assembly.GetExecutingAssembly().ManifestModule.Name, String.Empty);

    RuleSet newRule = RulesManager.LaunchNewRulesDialog(ruleName, path);
    testEmployee = RulesManager.ProcessRule(testEmployee, newRule);

    Trace.WriteLine(testEmployee.FirstName);
    Trace.WriteLine(testEmployee.Surname);
    Trace.WriteLine(testEmployee.DateHired);
}

So, in this post we’ve had a look at a very basic solution structure which demonstrates a reusable rules design.  At the moment it is as close to useless as a demo usually starts off looking like.  I’m only getting started, once you are familiar with the ‘RulesManager’ wrapper concept, we’ll be ready to expand upon it significantly.

This is part 2 of a multi-part series.  I’ll be expanding upon the concepts shown here in subsequent posts.

Check back soon!

Solution Files

 

Recently I put my mind towards developing a fairly light rules based utility for applying various logical patterns on top of some linear business data.  Honestly, my initial thoughts were “oh no, not another boring rules based implementation” because we (ought to) know how boring that can be – and convoluted – but this time I decided to try something a little different.

Through no fault of my own, I’ve actually had very little to do with Windows Workflow Foundation – WF, not WWF which stands for World Wildlife Foundation, and not to be confused with the World Wrestling Federation, just to avoid confusion!

Most of the project I’ve worked on previously have used some other workflow product, usually something like K2 blackpearl or a custom implementation, for example.  In any case, I’ve always been curious – from what I’d heard it kicks Sharepoint’s “workflow” (if you could call it that) in the butt.  I’m all for that!

Now, from my admittedly sparse knowledge of WF, I knew there was some sort of Rules Engine sitting somewhere near some sort of Workflow for Business Analyst, and it reminded me of a demo I’d seen a little while back involving a graphical interface to design business rules.  Don’t fear, this is way cooler than that!

Anyhow, to cut a long and boring intro short (too late?) I got stuck into designing a quick and dirty rules engine for working with Data Transfer Objects or POCO entities.  Actually, you can pretty much use whatever object you like, it’s very unassuming :)

To start out, you need to add a reference to the following .Net assemblies, available in the .Net Framework v3.0 and onwards:

System.Workflow.Activities
System.Workflow.ComponentModel
system.Workflow.Runtime

So just add them to the project you are working with, and you’re half way there.  Next, lets start off by actually creating some rules, or at least a mechanism to do so.  This is almost too easy for words, so let us take a look at some code instead:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Workflow.Activities.Rules.Design;
using System.Workflow.Activities.Rules;
using System.Windows.Forms;
using System.Workflow.ComponentModel.Serialization;
using System.Xml;
using System.IO;

public static class CreateRulesHelper
{
    public static void LaunchRulesDialog(string outputPath)
    {
        RuleSet ruleSet = null;

        // You could pass in an existing ruleset object for editing if you wanted to, we're creating a new rule, so it's set to null
        RuleSetDialog ruleSetDialog = new RuleSetDialog(typeof(BasicTerm), null, ruleSet);

        if (ruleSetDialog.ShowDialog() == DialogResult.OK)
        {
            // grab the ruleset
            ruleSet = ruleSetDialog.RuleSet;
            // We're going to serialize it to disk so it can be reloaded later
            WorkflowMarkupSerializer serializer = new WorkflowMarkupSerializer();

            string fileName = String.Format("BusinessRule{0}.rules", DateTime.Now.Millisecond);
            string fullName = Path.Combine(outputPath, fileName);

            if (File.Exists(fullName))
            {
                File.Delete(fullName); //if it bleeds, we can kill it
            }

            using (XmlWriter rulesWriter = XmlWriter.Create(fullName))
            {
                serializer.Serialize(rulesWriter, ruleSet);
                rulesWriter.Close();
            }
        }
    }
}

See? This is a basic implementation inside a static class and essentially launches the WF rules engine editor.  I’m not doing anything fancy here, just taking whatever the user does and serializing it to disk (to be loaded later).  Some key things to point out here:

  1. I’m always creating a new ruleset – you could rewire this to allow editing of existing rules
  2. You may notice I’m using a random file name.  A more detailed implementation could take some input to form the rule name
  3. I’m using a specific object type (“BasicTerm”) – check back for Part 2 when I’ll rework this code so you can use it with different object defintions
  4. This is just a quick mock up!  Sample purposes only :)

Now assuming you had an object called “BasicTerm” and you compiled and ran this code, you’d get prompted with a window not unlike this one:

image image

The best part?  It has IntelliSense too!  You can create some very interesting IF..THEN..ELSE runs here, and you can create multiple rules per rule set.  The neat part is that you can modify the object directly, although it is somewhat limiting, it could be a very handy asset for something like message based routing rules (like in BizTalk).

I won’t go into too much detail at this juncture, other than encouraging you to have a play with this relatively simple interface.  Once you’ve set some rules (or even if you set none) – provided you click OK, you’ll get a .rules file written out to disk.  Check back for Part 2 where I’ll demonstrate how we can use these rules in a quick and dirty workflow.

Update:

I’m terribly sorry, it looks like I haven’t uploaded Part 2 of this intriguing article. I’m currently away, but will be back in a couple of days. I’ll post Part 2 and add a link here for those who are interested.

Further Reading

Introdution to the Windows Workflow Foundation Rules Engine

How to use Windows Workflow Rules



NEW: Continued
in Part 2

 

Introduction

Now if you are like me, you’ve probably had some interest in POCO (plain old CLR objects) objects for at least some time.  They are an invaluable tool in the distributed systems and service oriented architecture areas, but up until now they’ve been inaccessible for those designs.

In a nutshell, both LINQ to SQL and Entity Framework (v1) class entities did not support serialization for the purpose of stateless transport(such as web service communication).  This stems from the embedded context tracking attributes, and the design which stipulates a fairly poor experience for those daring enough to detach entities and “pass them around”.

Enter the ADO.net Entity Framework v2.. ahem, version 4 which shipped in the early part of this year.  Whilst the EFv4 doesn’t support POCO objects out of the box (you have to use an online template), it’s easy enough to accomplish with minimal effort.  Plus, they can used (almost) as seamlessly as non-POCO objects.

Before we get into the nitty gritty of this particularly long post, I will direct your attention to the following MSDN article which covers most of the steps for harmonious life with POCO objects and WCF services.  What the article does not cover is handling somewhat more complex object graphs.  In other words, the MSDN scenario is fine with fairly basic (and bland) objects, but it’s pretty nasty when you have objects containing, well, joins (collections, relationships, yada yada).

Now what follows, is based on a number of other articles floating around the Internet.  I’m not trying to take any credit for (the majority), I’m just collating the information into one handy to reach place.  I’m also going to supply sample code in case you have any trouble getting it all configured.  The parts which are my implementation alone, I’ll highlight.

The Data Model

First, let us take a quick look at the sample data model.  Nothing fancy, I’ll admit, but enough for our purposes:

DB-Schema

Which we will use with a WCF Service or two.  You can use the attached T-SQL script to create and populate a SQL Server database (and later generate your EDMX model from that schema).  Next, create a solution containing WCF services, and add a ADO.net Entity Framework (v4) model.  You can see from my sample below, the model is admittedly not very complex.  Notice the “self join” on the Category table.  This is not an uncommon scenario in designing parent/child relationships at the DB level.  It also has the (awesome) advantage of generating Parent/Child navigation properties (you may need to do some renaming if you generated the model from my sample schema).

The Object Model

image

Solutions and Settings

Once you have generated the model, right click anywhere on the blank model surface and select “Add Code Generation Item”.  This prompts you with a bulky dialog window – select “Online Templates” from the left hand side tree view.

Poco-1 Poco-2

Select ADO.NET C# POCO Entity Generator and click OK a few times as needed.  The template builds up the POCO entities and removes the EDMX/Designer based implementation which the EF designer would have originally generated.  This leaves you with a number of new files in your solution, which should look a lot like the following:

image

Web Services

Now that I’ve got your attention, lets have a think about how we’re going to expose these via WCF.  I’ve created two WCF Services, SystemLogService.svc and ProductService.svc. 
The interface definition of each is per below:

image image

Don’t worry about those attributes just yet!  I’ll explain a little about why they are necessary shortly.  If you have reviewed the original MSDN article you’ll recall:

“The POCO proxy type cannot be directly serialized or deserialized by the Windows Communication Foundation (WCF), because the DataContractSerializer serialization engine can only serialize and deserialize known types. The proxy type is not a known type. For more information, see the Serializing POCO Proxies section in the Working with POCO Entities topic. To serialize POCO proxies as POCO entities, use the ProxyDataContractResolver class to map proxy types to POCO types during serialization.”

Which means that the default (runtime) classes generated by LINQ/EF are incompatible with WCF because WCF requires classes defined at compile time. 

The Solution

As such, you need to both disable the use of Proxies, and also label your web service methods with the [ApplyDataContractResolver] attribute as seen above.  You can obtain the details about this attribute from the MSDN article or from my sample solution.  You only need to use it on the service side.  This is as simple as creating a new class and pasting the implementation from either source.  Then add the attribute to decorate your web service definition (on the interfaces).

image

Now, for the part not previously covered – we generally encounter a problem with passing entities which are a little more complicated than the example POCO objects encountered in the MSDN article.  Take our sample application.  The System Log entities define a basic relationship, and the products include a (fairly standard) self join, allowing product categories to have a hierarchy.

If we then create a standard console application, and add a web service reference, we can observe the class definition from the generated WSDL (below). 

image

If you’re unsure about how to view the WSDL code within Visual Studio, simply follow these steps:

  1. Right Click on the Service Reference
  2. Select “View in Object Browser”
  3. From here, expand the namespace of the reference, then right click on one of the interfaces
  4. Select “Go To Definition”

image image image

Now assuming you have done everything correctly, you should be able to consume the web services and the POCO objects in your console application:

image

 

Execution

If we execute the code, the first web service call returns fine, with no errors.  The second call however, which returns a collection is not as fortunate.
When we step over the following line of code, we receive an exception with the following message:

SystemLog[] logs = logClient.GetLogEntryByCategoryId(1);

“The underlying connection was closed: The connection was closed unexpectedly.”

 

image 

Looking deeper into the service side of affairs (debugging), we may discover that the exception being thrown is, in fact, the following:

There was an error while trying to serialize parameter http://tempuri.org/:GetLogEntryByCategoryIdResult. The InnerException message was ‘Object graph for type ‘Products.WcfServices.SystemLogCategory’ contains cycles and cannot be serialized if reference tracking is disabled.’.  Please see InnerException for more details.

After a fair amount of searching, I found a way to work around this little problem.  Implementing the suggested attribute [CyclicReferencesAware(true)] to methods involving collections appears to fix the problem.  After applying the attribute and updating the service reference (just to be sure!)  you will find the call succeeds, as per below:

image

 

But Wait.. There’s More..

Just when you thought it was safe to go back into the ocean..  What happens when we want to send things the other direction

Let’s look ahead to a web service method which takes one of our POCO objects, and tries to apply an update.
The logic I’ve used here detects a new entity, and also when an existing entity can not be located in the data store.

image

So nothing terribly complicated, correct?  If we implement something on the client side – something very simple, like the following:

image

When we try to execute this rather simple update scenario, we get the same kind of exception we’ve seen before:

image

 

I love it when a plan comes together..

So what is the solution?  Well, rather simple, if somewhat complex in the implementation. 
The outcome I found which works quite well is to emit the same attribute into the generated WSDL on the client side, when the reference is created.
This turned out to be a pretty straightforward idea, but a terribly intriguing problem to try to solve.

Without delving too much into details (please download and examine the sample solution) the basic premise was two fold:

  1. Define the required files in a common or shared assembly that both the service and the client project can consume.
  2. Build a class which implements several WSDL extensions: IWsdlImportExtension, IServiceContractGenerationExtension,IOperationContractGenerationExtension and IOperationBehavior

Basically, the class is triggered when the WSDL is being imported, and it adds the appropriate [CyclicReferencesAware(true)] attribute above the appropriate methods. 
To do this, you must modify the client’s App.Config to include the following configuration:

image

When the WSDL import is called, the referenced extension finds operations decorated with the CyclicReferencesAware attribute (the export decorates them with a documentation text).
When an operation decorated with the attribute is found, the importer adds (writes) a reference to itself to the operations’s behavours collection. 
As the WSDL is being generated, it’s a relatively easy step to output the required attribute.

Now, when if you update the service reference the appropriate attribute is applied to the generated WSDL code, as you can see from the screenshot below:

image

Side Notes

The only thing I didn’t figure out was how to add the required using directive to the generated code, however it is very easy to add the reference yourself – just compile the client project and you’ll get the appropriate errors. 

Double click on one, right click on the reference and you can easily add it to the code.  I realise it’s a bad practice to modify generated code, but I ran out of patience and figured this wasn’t a terrible oversight.  If you find a nice way to fix this, please get in touch.

Running the solution after updating the configuration (and referencing the shared assembly) and now the previous code runs just fine.  You can check the database to ensure the update occurred.

image

 

Summary and Disclaimer

Thus far, I haven’t had much time to test this any further.  I’ve implemented it on a number of web service clients without any problems. 
I’ve not tried any further complicated scenarios, but I’d really appreciate any feedback if people find further problems.

To wrap up, I’ve included the sample project and T-SQL to create a database.  This is not production code, so please use it as a demo. 
There’s no encryption, compression or other types of scenarios we might encounter in a complete system. 
It is supplied “As-IS” and no warranty is implied :)

As always, if you have any feedback please leave it here or get in touch.

Seriously though, I sincerely hope this might help out some folks who are as intrigued and equally baffled with WCF and the Entity Framework.

Bon Appétit.  /R

[ Download Sample Project and Schema ]

Additional Reading

http://blogs.msdn.com/adonet/archive/2009/12/22/poco-proxies-part-1.aspx
http://blogs.msdn.com/adonet/archive/2010/01/05/poco-proxies-part-2-serializing-poco-proxies.aspx

MSDN Walkthrough on POCO Entities

http://msdn.microsoft.com/en-us/library/ee705457.aspx

The source for the cyclic check is courtesy of:

http://chabster.blogspot.com/2008/02/wcf-cyclic-references-support.html

 

This is part of a series of entries written about Microsoft’s new SQL Azure database service and the Entity Framework v4.

Following on from my previous posts (check them out before continuing) – this article assumes you have followed steps outlined in the  previous posts to create various models and accounts etc.

Continuing along..

Our next step is to create a Dynamic Data website.  If you haven’t come across this yet, it’s most likely because you haven’t been using Visual Studio 2010 or the .Net Framework 4.0.  Recently introduced and compatible with both LINQ-to-SQL and the Entity Framework, this nice site template makes use of the dynamic nature of both LINQ-to-SQL [.dbml] (SqlMetal) and Entity Framework [.edmx] data models.

Continuing within the solution we created in Part 3, we shall now add a Dynamic Data website to our solution.  Open the solution in Visual Studio 2010 Beta 2 and then Right click the solution, “Add –> New Project..”.

Please note: this portion of the solution will also work in conjunction with any other database provider supported by the Entity Framework, not just SQL Azure.  To use a Dynamic Data site all you need is a LINQ-To-SQL or Entity Framework Data Context!  For details, read on..

image

I’ve called the new project “SQLAzure.Application.Web” and it sits in a subfolder off the root folder level for the solution.  Once you click the OK button, you’ll be presented with the Global.asax.cs (or .vb) which, at first, I found a little unusual.  We’ll explore why shortly.

First things, let’s add a project reference to our DataAccess project.  Right click the References solution folder and add the reference.  You might notice that the project already has the appropriate references for the Entity Data objects.  At this point if you compile, it will build without any errors (and if not, you are off to a bad start indeed!).

Configuration Bliss

There are some configuration options which will need to be set.  First, double click on the web.config and copy your <connectionString> values from the app.config located in the DataAccess project.

image

Once you’ve updated the Web.Config, save and close it.  You should now be looking at the aforementioned Global.asax.cs (or vb).  To get us up and running, with the very minimum of configuration (and hassle), we’ll keep the default settings (using the ASP Development Server a.k.a Cassini).  Right click the Web project in the Solution Explorer and “Set as StartUp project”.

Web Scaffolding and other treats

Next, in the Global.asax.cs (or .vb) in the method “RegisterRoutes” uncomment all the code lines, remembering to substitute the value (line 31) “YourDataContextType” with the name of the actual data model context (in our case, SqlDataModelContainer) and you’ll also need to add “using SQLAzure.Application.DataAccess;” at the top of the file.

In the first line, change { ScaffoldAllTables = false }); to { ScaffoldAllTables = true });
We can always customize the data model later to only scaffold specific tables (if we want to).

Once this is all done, you should be able to cleanly compile the project/solution.  Assuming you’ve followed all the steps I’ve outlined, your Global.asax.cs should look like the following:

image

Compile and run the solution (without debugging).  You should notice the ASP Development Server load into the System Tray, and then your default web browser should open and browse to your nice new site.  If you get the following error message:

Server Error in ‘/’ Application.


There are no accessible tables. Make sure that at least one data model is registered in Global.asax and scaffolding is enabled or implement custom pages.

It means you haven’t changed the value on Line 31 from false to true:

image

You *should*, assuming everything has been correctly set, see the following in your web browser, a list of tables in your SQL Azure database!  If you click on the “Albums” link, you should drill into a paginated view of the first 15 records in the Albums table in your nice SQL Azure database in the cloud..

Prepare to be amazed!

image image

Notice the combo boxes in the top left hand corner?  Yes, the site has automatically created those so you can filter the results according to foreign keys in the entiry relationship model – very nice.  Go ahead, select an artist.  I’ve selected “Bob Dylan”:

image

…but wait, there’s more..

That was pretty effortless, wasn’t it?  You want more functionality?  Of course – click on the “Edit” button for the first result.  You’ll be treated to this “details view” of the record:

image

You’ll notice, of course, that again the site has detected all the relationships (including many-to-many) and created combo boxes/checkboxes according to the cardinality of the relationships.  You may edit any of these settings and hit the “Update” button – you are now updating your data in the cloud!

Another nice touch is that you can navigate the relationships (hyperlinked) as well.  All the tables are inter-related (where applicable).  I’ll leave you now to play with your shiny new website, backed with a SQL Azure data store.

The big picture

This was, by far, the easiest website-to-database project I’ve ever wired up, period.  The functionality is uncanny for an out-of-the-box template driven site, and what’s more, if you hook it up to a Server instance (as opposed to SQL Azure) you can run SQL Profiler and see that the queries (T-SQL) themselves aren’t terrible, either.

This is amazing value – foreign key filters, paginated data views, complete with full edit/delete functionality.

However, it also has some additional work which should be addressed in any decent implementation, for example, security and brevity (reducing the footprint of the tables).  We could also do with some optimization and customization!  For example, the screen (1) below could obviously be truncated as the results run right off the screen, and clearly look better in the “Details” view (2).

image image

There are many more things which a “production” quality implementation would require.  I may (or may not) go into details in future posts, but off the top of my head you would need to address any of the following:

- Move from Cassini to IIS (should be done early for serious multi-access websites)
- Add a custom App Pool and Identity
- Strong name the assemblies
- Edit all the templates with a scheme
- Restrictions/reductions in the data displayed in “grid views”
- Optimization of some T-SQL to suit the data model design
- Implementing a security model to restrict read/write/deletion of data
- Auditing changes
- Create an automated build and deploy script

However, this is a great start and would probably suit a number of “intranet” style applications.

Looking ahead

In my next entry, we’ll look at some customizations as well as the technology underpinning this great functionality.  I do hope you are enjoying this series of entries, please leave comments!

 

Happy Australia Day to you!

Following on from my previous posts (check them out before continuing) – this article assumes you have followed steps outlined in the  previous posts to create various models and accounts etc.

Now we should have a working data model which has been created in your SQL Azure (Cloud) database.  Taking lessons learned from Part 2, we’re going to create an Entity Framework (v4) model of the SQL Azure tables using Visual Studio 2010 Beta 2.

This entry is sort of a rehash of the first and second entries, but the outcome should provide you with a fully functioning Entity Framework (v4) data model, and a Unit Test to prove it works!

Creating the Model

Fire up Visual Studio 2010 (Beta 2).  Next use “File –> Project..” and choose a new Class Library.  You can name the new Library whatever you like, but I will be calling it SQLAzure.Application.DataAccess as you can see in the following screen shot:

image

Now, the first thing I do when I create a new solution or project is to add a corresponding Unit Test project, so we can code out some Unit Tests as we implement new functionality.  Let’s do that now.

If you can’t see it, the Solution node in the Solution Explorer can be set to “Always Visible” by going to “Tools –> Options”, “Projects and Solutions” and check the checkbox next to “Always show solution”.

image image

Now, right click the “Solution” in Solution Explorer and select “Add New Project”.  You want to select a new Test Project (select the Test node under the tree list on the left hand side).  I’ve called my test project SQLAzure.Application.DataAccess.UnitTests.  Once added, right click on the “References” solution folder and select the DataAccess project.  You’ll also need to add a reference to System.Data.Entity.

Now, let’s delete those default files added to the projects (Class1.cs and UnitTest1.cs) which should leave us with a fairly vanilla solution, like so:

image

Now let’s configure that Data Model!  In the DataAccess project, right click and “Add –> New Item..”.  Select “Application Configuration File” then press “Add”.
Note that the file opens in the Text Editor after it has been added.  Copy and paste the following code between the <configuration> tags, then replace the items in bold with the proper values for your SQL Azure database:

<connectionStrings>
    <add name="SqlDataModelContainer"         connectionString="metadata=res://*/SqlDataModel.csdl|res://*/SqlDataModel.ssdl|res://*/SqlDataModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=[servername].database.windows.net;Database=Vinyl;Uid=[user]@[servername];Pwd=[password];&quot;"
         providerName="System.Data.EntityClient" />
  </connectionStrings>

Next, on the DataAccess project, right click “Add –> New Item..” and select “Data” from the left hand tree.  Name the file “SqlDataModel.edmx” and click “Add”.  On the next screen, select “Empty Model” and then click “Finish”.

image

A blank model will open in the editor.  Now, assuming you’ve followed all these steps correctly, you should be able to right click the model and select “Update Model from Database…”.

You should find yourself looking at the “Update Wizard”’s “Choose Your Database Objects” window.  If you are not, it means your App.Config probably has incorrect values.  Refer to my first post for how to fix any issues you may encounter.

image image

Select all the Tables in this dialog and click “Finish”.  Barring any unexpected errors, you should now be looking at a fully articulated Entity Framework model of the schema we previously created and populated in SQL Azure.

image

Save the file and close it.

Verifying it all works!

Now, right click on the UnitTests project and select “Add –> New Test..” and select a “Unit Test”.  I typically add a “Smoke Test” set of tests which look for basic connectivity and authentication.  You can see from the following screen capture:

image

Next, remove all the template generated plumbing (you won’t need it for these basic tests, and rename the initial test to be now called “BasicDataAccessTest”, as follows:

image

Now, we’re going to need to add a config setting.  We’re going to add a link to the App.Config file you created under the DataAccess project.  Right click the UnitTests project and select “Add –> Existing Item..”.  Browse to the App.Config file under the DataAccess project (you may have to change the file filter to “All Files (*.*)” to see it).  Instead of clicking “Add”, click on the little downwards arrow on the Add button and select “Add As Link”.

image

Now, a reference to the DataAccess’ App.Config exists, thus no need to duplicate the connectionString settings across multiple projects, just one config for all.
Next, copy the following code into the body of the BasicDataAccessTest function (you will need to add “using System.Diagnostics;” at the top of the file): (apologies for a lack of colouring)

[TestMethod]
public void BasicDataAccessTest()
{
    using (SqlDataModelContainer e = new SqlDataModelContainer())
    {
        var albumsCount = (from a in e.Albums
                                   select a).Count();

Trace.WriteLine(String.Format("Total records found: {0}", albumsCount));
    }
}

Now, click on the Test menu and select “Windows –> Test View”.  You might need to click the little “Refresh” button to see the renamed Test Method.

imageimage

The proof is in the pudding

Assuming you’ve followed all the steps thus far, you should be able to successfully run this test now.  If you double click on the test result, you should see the following:

image

If you haven’t managed to get this test to pass, then you must have some problems with your configuration.  R
e-read this entry and you should manage to have it all wired up correctly.  Please drop me an email (rob.sanders [at] gmail.com) if you encounter any issues, I’m more than happy to try and help.

Conclusion

Now, assuming you have achieved success in wiring up this simple Unit Test, you have a working data model and working SQL Azure database instance.  Congratulations!

In my next article, I’ll show you some of the more complex queries we can perform, as well as begin to introduce you to some of the amazing functionality which is now available to use, because you have a working Entity Framework (v4) data model.

Check back soon for the next entry.

 

This post is the first in a series of posts on this topic. 
The following is a summary of the various parts.

Introduction

Well, it’s been “out there” for a while, and now it is time I gave you a taste of how to combine some of the “Next Generation” data access technologies, today.  SQL Azure is Microsoft’s “Database in the Cloud” and represents a complete makeover (replacement) of the first attempt, SQL Data Services (SDS).  SDS is no longer, SQL Azure is the future.

One of the biggest knocks on SDS was the lack of a relational database.  Everything was done according to vertical data “silos” and cross-silo querying was painful (and almost impossible).  Unfortunately, administration was also tricky (well, worse) and it veered quite severely from the established SQL world which we know and love (or submit to).

Now, I’m going to walk you through the process of establishing a working Entity Framework (v4) model which runs against a SQL Azure database – a database in the “Cloud”.  This is not, by any means, a definitive guide.  In fact, I only just got this working myself this morning.  It relies on a few creative “workarounds”, which I will explain in due course.

This example is only intended as a “Proof of Concept” and has not exhaustively been tested. 
Attempt at your own peril (usual conditions and waivers apply)!

Prerequisites

What you will need prior to following along:

All of the above can be obtained for free, from the links above.  You may have to wait for a SQL Azure invitation or token.  My tokens were obtained a little while ago.
I’ll consider writing a separate article on how to set up SQL Azure, so if you are interested send me an email or leave a comment here.

Starting Off

This article assumes you are familiar with Visual Studio basics.

Once all the tools are installed, and you have configured SQL Azure for the first time (this involves setting up a master user account and password) log into the Administration console and create a database – just click “Create Database” and give it a name of “Sample”.

image

You can now get connection strings by clicking on the “Connection Strings” button.  Your server name is the key thing here.  Make a note of it.

Next, you may need to add your IP address to the Firewall settings.  To do this, click on the “FirewallSettings” tab, and add a new Rule using your IP address (your IP address is listed on the screen).

Now, fire up SQL Management Studio R2 (Beta).  On the initial dialog, enter the full name of your SQL Azure database, e.g. [server].database.windows.net and for the Username, use the one you configured initially (use SQL Authentication, not Windows Authentication), in the form of [user]@[servername] and, of course, your password, which you used to configure SQL Azure initially.

image

Now, you should be able to successfully connect to SQL Azure from SQL Management Studio.  If you can not connect, recheck your config values, it’s really just servername, username@servername and the correct password.  You may also need to add a firewall rule to allow your IP address to SQL Azure (see above).

Ensure you are using SQL Management Studio 2008 R2 Beta (November CTP) – earlier versions are not supported, so this will not work with the Server 2008 RTM version or prior.

Creating a Test Database

Please note: The following parts in this post introduce you to concepts which will be used in later articles.  You may safely skip the rest of this post if you are only interested in establishing the application and data model for the project.  Otherwise, keep reading and you’ll get a full understanding of how we intend to map a SQL Azure database to a working Entity Framework model.

image

Everything here is done via T-SQL script (unfortunately) so to create a new table we’ll have to create it.  Here’s a sample T-SQL script you can use.  We’ll create a table called “DailyStats” which has an Identity PK, a text field and a date field.

– =========================================
– Create table template SQL Azure Database
– =========================================

IF OBJECT_ID(‘DailyStats’, ‘U’) IS NOT NULL
  DROP TABLE DailyStats
GO

CREATE TABLE DailyStats
(
    StatId int IDENTITY NOT NULL,
    Data nvarchar(150) NULL,
    Created datetime
NULL
    CONSTRAINT PK_sample_table PRIMARY KEY (StatId)
)
GO

Next, we can populate the table with a row of test data, so we can do some simple selects later.

INSERT INTO [Sample].[dbo].[DailyStats]
           ([Data]
           ,[Created])
     VALUES
           (‘Blah Blah Blah’,
           GETDATE())
GO

Now, we’re pretty much done with SQL Azure for the time being.  Close SQL Management Studio, and fire up Visual Studio 2010 (Beta 2).

Creating the Entity Model

Create a new Console Application.  Once you are ready, in the Solution Explorer, add an “App.Config” (right click under the project in Solution Explorer, “Add” –> “New Item”, “Application Configuration File”) file to your solution.  Once you have done so, open it in the editor and add the following:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <connectionStrings>
    <add name="SampleEntities"         connectionString="metadata=res://*/SqlAzureModel.csdl|res://*/SqlAzureModel.ssdl|res://*/SqlAzureModel.msl;provider=System.Data.SqlClient;provider connection string=&quot;Data Source=[servername].database.windows.net;Database=Sample;Uid=[user]@[servername];Pwd=[password];&quot;"
         providerName="System.Data.EntityClient" />
  </connectionStrings>
</configuration>

Make sure you change the items in bold to their correct values.  For the .csdl, .ssdl and .msl entries (the first three values in bold, above after the name value), use the name of the Model (file name) you are planning to use, e.g. I am using the name “SqlAzureModel.edmx”.

Now, in Solution Explorer, right click and “Add” –> “Add New Item”, from the Tree, select “Data” and select “ADO.NET Entity Data Model”.  If you can’t find this, make sure your project is a .Net Framework 4.0 Project!  This can be found in the project’s “Application” tab, under “Target framework”.

Now, just create an Empty Data Model.  You can specify your own name for the model, but you will need to edit the app.config.  Once you have added your new Model, expand it in the Solution Explorer and double click the associated “.Designer.” file.

image

image

Make note of the value of the Constructor in the Contexts region (mine is as follows):

public SampleEntities() : base("name=SampleEntities", "SampleEntities")
{
    this.ContextOptions.LazyLoadingEnabled = true;
    OnContextCreated();
}

You need to ensure your ConnectionString in the App.Config has the same name as the text in bold (whatever your Context is called).  This is the “hack” workaround.

image

Now, assuming this is all configured properly, you can double click the .edmx file and you should see an empty model.  Right click on the model and select “Update Model from Database…”.

image

Assuming you have configured the connection string properly, you should see the following dialog (note we can see the table we created in SSMS, earlier).  If not, just ensure that the key in the App.Config file matches the Connection String expected by the .Designer. file (see above).

image

Once you see the above dialog, ensure you have selected the table, and we can update the model.

Once created and saved, open the Program.cs (or vb) file and add the following code:

static void Main(string[] args)
{
    using (SampleEntities e = new SampleEntities())
    {
        Console.Write(e.DailyStats.Count());
        foreach (DailyStat stat in e.DailyStats)
        {
            Console.Write(stat.Data);
        }
    }
}

You should be able to execute this code now, and it should print the value (1)  representing the one row in the DailyStats table and the value (blah blah blah) which is the value found in the single row under the “Data” column.

I do realise this is a very basic example, but it does introduce you to a number of fundamental ways to interact with your SQL Azure database.  You have no successfully used SQL Management Studio 2008 R2 and Visual Studio 2010 (Beta 2) to contact SQL Azure.  To top it all off, we have (hopefully) managed to use the Entity Framework to query real dat
a in the database.

This is an important first step.  In the next article we will delve deeper into the supported functionality.  For now, it may help to read ahead on the limitations of SQL Azure:

MSDN: Guidelines and Limitations (SQL Azure)

That’s it for now – you have now successfully (hopefully) connected to your SQL Azure database using the Entity Framework v4.  Check back for my next entry on this topic soon.

 

This is a little late – but I feel it is worth going into and you can catch up be reading (or re-reading) Part 1 located here.  Just in case you are wondering “what on Earth is he babbling on about?” which is a fair and often under asked question, here’s a brief summary to refresh your memory.

The objective is to implement additional (background) functionality which is not seen by calling/consuming code – hence, transparent.  In fact, it’s even possible to enforce execution of certain functionality no matter how a DataContext is manipulated – neat, huh?

So let us look at a simple database table and let us further assume you have generated a LINQ to SQL Model:

image image
SQL Database Table              LINQ to SQL Data Model

Now, let’s assume you will at some point create and update a Movie entity. 
In code, by way of a simple example, you might have something like this (this is a primitive example):

image

Now – assuming you have a requirement to Audit all changes made to Movie data, how will you go about ensuring you capture all the changes made via LINQ to SQL – in this case, capturing the change from “New Movie” to “Newer Movie”?

Depending on your architecture and design, you’ll have various options.  If you want maximum flexibility, then functional transparency might serve you well.  Here is how you might accomplish this task, using the DataContext.

Now, first set a common base class for the Entities. 

By default they will be generated without a common base.  To do so using Visual Studio, you may edit the class definitions directly, or use SQLMetal to generate the entities.  If you have a large data model, it might be recommended to use SQLMetal from the command line.  For our purposes, the base could just be a public class with little to no implementation.  Here I have added a base class to the Movie class definition:

image

Once you have set a base class, it’s time to establish our auditing functionality.  Extending the Data Context is the next step.  This is easier than you might think.
Add a new class to your solution.  Ensure it is in the same namespace as your generated entities and data context.  Give it the same name as your data context and ensure it is declared as a partial class.

For our example, we want to capture changes to the Movie table, so we implement the “UpdateMovie” function, by adding our own implementation.  Don’t worry about the actual auditing part just yet.  The important thing to learn here is that “ExecuteDynamicUpdate(instance)” must be called, or no actual update will occur to the entity.

image

Now, let us look at the Auditing functionality.  I’ve implemented this functionality in a public static class called “Audit”.  The function AuditChanges, accepts an entity derived from the base EntityBase and also takes the associated DataContext. 

All that remains is to check if there are any pending changes and, if so, we can obtain them using the DataContext’s GetModifiedMembers function.

image

Now you have access to the original values and the modified values for each changed property.  You can log the changes any way you like.  Problem solved.  This implementation allows you to use the same routine to audit from any of the entities, assuming you use the same base class, and implement the extended Data Context. 

So to audit changes to the Actor table, you’d simply add this to the extended DataContext:

image 

Hopefully this is a good enough example to introduce to you the merits of extending your DataContext.  This is bu
t one minor example.

Other scenarios could include encryption/decryption of data stored in the database, inserting related records, setting concurrency (or checking timestamp values) and the list goes on.

None of this code here (in this blog entry) is production ready however It has proven on a large enterprise project to be a very reliable approach.  You can factor this approach into your own logging, auditing and exception management design.

Feel free to contact me or leave comments if you have any questions.

P.S. Here is a cleaner shot of the Audit function:

image

 

I’m going to coin the phrase ‘Functional Transparency’ because it fits the definition.  I’ve been working with functional transparency in LINQ to SQL for a few weeks now, and it seems to be working well enough to write about. 

The objective is to implement additional (background) functionality which is not seen by calling/consuming code – hence, transparent.  In fact, it’s even possible to enforce execution of certain functionality no matter how a DataContext is manipulated – neat, huh?

In other words, we implement additional functionality between the DataContext, its entities and the Database itself.  Our objective may be to modify the data returned from a query (or data saved to a database), might be to audit operations or even check access permissions – but all done so without the knowledge of the calling code. 

A perfect scenario would be for encryption/decryption of column (property) values (i.e. values are stored encrypted but read/written in clear text)…  Another great use would be for item tracking or auditing.

For example, we might want to be sure that when an entity is saved or deleted, that a record is made of the change.  We could do that without requiring an explicit call to a logging or auditing routine.

The LINQ to SQL object model allows us to work behind the scenes to facilitate this kind of functionality.  To work our magic we need to extend the working Data Context – mostly due to the nature of LINQ to SQL.  So by simply using partial classes we can easily extend the entity classes and the Data Context class and implement as necessary.

There are a couple of other issues which may crop up when working a little more closely with the Data Context and Entity definitions – beware the DBML! 

LINQ uses it’s own style of optimistic concurrency by updating and then retrieving a row back by executing a select command with the updated parameters in a where clause.  This is problematic if values are not likely to be the same between database reads.  To get around this some columns (timestamps, for example) require the UpdateCheck=Never attribute to be added to the DBML.

In the next article in this series, I’ll go into more detail, and also start providing examples of how we can implement some seriously wicked transparent functionality.  Hopefully the power of this flexible approach will become apparent.

 

Undoubtedly, anyone who has evaluated LINQ to SQL has fond it a fairly powerful yet lightweight ORM technology which is less complex than the ADO Entity Framework yet utilizes the strength and power of Language Integrated Queries.

One problem with LINQ to SQL is the auto paging feature of the LinqDataSource.  Below is a rough GridView which displays three columns, UserName, FirstName and LastName.  This is just a rough demo, so we’re looking at paging.

     image

If you simply drop a GridView and a LinqDataSource control onto a Web Form and configure the LinqDataSource (using Smart Tags) without specifying a Group By field or Order By field (Figure 1) then you will get fairly optimal database querying (Figure 2) although without any ordering.

     image 
     Figure 1: Configure Linq Data Source

     image 
     Figure 2: SQL Trace of a page load

While it is great for lightweight or simple applications, it’s rather unacceptable for use in anything serious (especially with more complex queries). 

Should you supply a Group By/Order By field, the LinqDataSource control will query for a RowCount before executing a single query for each row in the range, i.e. if Page Count is 10, it will execute 10 queries after the initial row query (Figure 3). 

     trace-1
     Figure 3: SQL Trace when using Group By

This occurs when you choose to use a group by/order by sort – The select clause of the LinqDataSource becomes:

     GroupBy="UserName" OrderGroupsBy="key" Select="new (key as UserName, it as Users)"

Obviously we’d like decent performance and the ability to sort/group our data.  So there is a pretty low-overhead solution.  The first (obvious) step is to disable the LinqDataSource AutoPage property.  The next is to implement an event for LinqDataSource’s OnSelecting event, as so:  (note: I’ve included the GridView for reference)

<form id="form1" runat="server">

  <asp:GridView ID="UserGridView" runat="server" AllowPaging="True"
      DataSourceID="UserLinqDataSource" AutoGenerateColumns="False">
      <Columns>
          <asp:BoundField DataField="UserName" HeaderText="UserName" ReadOnly="True"
              SortExpression="UserName" />
               <asp:BoundField DataField="FirstName" HeaderText="FirstName"
              SortExpression="FirstName" />
               <asp:BoundField DataField="LastName" HeaderText="LastName"
              SortExpression="LastName" />
      </Columns>
  </asp:GridView>

  <asp:LinqDataSource ID="UserLinqDataSource" runat="server" AutoPage="False"
      ContextTypeName="DataAccess.DataClassesDataContext"
      OnSelecting="UserLinqDataSourceSelecting"
      Select="new (UserName, FirstName, LastName)" TableName="Users">
  </asp:LinqDataSource>

</form>

Now for our code behind, we only need to implement the Selecting event, and we have our smarter paging enabled.

                /// <summary>
        /// Implements Server Side Paging for the LinqDataSource
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void UserLinqDataSourceSelecting(object sender, LinqDataSourceSelectEventArgs e)
        {
            /*
             When AutoPage is false, LinqDataSource requires that the user handle the paging
             manually during the Selecting event.  In this case, you need to set
             DataSourceSelectArguments.TotalRowCount yourself and perform the paging manually

             using DataSourceSelectArguments.StartRowIndex and            
             DataSourceSelectArguments.MaximumRows (pageSize).
            */         

            e.Arguments.StartRowIndex = 0;
            e.Arguments.MaximumRows = 10;                //add your paging limit requirement here
            DataClassesDataContext dc = new DataClassesDataContext();

            e.Arguments.TotalRowCount = dc.Users.Count();   //you could store this value or cache
                                                            //it to avoid the extra DB hit

            //uses an example of ten records/page modify to fit your own paging
            //requirements                                
            e.Result = (from i in dc.Users select i).Skip(UserGridView.PageIndex * 10).Take(10);

            //uses Linq’s Skip() and Take() functions to select a sub section
        }

Let’s take a look at the SQL profile trace, just to be sure:

     alt="image" src="http://byfiles.storage.msn.com/y1pALcXoBV6stTj5NZsZuk_XI4ZPxrmDfEbNajaEHxCJlA7IZDC2GQDPw-to7v5qhMvr8nqcJAYnng8izH51uR6bQ?PARTNER=WRITER" width="418" border="0"/>
     Figure 4: New SQL Trace with custom paging

So this is just a simple little scenario.  It’s nothing big.  It might help some people out there who are looking to use LinqDataSource but don’t like the overhead of the Auto Paging.  You’d probably also need to take a look at supporting sorting (which would need to be factored in to the custom paging query).

This entry doesn’t discuss management of Data Contexts or a plethora of other considerations which you should take into consideration before implementing a solution.  Please plan ahead accordingly before choosing an approach.

If you’re serious about using Linq to SQL I’d highly recommend you take a look at Patterns and Practices ‘ObjectContainerDataSource’ instead.  It allows you to wire up your data access to queries or providers and is far more robust.  There’s a good chance I might write a separate blog entry about the ObjectContainerDataSource.

More reading:

An alternative approach to custom paging
[ http://www.mikesdotnetting.com/Article.aspx?ArticleID=71 ]

Web Client Software Factory (Contains ObjectContainerDataSource)
[ http://msdn.microsoft.com/en-us/library/bb264518.aspx ]

Web Client Software Factory Source Code – February 2008
[ http://www.microsoft.com/downloads/details.aspx?FamilyId=8AF8F61D-558F-481F-BC83-E42D9B04C3E9&displaylang=en ]

How to use the ObjectContainerDataSource
[ http://msdn.microsoft.com/en-us/library/cc304832.aspx ]   

Aussie Wine Guy


© 2012 Rob Sanders: Sanders Technology Suffusion theme by Sayontan Sinha
WordPress SEO fine-tune by Meta SEO Pack from Poradnik Webmastera