|
|
My blog covers the technology areas I focus on here at EMC Consulting, namely Architecture using the .NET Framework, ASP.NET, WCF, ADO.NET Data Services and Agile development practices.
Technorati Profile
-
ADO.NET Data Services is elegant technology that feels very easy to work with. The majority of current scenarios for using ADO.NET Data Services involve exposing an Entity Data Model (EDM) implemented using the ADO.NET Entity Framework. Most of the time, this just works, but it can throw you when you do face problems; how exactly do you diagnose issues in data services, either at design time or in production?
In this blog post I will cover a common check list that should be followed when things haven’t gone to plan; either the service has never successfully run, or has stopped working in production. I will cover the techniques available to you to diagnose problems with your service.
Common Issues
Below is a checklist of issues that are common causes of data services not functioning properly.
Issue 1 : No database connectivity
It sounds obvious, but many data services appear not to be working due to an incorrect database connection string.
Start by checking the connection string that is being used by the Entity Framework. The connection string should be in the <connectionStrings /> section of your service host’s web.config file. Use the server explorer to test the connection credentials. Ensure that the CSDL / MSL / SSDL resources are set to be embedded in the EDMX designer.
Issue 2 : Your EDM is using features not supported by ADO.NET Data Services
Not everything that you can do in the Entity Data Model is supported by ADO.NET Data Services.
The first and most common issue here is the use of EDM types that are not supported by data services. A complete list of EDM types supported by ADO.NET Data Services can be found here.
The second issue you may have when surfacing your EDM is that you have used a form of inheritance not supported by data services. The Entity Framework supports table based inheritance, where the conceptual type must be mapped to a table that represents the sub class. This will only work in the data service if the sub class contains additional properties that are contained in the underlying table.
Issue 3 : The database user account does not have access to the required database objects
Check that all your database objects (tables, view etc) can be accessed using CRUD based SQL with the account that your are using in your database connection string.
Issue 4 : Check you have granted appropriate access to the required entities
By default, no entities from an EDM are surfaced by ADO.NET Data Services unless you specifically grant access to the entities. Check your data service’s Initialize method to ensure that you have granted the appropriate access. Conversely, ensure that there is no code grant access to entities or service operations that do not exist in your service.
Issue 5 : Invalid custom service operation or query interceptor
Custom service operations require the WebGetAttribute to be applied to the method.
If you have stubbed out your service operation, you must ensure that the operation returns a valid IQueryable object (don’t just return null!).
For more information on service operations, look at the MSDN documentation here.
How exceptions are thrown and handled by ADO.NET Data Services
ADO.NET Data Services are build on top of a WCF WebHttpBinding channel stack and use the complete WCF infrastructure to expose a service endpoint. Therefore, any unhandled exceptions from the data service will cause WCF to use its standard fault mechanism to return a response to the client. These faults need to diagnosed using the standard approaches to WCF faults as described later on in this post.
Exceptions handled by a data service implementation causes an HTTP 404 error response. This is because ADO.NET Data Services is a good RESTful citizen. If you see this behaviour, you know that an exception has occurred within the data services runtime.
Diagnosing exceptions handled by the data service at runtime
As mentioned above, ADO.NET Data Services return a standard HTTP 404 message when the service encounters an exception of any kind. To understand what is going wrong in your service, you need to switch on verbose errors in the service’s initialization. However, this should not be used by a service running in production, because it will stop the service returning a standard HTTP 404 error code. This following service initialization code will switch on verbose errors:
/// <summary> /// Initializes the service. /// </summary> /// <param name="config">The config.</param> /// <remarks>This method is called only once to initialize service-wide policies.</remarks> public static void InitializeService(IDataServiceConfiguration config) { // other config settings go here... config.UseVerboseErrors = false; }
Unfortunately out of the box, ADO.NET Data Services does not provide a standard configuration section to manage these settings at runtime. Therefore, I have attached (at the very bottom of this post) a custom configuration section that can be used within the InitializeService method to drive these settings through configuration.
The configuration section will require an entry in your host’s web.config file like the sample below:
<?xml version="1.0"?> <configuration> <configSections> <sectionGroup name="emc.common"> <section name="dataService" type="EMC.Data.Services.Configuration.DataServiceSection, EMC.Data.Services.Configuration"/> </sectionGroup> </configSections> <emc.common> <dataService> <serviceSettings useVerboseErrors="false" maxExpandDepth="3" maxExpandCount="10" maxResultsPerCollection="1000" /> <entitySetAccessRules> <add name="*" entitySetRights="All" /> </entitySetAccessRules> <serviceOperationAccessRules> <add name="*" serviceOperationRights="All" /> </serviceOperationAccessRules> </dataService> </emc.common> </configuration>
The code below illustrates how to wire up the configuration section to the InitializeService method:
using EMC.Data.Services.Configuration;
public class MyDataService: DataService<MyEntities> {
/// <summary> /// Initializes the service. /// </summary> /// <param name="config">The config.</param> /// <remarks>This method is called only once to initialize service-wide policies.</remarks> public static void InitializeService(IDataServiceConfiguration config) { DataServiceSection dataServiceSection = ConfigurationManager.GetSection("emc.common/dataService") as DataServiceSection;
if (dataServiceSection != null) { if (dataServiceSection.ServiceSettings != null) { ServiceSettingsElement serviceSettings = dataServiceSection.ServiceSettings;
config.MaxExpandDepth = serviceSettings.MaxExpandDepth; config.UseVerboseErrors = serviceSettings.UseVerboseErrors; config.MaxExpandCount = serviceSettings.MaxExpandCount; config.MaxResultsPerCollection = serviceSettings.MaxResultsPerCollection; }
if (dataServiceSection.EntitySetAccessRules != null) { foreach (EntitySetAccessRulesElement element in dataServiceSection.EntitySetAccessRules) { config.SetEntitySetAccessRule(element.Name, element.EntitySetRights); } }
if (dataServiceSection.ServiceOperationAccessRules != null) { foreach (ServiceOperationAccessRulesElement element in dataServiceSection.ServiceOperationAccessRules) { config.SetServiceOperationAccessRule(element.Name, element.ServiceOperationRights); } } }
}
Diagnosing WCF faults
If an handled exception is encountered by the WCF channel stack, it will use the standard fault mechanism to respond to the client. By default, the details of the exception will be hidden from the client (again this is how your service should be configured during normal running). This is how it looks in your browser by default when you have a WCF fault in your data service:
In order to see the details of the exception in the response from the service, you need to attach a ServiceBehavior to the data service. This can be done in code by applying a service behaviour attribute to the data service implementation, but again this is hard coded and not what you would want for general good running of your service.
Unlike ADO.NET Data Services, WCF does provide a standard way of configuring this behaviour at runtime through the web config file. The configuration code below illustrates how you can drive this behaviour through configuration:
<system.serviceModel> <services> <service name="Schedule" behaviorConfiguration="DataServiceBehavior"> <endpoint binding="webHttpBinding" contract="System.Data.Services.IRequestHandler"/> </service> </services> <behaviors> <serviceBehaviors> <behavior name="DataServiceBehavior"> <serviceDebug includeExceptionDetailInFaults="true"/> </behavior> </serviceBehaviors> </behaviors> <serviceHostingEnvironment aspNetCompatibilityEnabled="true" /> </system.serviceModel>
ADO.NET Data Services implements a service contract called IRequestHandler. The name of the service needs to be the fully qualified name of your data service implementation.
Using standard WCF messaging diagnostics
ADO.NET Data Services also benefit from the excellent support that WCF has for standard trace diagnostics. The following configuration entry illustrates how you can log out WCF’s tracing to a flat file. Again this would only be used when you have problems with your service at runtime:
<system.diagnostics> <sources> <source name="System.ServiceModel.MessageLogging" switchValue="Warning, ActivityTracing" > <listeners> <add name="ServiceModelTraceListener"/> </listeners> </source>
<source name="System.ServiceModel" switchValue="Verbose,ActivityTracing" > <listeners> <add name="ServiceModelTraceListener"/> </listeners> </source> <source name="System.Runtime.Serialization" switchValue="Verbose,ActivityTracing"> <listeners> <add name="ServiceModelTraceListener"/> </listeners> </source> </sources> <sharedListeners> <add initializeData="App_tracelog.svclog" type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" name="ServiceModelTraceListener" traceOutputOptions="Timestamp"/> </sharedListeners> </system.diagnostics>
|
-
Introduction
The data access wars are over; long live the ORM wars. The new battles are fought with even more fanatical zealous behaviour than in previous conflicts. Rather than just debating the pros and cons of how best to execute SQL and wire it into our objects, there is now a whole new dimension to developer’s polarized opinions; what is the best ORM to use, if you believe you should use one at all that is.
The objective of any ORM is to bridge the object-relational impedance mismatch that occurs between a normalized database model (optimized for data storage, consistency and performance), and an object model (optimized for writing maintainable domain logic). Without an ORM, a developer needs to bridge this mismatch by writing tedious code that is hard to test and maintain. Thus ORMs should improve productivity, and the trade off is one of loss of control over the exact SQL executed against your database, and the performance overhead of running the ORMs code.
What makes the ORM wars so intensely fought is that there is little or no objective comparisons of the available options, and many people do not have equal experience in all the options either, meaning they find it hard to come to an objective view. This post attempts to provide an objective guide to choosing a technology to access data and return the data in the desired entity model of the developers choice.
This post compares the most common set of options facing a .NET developer at the present time, although other ORMs are available. The options considered here are:
- Writing traditional ADO.NET data access code and manually mapping the data to your object model.
- Using LINQ to SQL to surface data and LINQ to Objects to map this data to your object model.
- Using NHibernate as an ORM to access data and map this information to your object model.
- Using ADO.NET Entity Framework as an ORM to access data and map this information to your object model.
Assessment Criteria
In order to provide an objective assessment of each option, we need to have specific criteria which to measure. The criteria used in this post are as follows:
- Productivity and Maintainability
- Control over SQL
- Operational Support and Maturity
- Mapping Capabilities
- Database Vendor Support
- Modelling Approach
- Coherence with the rest of the .NET Framework
- Testability
- Futures
Each technology choice is measured out of ten for each area of assessment. No overall “score” is given to each technology option, because that is missing the point of this work; really it depends on the design goals of your project as to what the best fit is.
Productivity and Maintainability
The business objective of an ORM is to provide a more productive method of populating an object model with data than traditional data access code, which is prone to human error (due to repetition). This section looks at how productive the development phase is for each option, looking at tooling, engineering practices and maintenance after release.
ADO.NET Data Access Code
A typical traditional data access layer in a .NET application will use ADO.NET Connection, Command, Parameter and DataReader objects to execute stored procedures (containing SQL) against a database connection. Parameters are passed into a stored procedure via a Command object, and after the procedure is executed, data is read by using a DataReader, or reading the output Parameter objects of the command. Normally, a developer will map the contents of a DataReader object to their own Entity object in code.
From a productivity perspective, the creation of Command and Parameter objects to execute against a stored procedure is prone to human error during development, because of the monotony of the task, and the lack of any compile time safety beyond the syntax of the ADO.NET code. The work of mapping a DataReader to an Entity is also tedious, although the advent of C# 3.0 and LINQ to Objects has made this task less onerous.
From a maintainability perspective, such a data access pattern does have one benefit over ORM approaches; SQL encapsulated in stored procedures can be more easily patched in a production system without a redeployment of the application.
Score: 2 /10
LINQ to SQL
LINQ to SQL provides code generation of data access objects using a command line tool called sqlmetal, which is integrated into Visual Studio 2008. It auto generates objects which represent to table structure of the database and are accessed using an auto generated DataContext. This provides a very quick start up time of developers, and the visual tooling is Visual Studio is very good.
A common misconception with the data objects that LINQ to SQL generates is that they replace the entities in your traditional architecture. They do not, because LINQ to SQL is not a fully fledged ORM. You will not be able to model the generated data objects as you would like using the designer. The best approach to using LINQ to SQL is to create your entities yourself and map the data objects to your custom entities using LINQ to Objects. This means that there is still be an amount of tedious mapping code to maintain, but the hand crafted SQL stored procedures have gone completely and you do have compile time safety over your data objects and mapping code.
The biggest issue that LINQ to SQL has both for development and maintenance is what happens when you make changes to the data model during development. Whilst none of the options here have a magic answer to this problem, but really the only answer LINQ to SQL has is to regenerate the entire data model. This becomes very onerous if you have customized the auto generated data objects in some say (like changed the names of properties or data objects using the designer). You can circumvent this weakness by not changing what is generated, but that does beg the question of the point of the designer in the first place.
Score: 6 / 10
NHibernate
NHibernate enables you to map your own entities to the database model using XML mapping files. Normally one mapping file is created per entitiy. Each mapping file contains meta data about the database model the entity maps to and how this data is mapped to the entity. Execution of these maps to populate objects with data from a database is handled using a Session object, which manages connections to database and generation of SQL against the database.
Like LINQ to SQL, the fact that NHibernate generates the SQL to execute against the database removes one productivity issue. However, each mapping file must be maintained by hand, and tooling support is thin on the ground, not really getting any better than XML intellisense. This additional control does not suffer from LINQ to SQL’s code regeneration issues (because nothing is generated), but is does beg the question “haven’t I just solved one maintenance problem but created a new one?”. Additionally, querying a repository using the session is achieved using a loosely typed syntax (string based), which is prone to error unlike a LINQ implementation like LINQ to SQL or LINQ to Entities (entity framework). LINQ support is however under development by the open source community.
Post production, any changes to the underlying database schema will require the deployment of the NHibernate mapping file (usually embedded within the data access layer assembly).
Score: 4 / 10
ADO.NET Entity Framework
The ADO.NET Entity Framework is an ORM that maps the structural model of a database schema to a conceptual (object) model. The framework uses the Entity Data Model (EDM) to achieve this. The EDM is broken down into three sections: the structural model (SSDL), the conceptual model (CSDL) and the mapping model (MSL), which are stored as XML normally embedded within an assembly. The entity framework is similar to NHibernate in that ORM mappings are stored as XML, but differs in the separation the EDM forces between the three models.
Like LINQ to SQL, the ADO.NET Entity Framework provides code generation for its EDM via a command line tool called EdmGen, which has integrated designer support in Visual Studio 2008 SP1. Because this design time support is based on changing the conceptual model of the EDM, the entity framework does not suffer from the same shortcomings as LINQ to SQL, in that it is a fully fledged ORM that will can (on the whole) produce the desired object model from the designer.
Better still is the tooling for the entity framework. This is where it plays an ace card; compile time support for the EDM comes for free, and what a free lunch it is. The entity framework refuses to build if there is an issue with the EDM that will cause it to fail under any condition. By this I mean any issue. So, the EDM will fail to build if there is an issue with your database schema, or if your conceptual model will not work under all CRUD conditions. Surely, this is the real problem an ORM was supposed to fix, and of these options, the entity framework is the only option that really helps you.
However, the ADO.NET Entity Framework still suffers from difficulties when in comes to changing the database model post production release. Depending on the nature of these changes will depend on how much of the EDM needs to be reworked; if the change is an addition to the structural model, it is possible to use EdmGen to regenerate the SSDL while keeping the conceptual model (you will need to do some extra mapping).
Score: 9 / 10
Control over SQL
If database performance matters (I mean it really matters to your scenario), then you may need to strictly control the SQL that is executed against your database. For these scenarios, ADO.NET Data Code cannot be beaten, because you can do anything you like. Most of the time however, all three of the other options will suffice. All the other options also provide support for stored procedures should it be necessary to execute some SQL that the technology cannot generate.
ADO.NET Data Access Code Score: 10 / 10
All The Rest Score: 6 / 10
Operational Support and Maturity
Its all too easy for developers to focus on a solution from a purely development perspective, but the reality is a truly useful application will remain in production far longer than its development phase. In many organizations the development team are not responsible for keeping an application running, largely because this is not cost effective. Hence, a support team is normally responsible for keeping the application running, and their skill set differs from that of the development team, in that they have less application specific knowledge, but more platform specific knowledge.
Operational support teams are largely concerned with procedures to get a system back online should an application fail. When these procedures do not suffice, the team will escalate the issue to the development team and ultimately the vendor’s support team.
For many businesses, this is a critical reason why they have chosen Microsoft technologies over other vendors, or open source offerings; operational support like the fact that they can fall back on Microsoft support should they need to. Having said that, NHibernate has a very active open source community, and if all else fails, you have the source code which you can fix.
Another important angle to operational support is maturity; the wisdom goes that the more mature a technology is, the less likely it is to have bugs. Of the technologies compared here, the most mature is ADO.NET Data Access code, then NHibernate, then LINQ to SQL, and finally ADO.NET Entity Framework.
ADO.NET Data Access Code Score: 10 / 10
LINQ to SQL Score: 8 / 10
NHibernate Score : 6 /10
ADO.NET Entity Framework Score : 6 / 10
Mapping Capabilities
The mapping capabilities of an ORM can prove a crucial deciding factor when choosing an ORM to use for data access. After all, the whole point of an ORM is to bridge the object relational impedance mismatch between your desired object model and the best relational database model.
Of the options considered here, only two can be considered as ORMs in the true sense (NHibernate and ADO.NET Entity Framework). Hence only these two can be measured in this area.
Firstly, both ORMs deal with associations between objects perfectly. In other words, both options model one to one, one to many and many to many associations without issue.
NHibernate provides more options for modelling inheritance than ADO.NET Entity Framework. The entity framework provides support of table based inheritance. This means that each sub classed object maps to a separate table, each row representing the sub-classed object. NHibernate also supports table based inheritance, but it additionally provides other options for modelling inheritance.
NHibernate Score : 10 / 10
ADO.NET Entity Framework Score : 8 / 10
Database Vendor Support
Many businesses have a chosen database vendor, and thus choosing a data access option will require that you can easily utilize the chosen database vendor. Common databases include SQL Server, Oracle, DB2 and MySQL.
ADO.NET Data Access Code
Out of the box ADO.NET has support for both SQL Server and Oracle, and other third party providers can be purchased for support for most common database vendors. This is the most supported method of accessing data from .NET code, an in some more exotic database it may be the only viable option.
Score : 10 /10
LINQ to SQL
As the name suggests, LINQ to SQL is for use only with SQL Server. If this is your database, LINQ to SQL is not a problem. If you are using anything else, LINQ to SQL is not an option.
Score : 1 /10
NHibernate
NHibernate has support for SQL Server and Oracle out of the box, and it is possible to roll your own support of other database vendors.
Score : 8 /10
ADO.NET Entity Framework
Out of the box the ADO.NET entity framework only has support for SQL Server, but unlike LINQ to SQL, other database vendors are not ruled out, as the architecture supports using a custom data access provider. Custom providers can either be built or purchased, and third party support is growing by the day for support of databases such as Oracle and MySQL. For example, DevArt have providers that support multiple third party databases here. There are also samples available from Microsoft on rolling your own here.
Score : 6 /10
Modelling Approach
There are two initial approaches to designing a solution with a database and an object model: either design the database model first, or the object model first (although clearly these two activities should never be executed entirely in isolation). There is no right or wrong order to these tasks, but each of the technology options provide varying support of both approaches.
ADO.NET Data Access Code
This option does not stop you from approaching a solution’s design from either standpoint, but then neither does it really provide any support for either option. Here, you can do what you like, including doing some very bad practices indeed. The only modelling you will have is the class designer and your database vendor tools.
Score : 3 /10
LINQ to SQL
LINQ to SQL only really models the data layer, which means you are free to do the object and database modelling either way round. But like the straight data access option it’s not really giving you much value here.
Score : 3 /10
NHibernate
NHibernate enables you to model your database and entities in either order, and for many in the object model first camp, this option feels like the most natural choice of the four here. But again, modelling support only comes in the form of not hindering you, rather than helping you
Score : 6 /10
ADO.NET Entity Framework
If there is one area that will rule the entity framework out for many developers, this is it. In reality, the entity framework currently only supports database model first development. If you can work this way, the modelling support is superb, but this does limit how the entity framework can be employed, particularly if you do not have control of an existing database’s model; this part of the entity framework is very unforgiving, yet also most giving.
Score : 3 /10
Coherence with the rest of the .NET Framework
One often overlooked point when considering a data access option is how coherent the technology is with the rest of the .NET Framework. By coherent I mean that the API feels like a natural extension to the rest of the framework. This is important, because it reduces the learning curve for developers new to the technology, and can also help future proof it with advances in the core framework. It was for all these reasons that I have always disliked many aspects of the Enterprise Library; because it felt unnatural to program against, and at its worst hid many core aspects of framework development.
In these respects, NHibernate is at a distinct disadvantage in that all the other options are the .NET framework. From a developers perspective its API shares no coherence with either the underlying ADO.NET paradigm, or the IQueryable (LINQ) paradigm. This makes learning NHibernate a uphill task for .NET developers skilled in either of these areas.
Of all the options, ADO.NET Entity Framework scores best, because it supports both the ADO.NET paradigm and the IQueryable paradigm, meaning that developers who have invested time in these areas can adopt the entity framework more easily. Another important area to consider here is the support ADO.NET Entity Framework has from other parts of the .NET Framework. Again the ADO.NET Entity Framework wins here because it has a lot of support from both the ADO.NET Data Services team and the ASP.NET Dynamic Data team. Once you have an entity model, you can create a data service or scaffold a website fairly easily, giving you significant quick wins (LINQ to SQL does not have this level of support).
ADO.NET Data Access Code Score : 5 / 10
LINQ to SQL Score : 6 / 10
NHibernate Score : 1 / 10
ADO.NET Entity Framework Score : 9 / 10
Testability
Making software easily testable by creating unit tests is an important engineering practice in ensuring quality in your solution. More specifically, the ability to test the interactions between the layers of your architecture is as important to testing for results of executing code in a specific layer of your application.
ADO.NET Data Access Code and NHibernate
Both of these options enable you to create unit tests and mock layers of the architecture, because you are complete control over all the code you test. This is because both options support the use of POCO classes for the creation of business objects and entities. Additionally, both options provide interfaces for you to mock out enabling you to test interactions.
Score : 10 /10
LINQ to SQL and ADO.NET Entity Framework
Both of these options generate out classes for you to work with that represent your database (for LINQ to SQL) or your entities (Entity Framework). These objects are not POCO objects, which forces a dependency of framework assemblies in all parts of your application that consume these objects. For ADO.NET Entity Framework, this problem is more serious, because entities proliferate around your architecture more than data layer objects. Additionally, these generated objects do not expose separate interfaces, meaning that you cannot use mocking practices against these objects to test interactions easily. The counter argument here is that you do not need to test these interactions, because the code is generated anyway.
Score : 1 /10
Futures
No one wants to invest money or time in a technology that has no future, or will be quickly superseded. It is a key factor for development teams who consider that their application will evolve through several releases over a period of time.
ADO.NET Data Access Code
You can be sure that your investment here will not be superseded, but neither will it be greatly improved upon in future. Its mature, but it is also feature light.
Score : 3 /10
LINQ to SQL
Microsoft are not developing LINQ to SQL features, because it is now considered to be superseded by the entity framework. Of all the options here, this one has the most bleak future (although Microsoft will obviously continue to support the technology).
Score : 1 /10
NHibernate
NHibernate continues to have a active ALT.NET community, and features continue to evolve. LINQ support is due soon, which will level the playing field with the Entity Framework in terms of support for ADO.NET Data Services and ASP.NET Dynamic Data. The only doubt here is how a maturing entity framework will affect NHibernate support in the future.
Score : 7 /10
ADO.NET Entity Framework
Many of the entity framework’s short comings are promised to be fixed in the .NET 4 timeframe. In particular, it will (apparently) support POCO objects, object model first design (including database generation) and better testability. What is for certain is that Microsoft are backing this horse, which should not be overlooked.
Score : 10 /10
Summary
Wow… that was a long blog post! But in short:
You might choose to use ADO.NET Data Access Code when:
- You really really need to worry about performance
- You’ve got a database not supported by any other means
- You have not need for an entity model in your application
You might choose to use LINQ to SQL when:
- You want to create a prototype quickly
- You haven’t got .NET 3.5 SP1 installed so you can’t use the Entity Framework
- You only ever want to work with SQL Server
You might choose to NHibernate when:
- You like modelling an object model before the database model
- You don’t have control over the database model
- Operational support have no issues with using a third party framework
- You want good testability
- The project is sufficiently complex that you would benefit from the investment creating maps manually
- You are not concerned with the lack of ADO.NET Data Services or ASP.NET Dynamic Data Support
You might choose to use the ADO.NET Entity Framework when:
- You have control over the database model and are happy modelling this first
- You want compile time safety over your entire ORM
- You want productivity without compromising your architecture (very much!)
- You want ADO.NET Data Services or ASP.NET Dynamic Data support
- You trust the generated non-POCO entities
- You want the most future proof option
|
-
One criticism I have heard placed at the door of ADO.NET Data Services is that it does not provide control over the syntax and design of the URI addresses that represent the resources you are surfacing through a data service. While it is true that there are limits to the control developers have over the formatting of URIs, most of these limitations are templating semantics rather than limiting how you structure your RESTful resources.
Many people who make this criticism want to use ADO.NET Data Services without the ADO.NET Entity Framework. This means you are losing the benefits of the Entity Data Model (EDM) that ADO.NET Data Services can consume as an implementation of IQueryable and IUpdateable. More specifically, you are losing the benefits of the conceptual model (CSDL) that is part of the EDM. While it is entirely possible to use data services with any given implementation of these interfaces (note that LINQ to SQL only implements IQueryable out of the box), you are resigning yourself to the limitations of these technologies. In other words, ADO.NET Data Services will only surface the classes that your LINQ implementation surfaces (in the case of LINQ to SQL this means classes that represent tables).
This blog post covers how to use the EDM effectively to present natural and readable entity URI addresses using ADO.NET Data Services. What is written here is only guidance, which means what I am suggesting is not fact, but merely an opinion based on my experiences of using ADO.NET Data Services and the Entity Framework through the course of writing my book, and my current project which uses both technologies extensively. Previous knowledge of ADO.NET Data Services and the Entity Framework is assumed. In particular how to address entities using ADO.NET Data Services (see chapter 3 of my book for more information if you are unclear about this).
Guideline 1 - Entity and Entity Set naming conventions
Single entities should be named as you would name any other single domain object: using a singular noun in PascalCase to best represent the object in question. Entity sets are most naturally addresses using a plural version of the entity name.
For example, the entity named Country would have an entity set named Countries, or the entity Customer would have an entity set named Customers.
Guideline 2 - Avoid using the entity's name in a scalar property name
As the entity is an object, there is no need to repeat the entities name in the scalar property name (as you might do for a table field to avoid ambiguous names across joined tables). Scalar properties should also be written as nouns in singular PascalCase like any other .NET object.
For example, an entity named Country may map to a table named Country with a field called CountryCode. In the Country entity, the property would be called Code. Therefore the object's property could be referred to by Country.Code.
Guideline 3 - Use natural keys as primary keys to entities
If there is an obvious candidate for a natural key, you are best using it as the entity's primary key. This is particularly true if the data is reference data that you are not truly the master of. For example, your service may need to expose countries that can be identified by a natural key of an ISO 3166 country code (such as GB, US and DE). The EDM has no concept of unique keys outside of the primary key. At first this seems like a limitation, but it is caused primarily because of the complexity of Create and Update operations if other key were used to address an entity; you would somehow need to map the natural key to the primary key for an update. If you are not the master of this data, your generated primary key is of no importance to a consumer anyway, so this limitation seems reasonable.
For example, if we had an entity called Country belonging to an entity set called Countries, a the country Great Britain could be addressed as:
http://myhost/myservice.svc/Countries('GB')
If the natural key is a composite key, the address reflects this. For example:
http://myhost/myservice.svc/TelephoneNumbers(CustomerId=123,Category='Work')
Identifies the work telephone number of the customer identified by id 123.
Guideline 4 - Navigation properties should be written as singular or plural nouns to reflect the multiplicity of the association
The EDM enforces both ends of an association between two entities. This means that each entity taking part in the association will contain a navigation property to the other entity. Associations can be either one-to-one, one-to-many or many-to-many. In each case, each end of the association has an inherent multiplicity, which should be reflected in the property's naming as either a singular or plural noun. Note that a plural noun will return an entity set.
For example, an entity named Country could have a one-to-many association with an entity named City. Therefore, the Country entity would contain a navigation property named Cities (indicating the many multiplicity) and the City entity would contain a navigation property named Country (indicating the one multiplicity).
So, the country (Great Britain) of the city of London might be addressed as:
http://myhost/myservice.svc/Cities('LON')/Country
And the cities found in the country of Great Britain might be addressed as:
http://myhost/myservice.svc/Countries('GB')/Cities
Guideline 5 - Use of Inheritance is limited by ADO.NET Data Services addressing
The EDM enables inheritance of entities using "table" inheritance. In other words, the EDM expects each entity sub class to map to a table. One restriction that ADO.NET Data Services places on to this model, is that each entity subclass cannot extend the properties of the base class (the service will throw an exception under these circumstances). The reason for this is because it is not possible to address each subclass using the same navigation properties if each subclass does not contain exactly the same properties as the base class. If you have entities that appear to be subclasses, but with additional properties, you should consider composition to duplicating properties between unique entities.
Summary
These guidelines have been formulated from experience of developing natural URI addresses to resources surfaced through ADO.NET Data Services and the Entity Framework. As a technology, ADO.NET Data Services is a good RESTful citizen, providing links to resources in any response to a URI, and following these guidelines promotes natural addressing that can be understood by anyone used to using the Internet.
|
-
This is a blog I've been meaning to write for over a year now, but have just not had the time. Its as much a "note to self" as a blog for external consumption.
WCF provides the ability to generate a client proxy that communicates with a service, such as a SOAP service conforming to basic profile. Generating a proxy can be done by either using the SvcUtil.exe command from the Visual Studio command prompt, or by using the "Add Service Reference" feature of Visual Studio 2008 and pointing Visual Studio at the WSDL of your service. This action will generate a client proxy class and the required WCF client configuration file. You can write code against this client proxy class and armed with the client configuration, WCF will handle the communication to the service endpoint and the (de)serialization of messages into CLR types.
When a client proxy class is instantiated, WCF will build a channel stack based on the binding settings contained in the client configuration. This binding includes the address of the service, the transport to use, the encoding of the message and any service policies that apply to the service endpoint. The channel stack is the runtime representation of the way in which the WCF client will communicate with the service. For example, an average SOAP based service will communicate using an HTTP transport using a text encoding, and the service may apply additional WS-* policies such as WS-Security to implement service authentication.
Much of what I have just written is common knowledge, but much less written about the performance implications of how best to use a generated WCF client proxy. There are a few really important points to note when using these generated classes, and how this affects the performance of your application:
Consider caching the proxy object
When you instantiate a proxy class, WCF examines the binding configuration and uses reflection to generate the channel stack at runtime. This channel is fairly intensive to create, and thus creating the client proxy class every time you make a call to a service operation is excessively expensive. Thus, you should consider caching the proxy object, or hold the proxy object in a static variable. For a web site the most logical place to cache the proxy is using System.Web.Caching. You need to be aware that you can only reuse the same proxy when the channel stack is exactly the same, and this means running in the same security context (for example you might need to cache by username if you are using Windows impersonation).
Consider calling request-reply service operations asynchronously
For a request-reply calls to a service you should consider calling the asynchronous BeginMyOperation() method rather than the synchronous MyOperation(). This is because when a synchronous call to a service operation is made, the thread is blocked until the reply is received. This causes the CPU to work much harder than if you use asynchronous calls and deal with the response in a callback.
Always call the Close() method on the proxy object
The WCF client proxy contains a method named Close() which you should call after you have completed communications with the service endpoint. This method closes communications over the channel stack and disposes of any unmanaged resources. Failure to call this method will seriously degrade the performance of your client application, as communications are not properly closed and unmanaged resources may consume excess memory.
Do not instantiate the client in a Using statement, or call the Dispose() method
Calling the dispose method can lead to some communication exceptions being hidden (because the unmanaged resources are disposed too quickly). It is also unnecessary to call Dispose() when you call the Close() method, because this method handles disposal of resources for you.
|
-
PDC went by in a flash. Of all the conferences I've ever had the privilege to attend, it was by far the most intense, in terms of hours, subject matter and variety of new software to consider. For all of these reasons, I chose not to blog whilst I was there. I wanted to chew on what I had seen before blogging about any of the technologies covered.
With the sheer weight of new technologies being unveiled, I decided to pick my subject matter carefully. Therefore, whilst I'm interested in Windows 7 as a consumer, it matters less to me as a .NET developer. That said, it looks like we might be getting the first decent version of Windows in a while judging by what we've seen in terms of performance improvement. I wanted to attend some PLINQ content, but it just clashed with more important content.
Apart from that, I got around all the content that was important to me. I went to several sessions on .NET Services, WCF / WF 4.0, C# 4, Dublin and Oslo, so here's a quick review of the best of what I saw....
WF / WCF 4.0
I was genuinely surprised by how good WF 4.0 looks. In fact, I'd say it was the best thing I saw at PDC in many ways. This was surprising to me, because I never liked WF 3 or 3.5, because it did not feel finished, and had some fundamental flaws like:
- Poor performance
- Lack of persistence control
- Lack of transaction control
- No correlation support
- Poor threading model for parallel tasks
- Weird XOML based markup
- Not really tied in nicely with WCF (even in 3.5)
- Host problems
So it was great to hear Microsoft say that they agreed! Reading between the lines it is clear that WF4 is a complete rewrite. Microsoft already claim that it is between 10 and 100 times faster than WF 3.5. Persistance control (complete with message box and tracking databases), transactions, correlation and threading have all been addressed and the object model (now under the System.WorkflowModel) now feels very natural to anyone who has used WCF to a point where they feel like the same framework. And for anyone who's used to WPF and Silverlight, you can now model workflows in XAML (including building your own designers - nice). All of this coupled with what was shown (e.g. not much) of Dublin (the new Windows Application Server) make me conclude that hosting issues have also now been addressed.
All this is very encouraging and would also seen to point to the end of BizTalk Server as a product in the long run.
It was great to see how WCF 4 seems more like an evolution than revolution, underscoring just how "right" this technology is. The main developments here are around more out of the box bindings, including support for SOAP over UDP. I think its fair to expect richer REST support in WCF 4 as again XAML based tooling support for all the WPF people out there. Like WF 4, this all just feels like the framework is a more coherent and unified universe.
Dublin
Not much real software was shown, but from what I could work out, Dublin seems best described as an on premise application server, providing the host scaffolding for WF and WCF services. Its built on top of IIS and WAS, but comes complete with persistence store and tracking database for WF 4 workflows, as well as deep SCOM support and tracing features. Finally, it appears .NET has an application server!
.NET Services
All part of the Windows Azure vision, .NET Services comprises of the .NET Service Bus and Workflow Services. The best way to look at these are WCF and WF in the cloud. In fact... this is one of the best things about .NET Services; if you know how to write a WCF service or WF workflow you already are 90% of the way towards understand how to build services for Microsoft's cloud vision. More importantly, this enables on premise services to be rapidly ported to cloud services without needing a rewrite, preserving and enterprises investment.
The most impressive feature of the Service Bus is the way the whole Relay mechanism works; the bus works out the appropriate message topology based on the subscribers network configuration. So, if a direct connection can be established, the relay will establish a direct connection between publisher and subscriber. Additionally, you can configure the bus to decide to most appropriate communications protocol to use. So, if the subscriber can communicate using Binary encoding over RDP, the relay with use this instead of HTTP. This is all great stuff that most developers will not consider when building middleware (normally just reverting to HTTP because its easiest to configure).
Workflow services are just WF... although these are currently WF 3.5, which is a bit disappointing given all I have seen of WF 4. I'm sure this will change in the near future though.
|
-
It's been a long time since I last wrote a blog post; around six months to be exact. It's not that I have not been busy writing though. In April I began embarking on one of the hardest things I've ever done; writing a book with my co-author John Shaw.
And now for a brief advertisement; the book is called "Pro ADO.NET Data Services : Working with RESTful Data", and is going to be published by Apress in December. Feel free to order itJ. In fact, here's a pretty picture link…

Colleagues of mine said to me that I should blog my experiences of writing it. Howard suggested to me that I seek comments from the community about what content to include much like Ayende Rahien has done for his up and coming book. I have resisted, not only because I am exhausted from writing, but also because I think I've had a deep sense of insecurity about the whole business.
I went into the process thinking it would be like writing a big blog; a marathon blog. But in reality, it's a much harder process than that. Every line of text gets scrutinized… and rightly so, for there is no room for mistakes in a book. But the real insecurity comes from realizing you're not a seasoned writer; you're an IT professional writing a book in your spare time. Starting a chapter from a blank page is a really daunting prospect; where do you start, and how do you structure the text?
I have found it very hard deciding what content to include and what level of detail to go into. John and I decided at the beginning of the process that we wanted to write a "real world" book, where we painted ADO.NET Data Services in the context of the enterprise, because we were sick of reading about how to apply the latest and greatest technology with no mention of using it with surrounding technologies. The result is that the book covers a really diverse set of technologies you may find in your enterprise, from WCF to BizTalk and from ASP.NET AJAX to Silverlight, we have stitched together a picture of how many development landscapes look and how these technologies apply to ADO.NET Data Services. This broad array of technologies made the writing process even harder; just how do you cover ASP.NET and AJAX solutions in one chapter and make the text useful and relevant without writing a whole book about the subject?
Like software development, it is difficult to accept when you are done writing a book, because you can always add a little bit more. This problem is exacerbated when you are writing about something not yet released, as was the case when we began the project. I had to keep asking myself whether the delivery was fit for purpose, and I'm pleased to say that I'm really happy with what we've covered in the book.
But I could always write more about ADO.NET Data Services, in the next two weeks I am going to write a couple of blog entries covering additional material on more exotic subjects, such as using ADO.NET Data Services with Oracle.
It just so happens that the completion of my chapters in the book also coincides with the start of PDC 2008 in Los Angeles, where I am currently writing this post from. I think PDC this year marks the start of a new chapter in Microsoft technologies. It's certainly the most important event since WCF and WPF were announced several years ago and I now have the time to start thinking in earnest about the impact of .NET 4.0, Oslo and Dublin. After the concluding chapter of my book covers the future of services and cloud computing, it feels like the perfect place to be right now.
|
-
Today I spent several hours of my life trying to figure out what had broken the design view and CSS features of web pages in my installation of Visual Studio 2008. After a process of elimination, starting with uninstalling the Silverlight 2.0 SDK bits off my machine, I found that the culprit for rendering the excellent design features of Studio useless was the XML Spy 2008 components for Visual Studio. On the plus side, uninstalling the offending XML Spy pieces from Visual Studio fixed the issue without me having to completely reinstall Visual Studio, but nevertheless this was a frustrating few hours that I hope no one else has to endure.
|
-
I never really got that excited about Silverlight 1.0, mainly because whilst it had a great core graphics engine and did video streaming very well, it's feature set was just not rich enough to make it really useful to applications that demanded deep functionality.
One of the features that was clearly lacking was connectivity; you could not directly consume services; the main workaround was to use the connectivity features of ASP.net AJAX to consume services from Javascript. Thankfully, this problem has been rectified in Silverlight 2.0. Alongside other new features in Silverlight 2.0, I am now much more interested in what I can do with the technology.
When calling any service from within Silverlight 2.0, you have to call the service asynchronously (like AJAX). The reason this is important is because of the nature of the browser itself, which ultimately is the host you are running in.
There are two sets of services you need to consider consuming from Silverlight: services that are self describing (such as SOAP / WSDL and RSS / ATOM) and services that are not (such as REST and POX). Silverlight 2.0 can consume all of these types of services, but it does so in distinctly different ways.
The easiest services to consume in Silverlight 2.0 are SOAP based services (something sure to infuriate all the RESTafarians out there!). They are consumed in Silverlight using a WCF service proxy configured to the BasicHttpBinding (BasicProfile 1.1). They are easiest to consume, because it is possible to auto generate a service proxy from the WSDL. Unlike AJAX, XML is also the preferred encoding over JSON, because the Silverlight runtime has good XML performance in managed code unlike Javascript. When returning XML, Silverlight gives you two main approaches to parsing the asynchronous result (returned via an event handler): either using LinqToXML or using the XmlSerializer to deserialize the result into a predefined type. SOAP faults, however are not supported, because of security restrictions on the browser, which could cause some issues to existing services that use the fault mechanism to return exceptions.
Other services (such as REST) are a little harder to consume, and the one thing that did surprise me was that there is no support for the auto proxy generation used by ASP.net AJAX against the WebHttpBinding. You have to construct a Uri string manually and call the service either by using the WebClient class in the case of HTTP GET resuests (REST), or by using the HttpWebRequest class for other HTTP verbs. If the service uses JSON encoding, parsing the response can be achieved in one of two ways: either through WCF's DataContractJSONSerializer (similar in concept to the XmlSerializer), or by using LinqToJSON, which you can find here:
http://www.codeplex.com/Json
Whilst XML seems to be the first citizen of Silverlight, it is important that JSON encoding is supported easily, as many services you write may need to be consumed from both an AJAX enabled page and a Silverlight control. In these circumstances, you would definitely opt to use JSON, because the performance of XML in Javascript is poor, and the payload size is also bulkier.
RSS and ATOM feeds are consumed using a mixture of the two methods above; you need to manually construct a Uri and pass it into a WebClient which calls the feed, but Silverlight does provide you with a SyndicationFeed class to parse the response.
One final point to make is around security. As with AJAX, there is a difference between private services that are written and consumed only by your application and public services that are consumed by multiple applications across the internet.
For privates services, Silverlight uses the ASP.net forms authentication mechanism (based around an authenticated cookie) to secure your services from other clients. This means that there is no additional code (on top of your ASP.net code plus config) to write in order to secure your own services.
For public services, the same cross domain security issues apply as they do in the AJAX world. Silverlight supports its own ClientAccessPolicy.xml format as well as Flash's CrossDomain.xml file format to resolve this problem. So long as the service you call has one of these two files in its root, Silverlight will be able to call the services cross domain.
|
-
There are several blog posts out there that cover the subject of how to consume JSON enabled services from Javascript using ASP.net AJAX. Having scouted around the usual suspects, I think the best article is by Fritz Onion here:
http://www.pluralsight.com/blogs/fritz/archive/2008/01/31/50121.aspx
That said, I haven't found it all plain sailing, so I thought I'd write a blog entry covering the issues I have had to overcome in order to get this functionality working.
Service contract namespace issues
You will see from the article above that setting the namespace in the service contract attribute affects the Javascript namespace ASP.net's auto generated Javascript proxy uses to call the service. So, for example, when you use the namespace http://schemas.conchango.com/myservice/, you would instantiate a client proxy in Javascript using the following code:
var proxy = new schemas.conchango.com.myservice.IMyServiceContract;
where IMyServiceContract is the name of the service contract you are instantiating. Notice that the forward slashed are turned into dot notation by the Javascript proxy generation (and the final forward slash does matter!).
Now this all makes a lot of sense so far, until I introduced schema versioning into my namespace:
http://schemas.conchango.com/2008/03/myservice/
Versioning a schema in this way is standard practice for self describing SOAP services that expose a WSDL. It is important to ensure that you can version your service contract safely, ensuring that you don't break any consuming clients when you update your service to include new and changed functionality. It is also fundamental to publish-subscribe middleware, such as BizTalk understands who subscribes to the published message.
Here a hardnosed RESTafarian may well point out that my WCF endpoint exposed using a WebHttpBinding is indeed not a SOAP service, so this is not important. And they are right, so long as I never want to expose my WCF service through an additional SOAP based endpoint (using a binding such as BasicHttpBinding). This is one on the single most important principles of WCF and Service Orientation: to be able to expose a single service, hosted as a single instance through different endpoints so that you can communicate using different transports, encodings and security. In other words, services should be policy driven.
So I did work around the problem; when I need to version my schema I would use a new namespace such as:
http://schemas.conchango.com/myservice2/
I think this is acceptable, although not standard schema naming practice. At least I can now version my schemas properly and expose my service through SOAP and REST at the same time.
Debugging issues with IIS
In order to use ASP.net debugging with ASP.net websites and services hosted in IIS, Visual Studio requires Integrated Windows Authentication to be enabled. However, if you are developing a web site for anonymous access, you will need to enable anonymous access in IIS too. The problem here is that WCF only allows one authentication scheme to be used. If you attempt to debug a service in IIS under these conditions, you will see the following error message:
IIS specified authentication schemes 'IntegratedWindowsAuthentication, Anonymous', but the binding only supports specification of exactly one authentication scheme. Valid authentication schemes are Digest, Negotiate, NTLM, Basic, or Anonymous. Change the IIS settings so that only a single authentication scheme is used.
The simplest workaround to this problem is to switch from using IIS to Visual Studio's development web server. Otherwise I found that I was limited to running the site in IIS.
|
-
So I just got back from TechEd Barcelona, and I had a great conference in no small part due to the people I went with: Merrick Chaffer, James Dawson and Paul McMillan. This is a selection of some of Conchango's finest from the world of .Net development, infrastructure and SQL Server. One of the reasons the conference was so much more enjoyable in the company of my colleagues was that I got as much out of the conversations we had outside of the sessions as I did from what I learnt in the sessions and labs. Of all the subjects covered at this year's TechEd, none had more coverage than LINQ, and between us, none had more debate than the impact LINQ has on our worlds. This is a conversation I can see playing out across the Developer / DBA divide the world over, as the majority of solutions developed require some sort of data access to a database. David Chappell made a comment in his REST vs SOAP session (which was probably the best session I went to) that struck a real chord with me; he said "people are only passionate when something is in doubt" Conversations of this nature can tend to take a slightly religious standpoint, so I'll try to bear in mind David's comments. So with LINQ to SQL (and LINQ to Entities) the most hotly debated point was around the auto generation of LINQ's dynamic SQL vs. using stored procedures. In the old days, one point favoring of using stored procedures was the performance gain from a cached execution plan. But since SQL Server 2005, dynamic SQL is also cached, so this reason for using stored procedures has largely gone away. One benefit dynamic SQL has over stored procedures is that you only select what you need in all cases. In a typical scenario with stored procedures and a DAL populating an object model, stored procedures will tend to be reused even when all of data is not used by the calling DAL method. There ways around this, but it largely involves an ever increasing list of stored procedures (GetObjectByX), make the solution less manageable. What became clear in our debates on this topic is that really all depends on who you trust more: a developer writing a stored procedure or the LINQ to SQL engine? I began the week at TechEd a skeptic of LINQ to SQL, but the more I looked at what the engine produced, the more I began to trust it. Sure it won't do a better job than a highly trained SQL expert (like Paul), but is it good enough and better than your average developer? From what I have tried I would say the answer is yes. And the bottom line is, when you need that extra performance out of a finely tuned stored procedure, you can still use them.
|
-
Message ordering is a key concept in a service orientated architecture. It is a key concept, because one of the core tenets of SOA is that a service must remain autonomous. In remaining autonomous, a service must be able to deal with any unexpected circumstance of messages sent to it, including the possibility of messages being received out of order. There are two core strategies you can apply when designing your service: you can either force your client to send messages in the correct order, or you can enable messages to be processed out of order and implement mechanisms within your service to deal with the consequences of this. There are strengths and weaknesses to both approaches. In general, imposing ordering has a cost to the performance of the solution, because managing ordering requires you to manage the state of order. However, removing ordering requires the service to respond to the consequences of receiving the messages out of order. This has an additional development cost, which normally requires the development of compensating transactions. When discussing the strengths and weaknesses of imposing ordering, the decisions have to be considered for two distinctly different scenarios: homogeneous and heterogeneous ordering. Homogeneous message ordering Homogeneous message ordering is ordering of messages with the same schema. For example you may have a message to update a customer. In this example, it is important that ordering is considered, because messages being received out of order could result in a customer being updated in an incorrect state (because the final update to the customer has been overwritten by an earlier message which has been received out of order). Imposing ordering of such messages can be achieved in WCF using a binding that supports ordering, such WsHttpBinding (this uses the WS-ReliableMessaging policy). Likewise, BizTalk can support ordering through use of correlation. However in such ordering scenarios, be mindful of the fact that both the client and the endpoint must be order aware. So for example, if BizTalk (as the client) correlates messages being sent to a web service that is not order aware, no guarantee can be made on the order the messages are processed by the endpoint. Enabling a service to receive homogeneous messages out of order means that the endpoint must be able to cope with messages that have expired; the service must cope with the fact that the inbound message has already been superseded by a newer message. An expired message is known as a "zombie" message. The simplest way to ensure that the endpoint is able to evaluate the message as older than the previous message is to include a published date (date time) on the outbound message. If the endpoint persists this published date as stateful data, the service can then evaluate the previous published date for the message and ignore older messages. Heterogeneous message ordering Heterogeneous message ordering is ordering of messages of different schemas that have a relationship to each other. For example you may have a message for each customer and each customer may have many bank accounts, each of which constitutes a separate message which is related to a parent customer message. Imposing ordering of such messages means that the publishing system needs to maintain some understanding of related messages that have been previously sent. In BizTalk, this is achieved using correlation which requires messages to expose distinguished fields that will be used for correlation. Whilst imposing ordering is achievable its performance cost can be avoided if the endpoint is designed to be order agnostic. When an endpoint is order agnostic, it is able to receive heterogeneous messages in any order and tie the messages together within the endpoint. So for example, the endpoint would accept an account for a customer it has yet to receive the message for. To enable the endpoint to correlate these messages, the customer and account messages would need to contain enough information about the relationship between these two entities. The endpoint then needs to be designed to be able to accept these messages in either order and deal with the consequences of this. By enabling an endpoint to receive messages out of order, the service endpoint must also deal with the consequences of one these messages never arriving at the endpoint. So, for example, if a customer has two accounts and only one them is received by the endpoint, it may be important to deal with this account having gone missing. This scenario would require compensation. Compensation is some form of action that compensates the publishing system when a failure has occurred. Compensation can take many forms from the simple to the sophisticated, depending on the requirements and the business value. Indeed, in some scenarios, it may be deemed reasonable not to provide any form of compensation at all. A simple form of compensation might be to email an administrator to tell them in invoke a manual procedure. A sophisticated form of compensation might be to send a compensating message back to the publishing service which would then rollback the state of customer in that system.
|
-
Overview of WCF architecture Figure 1 illustrates the generic architecture WCF employs for all services. 
Figure 1 - General WCF Architecture The outer service represents the AppDomain of the service. A WCF service consists of contracts (data, fault and service contracts), a service instance (implementation) which is the implementation of a service contract, and configuration which contains the service endpoint information used at runtime to create the channel stack and the service instance. A WCF service endpoint consists of two core elements: the service contract and a binding. A binding describes how the service will communicate with clients at runtime. WCF provides several default bindings out of the box, covering many common communication scenarios such as BasicHttpBinding and MsmqIntegrationBinding. In addition to creating bindings from scratch, you can also extend and customize the default bindings to work as you wish. Service Instancing and opening communication Figure 1 illustrates what happens when a WCF service is run. Ultimately, the aim of the service is to map messages from clients to operations within the service instance. The service instance requires a runtime host and the host must be opened for communication to occur. The ServiceHost<T> class represents this host in code. When the host's constructor is run, it uses the service endpoint configuration combined with the contracts defined in assemblies (retrieved using reflection) to create a ServiceDescription. Thus, the Service host description contains the complete description of the service, including what contracts to use, what service implementation to use for the service contract and how the service will communicate with the outside world (binding). When the ServiceHost<T>.Open() method is run the Service Description is used to create a Channel Stack and a Service Instance. The Channel Stack is the runtime equivalent of the Binding information. The stack contains all the protocols required in order for the communication. As a minimum for any binding, this must contain at least a Transport and an Encoding. Additional protocols (such as Sessions and Transactions) can then be applied on top. For more information on the channel stack architecture, look at the following MSDN article: http://msdn2.microsoft.com/en-us/library/ms729840.aspx. Once the service host is open, the service is ready to receive incoming requests from a client. The inbound message gets received through the channel stack and the Dispatcher is then responsible for assigning the message to the corresponding Service Instance operation, deserializing the message using the appropriate deserializer and executing the operation on the service instance. For more information on serialization , look at the following blog entry: http://blogs.conchango.com/simonevans/archive/2007/05/14/WCF-serialization-with-MSMQ.aspx. Behaviors in WCF Behaviors are a way of changing the default way in which either a service instance, or operation will execute when run. Behaviors either inherit from interface IServiceBehavior or IOperationBehavior. They are attached to the service description when the host is constructed, using a mixture of configuration, and/ or any additional code you choose to add. Whether you add a behavior in code or in configuration depends on whether you want the behavior to be configurable, or if you need to behavior to be permanently applied. Examining runtime metadata using InstanceContext and OperationContext Whilst the service instance is running, the InstanceContext class provides you with the ability to examine metadata about the instance at runtime, such as information about the channel stack and the host. The lifetime of the InstanceContext is aligned to the life time of the service instance. Likewise, the OperationContext provides you with runtime access to service operation metadata for the lifetime of the call to the service operation. OperationContext provides you with access to information such as message headers and properties for inbound and outbound messages, and the Dispatcher. Handling Concurrency InstanceContextMode and ConcurrencyMode are service behavior attributes applied to the Service Description when the host is constructed. Together, these two attributes are used to configure how a service will control the lifetime of service instances and how many messages a dispatcher will hand over to an instance. The InstanceContextMode controls the lifetime of a service instance. By default, this is set to PerSession. This means the service instance will last for the lifetime of the corresponding Session (which equals PerCall if no session is available). PerCall instancing means the instance will be destroyed after the service operation completes. The final option here is Single, which means that the instance context will only be destroyed when the host is closed. The ConcurrencyMode handles how many messages the dispatcher will hand over to the service instance. The default is Single, which means the service instance runs single threaded, but this can be changed to run multi threaded by using the setting Multi. Additional to InstanceContextMode and ConcurrencyMode, the ServiceThrottlingBehavior controls how many concurrent service instances will be run by the host at one time (the MaxConcurrentInstances property), and how many concurrent calls can be made to a single instance (the MaxConcurrentCalls property). It is worth noting with this final property that the channel stack will automatically ready the next inbound message even when concurrent calls are set to 1. All of these behaviors together control how concurrency and threading will be handled within a WCF service. Figures 2 to 4 below show how these variables can affect threading and concurrency in WCF.
Figure 2 – PerCall instancing, Single Threaded with 2 concurrent instances Figure 2 shows a service that allows two concurrent service instances to run, each of which is destroyed when the call to the operation is complete (PerCall). Each service instance is configured to only allow one thread to run at a time. This means that of the three inbound messages (shown on the left), the dispatcher will handle two currently using two service instances in the process. The third message will be made ready by the channel stack for handling by the dispatcher. 
Figure 3 – PerCall instancing, Single Threaded with 1 concurrent instance Figure 3 shows what happens when the concurrent instances property is set to 1. The result is only one service instance is created and runs single threaded. A second message is made ready for the dispatcher in the channel stack and is not processed until the service instance is destroyed at the end of the first operation call. 
Figure 4 - Single Instance, Multi threaded with 2 concurrent calls Figure 4 shows how using multi threaded concurrency with a single instance would affect how the service behaves with two concurrent calls allowed. The top two inbound messages from the channel stack would make a call to an operation on a single service instance concurrently. This service instance would not be destroyed at the end of the call to the operation. The third message would be waiting in the channel stack, and as soon as the service instance made a thread free for use, the dispatcher will call the operation for the third message against the same service instance. Service Exceptions When a service instance throws an unhandled exception, the InstanceContext is said to be in a Faulted state, and the Faulted event will be raised. This event can be handled in the service host. Once the host is in a faulted state, no further service instances will be created until the channel stack is taken out of a faulted state. This can be achieved by closing and reopening the host (services hosted in IIS automatically handled restarting the host). Using MSMQ with WCF Why use MSMQ for communication? MSMQ provides core features which can be employed to solve various architectural problems. It is a store and forward mechanism for messages, and provides core features such as durability, exactly once delivery and transactional support. MSMQ should be employed in situations where you need a buffer in communication between two endpoints, such as cases where a service intermediary is required to deal with service (un)availability and reliability. Before looking at using MSMQ with WCF, it is important to understand some core concepts of MSMQ, particularly when it is employed in a distributed architecture, where endpoints are running on different servers. These concepts are largely abstracted away from you writing service code, but their impact is important to understand. Firstly, the term store and forward refers to how MSMQ will send a message across machine boundaries. When a message is sent from one machine to another, MSMQ will send the message to an outbound local queue and then forward it onto the remote queue on the other machine. If the remote machine is offline, the message will remain on the local queue for a period of time until the remote queue becomes available, or the attempt to process the message times out. The store and forward mechanism deals with the requirement that the remote machine may be offline when the message is sent. If the timeout for processing the message from outbound local queue expires, the message will be removed from the local queue and can be optionally moved to a dead letter queue. This queue is the final resting place for a message ensuring that the message has not been lost. The second concept to understand is durability. During the store and forward mechanism a message can optionally be persisted to disk. The reason for doing this would be to recover from machine failures and reboots on either the outbound queue or the remote queue. The next concept is exactly once delivery. This ensures that an individual message is assured to be sent exactly once. This assurance should be used when it is important not to receive duplicates of the same message. Finally, MSMQ can optionally support transactions either homogeneously using a local MSMQ transaction manager, or heterogeneously using DTC. When transactions are required, MSMQ requires that the message uses exact once delivery with durability in order to achieve the requirements of an ACIDic transaction. It is worth also understanding the transport that MSMQ uses for sending and receiving messages. Messages are sent using TCP/IP and received using RPC. As RPC is a heavy weight protocol, it is seen as best practice to send to a remote machine queue, but receive from a local queue. Indeed remote receives on a transactional queue are not supported in MSMQ 3.0, because of the performance implications of doing so. MsmqIntegrationBinding in Detail MsmqIntegrationBinding is an out of the box binding for communication with MSMQ alongside NetMsmqBinding. The reason that WCF provides two bindings to communicate with MSMQ is that NetMsmqBinding will only work in pure WCF only architectures, where all clients and services are WCF based. It does however provide a much richer channel stack, including support for Sessions, which in turn gives you greater transactional control. If you are working in a heterogeneous architecture where WCF is interoperating with other MSMQ based clients and services, MsmqIntegrationBinding is the only option available to you. MsmqIntegrationBinding is limited in the communication policies it can use, and it also requires service contracts specifically designed for use with the binding. Figure 5 below shows the WCF architecture from the point of view of MsmqIntegrationBinding. 
Figure 5 - WCF architecture using MsmqIntegrationBinding MsmqIntegrationBinding requires contracts that are designed only for its use. Unless additional behaviors are applied to the dispatcher, the service contract can contain only one service operation. For more information on extending the service contract in this way, look at the following link: http://msdn2.microsoft.com/en-us/library/ms752265.aspx. Service operations for MsmqIntegrationBinding can contain only one argument typed as MsmqMessage<T>, where T is the data contract used in the MSMQ message body. MsmqIntegrationBinding is also limited in terms of what policies can be applied in the channel stack. The Transport used is TCPIP for sending messages and RPC for receiving messages, and it is only at the transport level can security be applied (there is no message level security available). Beyond Transport and Encoding policies, the only optional addition to the channel stack is transactional support. For MsmqIntegrationBinding, transactions have to use auto completing transactions. This is achieved by applying an operation behavior to the service implementation with attributes TransactionScopeRequired and TransactionAutoComplete set to true. This means that the transaction (with MSMQ) will automatically be committed if the service operation completes without an unhandled exception. This is limited transactional support because it means your transactions cannot flow beyond the service operation you are running into other service operations or behaviors. The reason MsmqIntegrationBinding does not support explicit transaction control is because explicit transactions in WCF require Sessions to be enabled, which are not supported by this binding. MSMQ comes in various different versions. MsmqIntegrationBinding supports both version 3.0 and 4.0. Version 3.0 is provided with Windows XP and Windows Server 2003. MSMQ v4.0 is provided with Windows Vista and Windows Server 2008; it provides richer functionality for custom dead letter queues and poison messaging (see later). The following table lists key properties exposed by the MsmqIntegrationBinding, how these are used and what versions of MSMQ will support this property: Property name | What does it do? | MSMQ version | CustomDeadLetterQueue | Specifies the name of the custom dead letter queue to use. | 4.0 only | DeadLetterQueue | Specifies whether to use a system, custom or no dead letter queue. | All (3.0 does not support custom dead letter queues). | Durable | Specifies whether MSMQ will persist the message to disk, making it durable to server shutdown. | All | ExactlyOnce | Specifies whether MSMQ will ensure that a message is delivered exactly once. | All | ReceiveRetryCount | Specifies the number of times the channel will retry attempting to process the message from the queue (executing the service operation). This is used for poison message handling (see below). | All | ReceiveErrorHandling | Specifies how the channel will respond to a message which fails to be processed once all retry cycles are exhausted. This is used for poison message handling (see below). | All (3.0 only supports Drop and Fault values). | MaxRetryCycles | Specifies how many retry cycles are used in an MSMQ 4.0 retry queue. | 4.0 only | RetryCycleDelay | Specifies a delay between retry cycles used by the MSMQ 4.0 retry queue. | 4.0 only |
Message processing failure When an MSMQ message is processed by the channel stack and handed to the service instance from the dispatcher, the service operation must cope with exceptions. These exceptions can be separated into two distinct groups: expected exceptions and unexpected exceptions. For unexpected exceptions, WCF provides a built in mechanism called Poison Message Handling, which is covered later in this document. Expected Exceptions A general principle of all message based architectures is to handle the message as quickly as possible to a final destination (normally your endpoint). This is important to remember when dealing with expected exceptions. These are exceptions raised within the service operation that are expected to happen under certain known conditions. Expected exceptions are best dealt with explicitly in code, with a definitive outcome for handling the exception. For example, a message may contain data which is invalid for the service implementation's business rules. In this example, the message would never be allowed into the endpoint, so the message needs to be routed to another destination, such as another queue. The best way to handle expected exceptions is to catch the typed exception in a try catch block, as place the action for the exception in the catch block. With MsmqIntegrationBinding it is important to remember that transactional messages can only be processed using automatic transactions, which means that your handling code needs to participate in the outer transaction if you want the message to be moved off of the queue. The metadata about the MSMQ message can be retrieved using the OperationContext.IncomingMessageProperties collection, which for MsmqIntegrationBinding are typed as MsmqIntegrationMessageProperty. The following code is an example of such a handler for an expected exception used within a service operation that executes in an automatic transaction: try { // do service operation logic here: } catch (MyValidationException) { MsmqIntegrationMessageProperty integrationMessageProperty = operationContext.IncomingMessageProperties[MsmqIntegrationMessageProperty.Name] as MsmqIntegrationMessageProperty; string label = integrationMessageProperty.Label; if (integrationMessageProperty.Body != null) { using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required)) { using (MessageQueue badQueue = new MessageQueue(badQueuePath)) { badQueue.Send(integrationMessageProperty.Body, label, MessageQueueTransactionType.Automatic); scope.Complete(); } } } } |
Poison Message handling for Unexpected Exceptions in MSMQ 3.0 MsmqIntegrationBinding includes a built in mechanism for dealing with unexpected exceptions called Poison Message Handling. MSDN includes a lot of useful information about poison messaging at http://msdn2.microsoft.com/en-us/library/ms789028.aspx. More specifically, there is also information about using Poison Messaging with MSMQ 3.0 at http://msdn2.microsoft.com/en-us/library/ms751472.aspx. There are however quite a few missing details from these articles. MsmqIntegrationBinding exposes properties called ReceiveErrorHandling and ReceiveRetryCount which deal with the number of times the channel stack will attempt to process the message from the queue and what happens when this retry number is exceeded. In MSMQ 3.0, the only options for dealing with poison messages is either to Drop the message (remove it from the queue and ignore) or Fault, which will cause the service instance to be put in a Faulted state. When Fault is chosen for error handling, WCF will throw an MsmqPoisonMessageException, which can be handled by writing a custom error handler which implements IErrorHandler. This error handler can be attached to the service description (either in code or configuration) by attaching the error handler to a behavior. The above MSDN articles provide code samples for this scenario. Any error handler that is attached to the service description is executed when any exception occurs within the channel stack. The HandleError method is called, passing in the exception that was thrown. If the error is handled by the error handler, the method returns true back to the channel stack and no other error handler is called. The standard scenario for a handler for poison message exceptions is to move the message off of the main queue to allow other messages to be processed, and reopen the service host so that communication can continue. It is important to understand the context in which the MsmqPoisonMessageException is thrown. When this exception is thrown, receive retries have been exhausted and the InstanceContext is faulted. This means that communication is stopped in the channel stack and the host is no longer open for communication. The message transaction will therefore rollback and return to the message to the queue. The poison message handler is then able to receive this message from the queue to move it onto another queue so that normal message processing can resume once communication is reopened. Using a poison message handler needs to be understood from the point of view of concurrency. As the original transaction is rolled back a new transaction is created to move the message from the queue, there is a possibility that other concurrent instance will attempt to process the message off of the queue before the host has been completely closed and the poison handler is run. Therefore, the poison message mechanism cannot guarantee how many times the service operation will be executed before the handler is run. The handler is running under transactional race conditions where other processing service instances might not yet have fully rolled back their transactions. Therefore the handler needs to attempt to receive the message off the queue several times to ensure the message is moved off the queue. In MSMQ 3.0, the ReceiveRetryCount for each message processed concurrently is maintained in memory in the channel stack and can only be maintained for a maximum of 256 concurrent messages. The current count is available from the MsmqIntegrationMessageProperty.AbortCount property of the inbound message in the InstanceContext. Because this maintained in memory, an MsmqPoisonMessageException will cause all concurrent instances to be destroyed as the host is closed, and there all retry counts are reset. This means that with concurrent instances, it is feasible that messages may be processed more times than their retry count, because the count is reset during processing. This problem is resolved in MSMQ 4.0, where MSMQ stores the retry count on disk outside of the channel stack. Related articles
|
-
By default, Windows Communication Foundation (WCF) uses the DataContractSerializer to serialize and deserialize XML in a service, but this serialization mechanism can be replaced to use the XmlSerializer. The following article on MSDN covers this in more detail: http://msdn2.microsoft.com/en-us/library/ms733901.aspx Whilst using the Data Contract attributes is much easier, and many more types are supported for serialization, there are various scenarios where switching to the XmlSerializer can be beneficial, such as: - Migrating services from ASMX services
- Tighter control over XML serialization
- Using WCF with MsmqIntegrationBinding
The latter of these three examples is core reason why I have spent a lot of time investigating the use of serializers within WCF. Msmq integration binding relies upon the old Msmq API's used by the System.Messaging namespace in order to ensure interoperability with other legacy Msmq endpoints, such as BizTalk Server 2006 (pre R2). The scenario I have been working with is that I want to use MsmqIntegrationBinding with BizTalk Server 2006 in the short term, and move to BizTalk Server 2006 R2 and NetMsmqBinding in the future. Thus in the future I want to move towards using the DataContractSerializer, but in the short term, I need to support the old XmlSerializer, because MsmqIntegrationBinding uses this serialization method internally. Getting MsmqIntegrationBinding to work with complex data contracts I have seen dozens of samples on the internet for using MsmqIntegrationBinding in WCF, and in short the basic mechanics are as follows: - Create a service contract with a one way service operation that takes a single argument MsmqMessage<T>, where T is a data contract.
- Ensure the T of MsmqMessage<T> is specified using the ServiceKnownType attribute.
The problem is, all the examples I have seen use a single simple data contract to demonstrate this binding. When a more complex data contract is used, things get more difficult. For example if I have two data contracts A and B, and A exposes B as a data member, the above instructions alone do not work; the message is never received by the service implementation. The reason this would not work is because data contract B is not known to the serializer and thus A could not be serialized. After much hunting, I found the solution was to implement serialization using the old XmlSerializer, using XmlRoot and XmlArray attributes to decorate each data contract. By using the XmlSerializerFormatAttribute, you don't need to use the data contract attributes at all, as WCF will use the old serializer instead throughout the service contract. Comparing the DataContractSerializer with the XmlSerializer In order to ensure a smooth transition from MsmqIntegrationBinding to NetMsmqIntegration binding I have opted to support both serializers in my data contracts, with a view to retiring the XmlSerializer as some point in the future. The problem is, the serializers handle things in different ways. The table below lists the differences: DataContractSerializer | XmlSerializer | Resolution to serialization differences | If ordering is not specified, the data members are serialized in alphabetical order | The properties are serialized in the order they appear in the class | Ensure the order argument is specified in the data member attribute in the order that the properties appear in the class. | If the data contract contains an array of objects from a different XML namespace, the serialization will automatically set the namespace of the array to be that of the objects within the array. | The namespace of the array will be that of the outer data contract. | Ensure the XmlArray attribute is used against the array property to ensure that the array is serialized the same in both serializers. |
Additionally, you must ensure the XSD namespace for each DataContractAttribute and XmlRootAttribute match. You can compare how the two serialization models match by running svcutil.exe and xsd.exe against the data contract assembly and look at the output XSD schemas.
|
-
Here at Conchango we have community days every six weeks where we get together to discuss common work interests. As a .Net Architect, one of the core communities I participate in is the .Net community. At our last community day, I presented on Windows Communication Foundation, a technology I have been working with in anger now for over a year. Whilst the PowerPoint slideshow doesn't really cover any detail on what I presented or demonstrated, I thought I'd attach it to this blog anyway, as this blog covers the contract design guidelines I ran through with the community. Overview The following design guidelines can mostly be based around one single important fact about implementing a service orientated architecture (SOA); changing the design of a contract is in an expensive exercise. If changes in contract designs can be limited to a minimum, the cost of ownership of your SOA will reduce, because calling clients have to change less often. Do all you can to design service contracts to be binding agnostic This sounds like a simple point to make, but it's an easy one to forget, and difficult to achieve. The bottom line is that to get the most out WCF from an architect's perspective, you really want to try to ensure that non-functional requirements related to your communication needs do not become hard coded into your contract designs; if you can use configuration to drive transport, policy and encoding decisions, the cost of changing these features later becomes much cheaper than changing code. This can be hard and at times impossible to achieve. For example, if you decide that you need to use MSMQ integration binding, your service contract will need to use the MsmqMessage<T> class as the only argument allowed in your service operation, and thus your service contract is effectively hard wired to this binding type. However, with other examples, you can design your contracts in an agnostic way. For example, you may choose to use the Session features of WCF, and could use the IsInitiating and IsTerminating properties of the OperationContractAttrbute in your design to enforce the opening and closing of sessions. Whilst this feature is useful, it will tie you into not being able to use BasicHttpBinding, and thus if you decide later that this binding best suits your communication needs, you will need to change the design of your service contracts accordingly. Design request data contracts to act as an envelope to service requests As mentioned above, MsmqIntegration binding requires dedicated service contracts using the MsmqMessage<T> class. If you design a service contract for a web service, but later decide to change this into a service that receives a message from a message queue, you will need to use MsmqMessage<T>, where T is the type of a single data contract. Consider the following service operation: int MultiplyNumbers(int firstNumber, int secondNumber); If you choose to implement this using MsmqIntegration binding, you will need to create an envelope data contract, with properties for the first and second numbers. To avoid having to redesign data contracts later, always wrap multiple arguments into a single request data contract, such as: int MultiplyNumbers(MultiplicationRequestData request); Fault contracts require two way communication Obvious when you think about it, but if you expect service faults to be reported to the calling client, the service operation must use two way communication. Avoid contract namespace conflicts with the application layer Be careful how you name you data contracts, because you will normally be wrapping an application with its own API, and this can lead to namespace conflicts which require you to use a fully qualified namespace on every call to the type, which mean messy code. On my project, we have adopted a standard of using EntityNameData as the naming convention for data contracts. Always assign dates to your schema namespaces This is important for schema versioning in your contracts, and particularly important in you are planning you use your services with a messaging hub, such as BizTalk Server where subscription is driven off of unique schema namespaces. Only use types that translate to XSD types in your data contracts Data contracts should only use types that have a direct comparison in XSD, because you WSDL will otherwise contain XSD import statements to Microsoft generated schemas to represent .Net types. This means using arrays instead of collections, and sticking to enumerations, integers, strings and datatimes. Do not take an Object Orientated approach Remember when you are designing a data contract that it is a schema, not an object model. This means that whilst the entities in your application are likely to use inheritance, this approach does not make sense for the generated schema you are creating. Always review your generated schema using svc util to ensure that the contracts create a clean WSDL with clean XSD schemas. Implement ordering in your data contracts Ordering is an optional parameter of the DataContractAttribute that controls the order of elements as they appear on the XSD schema that is produced as a result of designing a data contract. If you do not specify order, the schema will be generated in alphabetical order. The affect of this is that newly added elements do not necessarily appear at the end of the schema, which can make altering related artifacts (such as BizTalk maps) more problematic. Data contracts should define type, not business rules It is not an in built part of WCF of define validation rules within schema (the data contract), but I am commonly asked how to extend WCF data contracts to validate properties. The reason that WCF does not support XSD restrictions through data contracts out of the box is that implementing business rules through schema design is an expensive way to implement business rules, because if a rule changes, the schema must be changed, and thus calling clients must be made aware of the change. A better way to implement business rules is within the service implementation itself. The data contract should only define the types allowed to be used by the service. This way, changes in business rules do not affect contract designs and services become cheaper to maintain going forward.
|
-
Last Tuesday I conducted a presentation at the Microsoft Architect Insight Conference entitled "Architecting web sites using Microsoft ASP.net AJAX". You can download the presentation below. The aim of the presentation was to get attendees thinking about how they choose to architect aspects of their website with ASP.net AJAX. The presentation was conducted with demonstrations on client and server side data binding patterns, and client and server side authentication patterns. I will be making the code for this available shortly. It was good to see that the web, user experience and AJAX were well represented at this year's event; all too often these subjects are not considered in terms of architectural considerations, which can easily lead to applications that perform or scale poorly.
|
|
|
|