|
|
-
The RTM version of ProxyFactory is now available on CodePlex it has full support of ASMX, .Net Remoting and WSE 3.0. Along with the binaries available in this release, the getting started package has been updated to include WSE 3.0 sample.
|
-
Even if the revolution announced by MS with its .Net 3.0 platform is just around the corner, the fact is that its adoption curve will be slow enterprise wide because the revolution goes as far as the OS both on client and server.
The emergence of the WCF platform announces the unification of all communications technologies provided by the .NET platform in a common foundation. However, it is still a safe bet to develop a library to leverage NET 2.0 generics and SchemaImporterExtension mechanisms to offer a similar developer experience.
Such a library already exists and targets only web services communication, it was developed by Christian Weyer from Thinktechture but, as opposed to the WCF ChannelFactory<TChannel>, his library doesn’t provide an effective mechanism to handle specific types during the proxy generation process. This is what is tackled by the ProxyFactory by taking advantage of SchemaImporterExtension mechanisms. Futhermore, alternative communication technologies are targeted such as Remoting and WSE. This library offers a convenient way to provide an easy-to-use programming model that first, addresses in a common way two heterogeneous communication channels (Asmx and Remoting) but also help to drive existing applications towards the unification of the communication platforms proposed by Microsoft .Net 3.0 with WCF.
Even if ProxyFactory is based a business contract it doesn’t break any functionalities available to asmx. The well-known asynchronous support still works; therefore using interface inheritance it is trivial, client side, to add the methods allowing asynchronous calls to the original contract.
Thanks to a custom Dependency Property-like mechanism the same level of parameterization of each CommunicationPlatform is still available. It is important to keep in mind that the ProxyFactory federates where WCF unifies; therefore each CommunicationPlatform has its own set of parameters. These are exposed via a Dependency Properties mechanism inspired by .Net 3.0’s. Furthermore, this mechanism is used to define the cache policy associated to the dynamically generated assembly.
This project is available on Codeplex at the following url: http://www.codeplex.com/ProxyFactory so watch out for the first release!
Along with the binaries, a getting started package will be available where you will find details about the concept behind the ProxyFactory and 4 samples to walk through step by step. The first one covers a web service dynamic proxy generation, the second covers a .Net Remoting dynamic proxy generation, the third and fourth shows you additional concepts such as asynchronous calls and proxy configuration using a mechanism similar to dependency properties as described above.
Finally, if you really can’t wait for the release to go public (which will happen sometime between today and tomorrow) check out this video for a quick overview of ProxyFactory.
So gentlemen, start your favorite browser!
|
-
People, am at your service! But I am not afraid to say that I am dead lazy so that why I'm a big fan of code snippets. You've probably noticed the exe SvcConfigEditor located @ %PROGRAMFILES%\Microsoft SDKs\Windows\v1.0\Bin, this is a great tool for creating both service side and client side configuration file. However most of the time, when I'm exploring the depth of WCF I just want to stay focus on what am doing and don't want to start opening up any other applications.
That's why I took few minutes to create a set of code snippets that speeds up the process of creating those configuration files in a matter of seconds.
Here are the snippets I've created so far:
- client basicHttpBinding,
- client wsHttpBinding,
- client netTcpBinding,
- client netNamedPipeBinding,
- service basic configuration exposing 3 end points (wshttp, netTcp and netNamedPipe),
Additionaly, I've added a three other snippets related to Diagnostic that I find useful:
- enable perf counter,
- enable default trace,
- enable default message logging.
Feel free to edit, modify, customize, extend those snippets to suit your needs.
The code snippets are available for download here. Unzip it to: [Your Documents]\Visual Studio 2005\Code Snippets\XML\My Xml Snippets
Last hint, I told you I'm lazy so the keyboard shortcut to access Snippet within Visual Studio 2005 is [CTRL]+K+X.
|
-
One sure thing about Windows Communication Foundation is that you can easily define multiple endpoints on both service and client side and this only by configuration. As shown in this MSDN sample, the user decides explicitly which EndPoint to use client side. One interesting thing is that if you don’t specify any EndPoint to use during the creation of the proxy then the last EndPoint defined in the client’s configuration file is used. This is what I want to focus on in this post. I am going to show you a way to easily add a bit of intelligence in the way multiple client side EndPoints can be handled. Without going into the entire business capability map buzz (think about how you could add some functional considerations to the choice of a particular service based on business facts instead of purely technical ones), wouldn’t it be nice to let the system pick the best EndPoint for a specific service? (personally I think this should have been provided right out of the WCF box)
So let’s start with a few basic assumptions:
- if a service and a client are on located on the same machine, I want to stay as high as possible in the transport layers for performance considerations (yeah, you’re right I’m thinking about IPC or NetNamedPipe as it is called in WCF),
- if a service and a client are on different machines within the same network (intranet) I do not want to use all the bells and whistles of SOAP and WS where it is not absolutely required,
- if a service is to be accessed over the WWW then I want to take advantage of WS.
Based on these facts, here is a table that sums it up:
|
NetNamedPipe |
NetTcp |
P2P |
WsHttp |
| Local Machine |
X |
X |
X |
X |
| Intranet |
|
X |
X |
X |
| Internet |
|
|
X |
X | The SmartClient generic class is the main entry point. This class wraps the creation of the ChannelFactory and allows using the most appropriate endpoint configuration. It is similar to the ClientBase<T> class availavle in System.ServiceModel.dll thus deriving from ICommunicationObject (see this blog for a very good overview of the communication state machine that ICoomunication offers). The constructor requires an enumeration that offers 2 options to get the configuration information. The first one ConfigurationMode.ClientConfiguration can be seen as the “classic” way of doing things. The configuration is read from the client’s configuration file. The other one ConfigurationMode.ServiceMetadata requires near to zero configuration client side (you still have to know the service’s base address though). Instead of reading the EndPoints declaration and configuration out of the client’s configuration file, the MetadataResolver that I presented in a previous post which allows you to get a ServiceEndPointCollection containing all the EndPoints exposed by the service is used.
Using this technique, the obvious advantage is that there is no maintenance cost associated with the client’s configuration file apart from the service base address.
Now, let’s drill into the code.
The following constructor is used to initialize the ChannelFactory:
public SmartClient(ConfigurationMode configMode) { this.Initialize(configMode); switch (configMode) { case ConfigurationMode.ClientConfiguration: _factory = new ChannelFactory(this.GetBestEndPointLocalConfiguration()); break; case ConfigurationMode.ServiceMetadata: ServiceEndpoint __svcEndpoint = this.GetBestEndPointServiceMetadataConfiguration(); _factory = new ChannelFactory(__svcEndpoint.Binding, __svcEndpoint.Address); break; } }
As you can see, two methods are called based on the ConfigurationMode:
- GetBestEndPointLocalConfiguration, get the configuration out of the client app.config and pick the most suitable EndPoint.
- GetBestEndPointServiceMetadataConfiguration, get the configuration out the service metadata and pick the most suitable EndPoint.
The following code demonstrates how the selection is made between all the available EndPoints: protected virtual String GetBestEndPointLocalConfiguration() { Int32 __mostSuitableIndex = 0; ServiceModelSectionGroup __serviceModelSectionGroup = this._LoadClientConfigurationFile(); foreach (ChannelEndpointElement channelEndPointElmt in __serviceModelSectionGroup.Client.Endpoints) { switch (channelEndPointElmt.Address.HostNameType) { case UriHostNameType.Basic: case UriHostNameType.Unknown: //can't decide so do nothing and go to the next. break; case UriHostNameType.Dns: //LAN (petit clin d'oeil aux frères tombés au combat) if ( (channelEndPointElmt.Address.Host.ToLower() == LOCALHOST) || (channelEndPointElmt.Address.Host == IPAddress.Loopback.ToString()) || (channelEndPointElmt.Address.Host.ToLower() == Dns.GetHostName().ToLower()) || (channelEndPointElmt.Address.Host == Dns.GetHostAddresses(Dns.GetHostName())[0].ToString()) ) { Int32 __currentIndex1 = _orderedMasterEndPointPriorityLocalConfig.FindIndex(STARTINDEX_LOCAL, SPAN_LOCAL, delegate(KeyValuePair keyValue) { return keyValue.Key == channelEndPointElmt.Binding; }); if (__currentIndex1 > __mostSuitableIndex) { __mostSuitableIndex = __currentIndex1; _orderedMasterEndPointPriorityLocalConfig[__currentIndex1] = new KeyValuePair(_orderedMasterEndPointPriorityLocalConfig[__currentIndex1].Key, channelEndPointElmt.Name); } } else { Int32 __currentIndex2 = _orderedMasterEndPointPriorityLocalConfig.FindIndex(STARTINDEX_LAN, SPAN_LAN, delegate(KeyValuePair keyValue) { return keyValue.Key == channelEndPointElmt.Binding; }); if (__currentIndex2 > __mostSuitableIndex) { __mostSuitableIndex = __currentIndex2; _orderedMasterEndPointPriorityLocalConfig[__currentIndex2] = new KeyValuePair(_orderedMasterEndPointPriorityLocalConfig[__currentIndex2].Key, channelEndPointElmt.Name); } } break; case UriHostNameType.IPv4: case UriHostNameType.IPv6: //W3 -> want to use WsHTTP if available Int32 __currentIndex3 = _orderedMasterEndPointPriorityLocalConfig.FindIndex(STARTINDEX_WWW, SPAN_WWW, delegate(KeyValuePair keyValue) { return keyValue.Key == channelEndPointElmt.Binding; }); if (__currentIndex3 > __mostSuitableIndex) { __mostSuitableIndex = __currentIndex3; _orderedMasterEndPointPriorityLocalConfig[__currentIndex3] = new KeyValuePair(_orderedMasterEndPointPriorityLocalConfig[__currentIndex3].Key, channelEndPointElmt.Name); } break; } } return _orderedMasterEndPointPriorityLocalConfig[__mostSuitableIndex].Value; }
If you want to implement your own intelligence to pick YOUR most suitable EndPoint, you can easely do so by overriding those two methods and implement your own logic.
To conclude, using this technique it is easy to provide your own “intelligence” into the model in order to choose between several options and not just use the default one and, as you’ve seen, can spare you a bit of maintenance overhead by making the choice right out of the service’s metadata. Obviously, this can be greatly improved in numerous ways. A first improvement would be to cache the EndPoint used instead of testing the configuration each time a proxy is created. Additionally, by adding the same functionality around multiple services implementing the same contract - the choice can be made based on technical considerations such as communication protocol, average response time etc… but also based on functional considerations such as shipping cost, customs cost etc… for an order service and I won’t even start on putting some WWF into this “intelligence”…
The source code is available here The binaries are available here
(WinFX February CTP required)
|
-
Nowadays it becomes more and more useful to be able to get information about how your database is defined and structured. This is where database metadata comes in. By now, pretty much every RDMS has means to retrieve this info by querying specific system views or tables. Even though the info exposed is pretty self explanatory, there is no easy way to get all this info exposed in a nice way (by nice way, I mean a nice object model I can easily use and extend).
This is what I want to discuss here.
So first, what is the information required, you may ask? Well, first we'll assume that we want metadata for only one database (even though the model can be easily extended I prefer to focus on tables, columns and relational info*). Therefore, the information required is: all the tables names and for each table all its columns name, type, length, precision, scale, and if it allows dbNull and if whether a given column is a primary key or a foreign key.
Then, now that you know what you need to extract, what is providing Microsoft in ADO.Net 2.0? Well, the DbConnection class has a method called GetSchema which does exactly what it says on the tin. Actually, it only does part of the trick because it does not provide info about primary keys and foreign keys. If you look at the resources embedded in system.data.dll using Reflector you can see a file called System.Data.SqlClient.SqlMetaData.xml which contains the infos required to query Sql Server and get its metadata (this file is used by the GetSchema method to extract the data from the db).Thus, in this file you can find the queries used to extract the metadata from the database system views except the ones regarding the primary and foreign keys.
After a quick look at SqlServer 2005 system views, you can quickly end up with the following query which returns all the PKs and FKs of the current db:
SELECT INFORMATION_SCHEMA.TABLE_CONSTRAINTS.TABLE_NAME AS TABLE_NAME, INFORMATION_SCHEMA.KEY_COLUMN_USAGE.COLUMN_NAME AS COLUMN_NAME, REPLACE(INFORMATION_SCHEMA.TABLE_CONSTRAINTS.CONSTRAINT_TYPE,' ', '_') AS CONSTRAINT_TYPE FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS INNER JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE ON INFORMATION_SCHEMA.TABLE_CONSTRAINTS.CONSTRAINT_NAME = INFORMATION_SCHEMA.KEY_COLUMN_USAGE.CONSTRAINT_NAME WHERE INFORMATION_SCHEMA.TABLE_CONSTRAINTS.TABLE_NAME <> N'sysdiagrams' ORDER BY INFORMATION_SCHEMA.TABLE_CONSTRAINTS.TABLE_NAME ASC
Notes:
- The WHERE clause is specified only to exclude from the resultset the PK of the SysDiagrams table (which is created only if you have diagrams associated with your database).
- The replace statement in the SELECT clause is only used to transform the key used to flag a particular constraint as a “PRIMARY KEY” or “FOREIGN KEY” in order to directly map it to an Enum (which of course does not allow spaces).
So by combining the info returned by the GetSchema method (which provides everything except the PK and FK info) and the data returned by the query described above we can easily define an object model that will expose the database metadata in a nice and simple way. Of course, even though this post focuses on extracting metadata information from Sql Server 2005 the object model must be flexible enough to support other RDMS.
Then once you know how to extract the required information from the database, it’s time setup a nice object model to get and publish this info. Here is the deal, each RDMS has its owns way of publishing its metadata based on different queries, so the first thing to think about is he a set of classes who are going to offer a generic base that anyone can extend in order to support any RDMS offering metadata capabilities.
The base resulting object model is pretty self explanatory and described bellow in a class diagram:

Obsiously, the Table and Column templated types referenced in BaseMetaDatabase must implement BaseMetaTable and BaseMetaColumn as you can see in the code.
The code bellow shows the core mechanism allowing getting the metadata (this is the Load method of BaseMetaDatabase):
public void Load { // 1. Preloads. this.PreLoad(); // 2. Loads tables. DataTable __tablesDataTable = GetDataTable(MetaDataCollectionNames.Tables); foreach (DataRow tablesDataRow in __tablesDataTable.Rows) { Table __table = this.Fill<Table>(tablesDataRow); this.Tables.Add(__table); this.PostLoad(__table); // 3. Loads columns. string[] __restrictions = new string[4]; __restrictions[2] = __table.TableName; DataTable __columnsDataTable = GetDataTable(MetaDataCollectionNames.Columns, __restrictions); foreach (DataRow columnsDataRow in __columnsDataTable.Rows) { Column __column = this.Fill<Column>(columnsDataRow); __table.Columns.Add(__column); this.PostLoad(__column); } } }
From this baseline, you then have to implement a custom MetaDatabase, MetaTable, MetaColumn and MetaColumnNames . Because the RioterDeckers are cool guys, we already offer an implementation for Sql Server 2005.

So let’s have a look at the code implemented for Sql Server 2005:
First, the most interesting, adding the primary key and foreign key info to each column (isprimaryKey and isForeignkey props). This is done by adding a bit of logic into the Preload and PostLoad methods. The PreLoad method extracts additional metadata from the Db using SqlCommands.SELECT_CONSTRAINTS_KEYS which has been defined (this is typically the TSQL query described above). the PostLoad method is then used to set the IsPrimarykey and IsForeignKey properties of SqlMetaColumn.
protected override void PreLoad() { using (DbConnection __connection = this.InnerDbProviderFactory.CreateConnection()) { __connection.ConnectionString = this.ConnectionString; // Connect to the database then retrieve the relation constraints information. __connection.Open();
DbCommand __command = this.InnerDbProviderFactory.CreateCommand();
__command.CommandText = SqlCommands.SELECT_CONSTRAINTS_KEYS; __command.Connection = __connection;
using (DbDataReader __reader = __command.ExecuteReader()) { while (__reader.Read()) { _relationConstraints.Add(string.Format("{0}.{1}.{2}", __reader[SqlMetaDataColumnNames.TableName], __reader[SqlMetaDataColumnNames.ColumnName], __reader[SqlMetaDataColumnNames.ConstraintType])); } } } }
protected override void PostLoad(SqlMetaColumn column) {
column.IsPrimaryKey = _relationConstraints.Contains(string.Format("{0}.{1}.{2}", column.TableName, column.ColumnName, ConstraintType.PRIMARY_KEY));
column.IsForeignKey = _relationConstraints.Contains(string.Format("{0}.{1}.{2}", column.TableName, column.ColumnName, ConstraintType.FOREIGN_KEY)); }
The SqlMetadataColumnNames class is interesting in the sense that it allows to access the data returned by the GetSchema method (depending on the provider usded a different resource file may host that data, in the case of Sql Server 2005 System.Data.SqlClient.SqlMetaData.xml contains the info required.)
Notes: * Check out the class called Utils in the RD.Metadata.Database project which allows the convertion of a SqlDbType to a DbType. * If you have good insight into the .Net Framework 2.0 you may wondering why I am not using the Microsoft.SqlServer.Server.SqlMetaData library in order to retrieve type mappings between .Net and Sql. The answer is quite simple: most .Net types do not have mapped SqlTypes which is quite confusing.
Download the code with a sample project.
A big THANKS to Raskal for his contribution on this dev.
Acknoledgement:
-
Actually the number of round trips to the database is not optmized at all. At this stage, loads of connections to the db are made to get the metadata out. With small tweaks to the Load method the number of connections to the db can be reduced to its minimun, i.e. one to get the tables, one to get the columns and finally one to get the relational constraints. So stay tuned, the optimized code will be posted soon!!!
Additonal reading: SQL Essentials: Foreign Key Metadata: Improving on INFORMATION_SCHEMA Views
|
-
WCF is very cool and the dudes at MS done a lot of work but as it is presented now, maintaining the configuration client side up to date could be quite a pain to handle. Imagine you have 200 clients using your funky Wcf service. They would all have in their conf file a section like this one: <client> <endpoint configurationName="default" address="http://localhost/servicemodelsamples/service.svc" binding="wsHttpBinding" bindingConfiguration="Binding1" contract="IDataContractCalculator" /> </client> <bindings> <wsHttpBinding> <binding configurationName="Binding1" /> </wsHttpBinding> </bindings>
Now, you decide to change the existing endpoint (server side) with a new one that uses SSL for security reason. How do you update your clients? You can quickly see that it can become tedious. So the idea I want to detail here is to implement a discovery service similar to what UDDI does and to use a metadata resolver to get the configuration out of the service in order to create dynamically a proxy allowing the client to discuss with the service.
So how would that work you may ask? Here is the deal:
First, you need a discovery service; its purpose is to give back a Uri based on the WCF contract someone wants to "speak". That Uri serves two goals; first of all when a WCF service starts it first contacts the service discovery and gets the Uri that corresponds to the contract it’s implementing. This allows the service to set its ServiceHost base address and then open it up. The base address is set as HTTP:// thus allowing WS-MEX request. The service is now ready to serve requests from clients. On the other hand, the Uri is also used client side by the MetadataResolver to retrieve endpoints configuration(using its RetreiveEndPoints method) required to talk to the service allowing the client to only know the address of the discovery service and the contract he wants to “speak” and still be able to dynamically generate the proxy to initiate the communication with the service.
Along with this discovery service, you need a storage that keeps track of pairs of contract/serviceBaseAddress. Because the information to store is only pairs of contract/Uri (some other will be added later on in my next post so stay tuned!), a database would be over the top. A configuration file would do the job perfectly (check out Avalonboy’s blog who published a great post about System.Configuration using FW2.0). Here is a sample of its structure: <mySectionGroup> <WcfDiscovery discoveryServiceUri="http://localhost:8000/IServiceDiscovery"> <services> <service contractType="IProductService" discoveryBaseAddress=http://1 /> <service contractType="ICurrencyService" discoveryBaseAddress=http://2 /> </services> </WcfDiscovery> </mySectionGroup>
Check out the following diagram to get the storyboard of the service discovery:
Now, here is an excerpt of the code to implement such functionality:
//Get the EndPoints of a specific Wcf Service from its ServiceHost base address public static void LoadMetaData(String baseAddress) { if (_baseAddressesLoaded.Contains(baseAddress)) { return; } else { _baseAddressesLoaded.Add(baseAddress); } try { MetadataResolver __metaDataResolver = new MetadataResolver(new EndpointAddress(baseAddress)); ServiceEndpointCollection __temp = __metaDataResolver.RetrieveEndpoints(); if (Validation.IsNull(_serviceEndPoints)) { _serviceEndPoints = __temp; } else { foreach (ServiceEndpoint item in __temp) { _serviceEndPoints.Add(item); } } } catch (Exception ex) { throw new Exception(String.Format("An error occured while retreiving metadata from the following address {0}. Check that the associated service is started.",baseAddress),ex); } } //Custom client base that exposes the proxy as public public class ClientBase<T>: System.ServiceModel.ClientBase<T> where T: class { #region Props public T Proxy { get { return base.InnerProxy; } } #endregion #region Ctors public ClientBase(EndpointAddress endPointAddress, Binding binding) : base(endPointAddress, binding) { } #endregion } //Get a proxy based on contractType the client wants to “speak to” public static T Proxy<T>() where T : class { if (!typeof(T).IsInterface) throw new Exception("A service contract must be an interface."); //Get metadataInfo for T type ServiceMetaDataDiscovery.LoadMetaData(ServiceHostHelper.GetServiceContractUri(typeof(T)).AbsoluteUri); //Get its Endpoints ServiceEndpoint __svcEndPoint = ServiceMetaDataDiscovery.ServiceEndPoints.Find(new System.Xml.XmlQualifiedName(ContractDescription.GetContract(typeof(T)).Name, ContractDescription.GetContract(typeof(T)).Namespace)); if(Validation.IsNotNull(__svcEndPoint)) return new RD.ServiceModel.ClientBase<T>(__svcEndPoint.Address, __svcEndPoint.Binding).Proxy; //Return the Proxy to communicate w/ the T service throw new Exception("Could not find info related to {0} service in the service discovery. Ensure that the {0} service is referenced and that the discovery service is running."); }
To sum up, the benefits of such a setup are:
* The clients only need to know where to contact the serviceDiscovery, then everything up to proxy creation is dynamic, * The address lookup is driven exclusively by contracts, * Each service owns its configuration except its ServiceHost base address which is set on the serviceDiscovery config, * The service side configuration can be changed without impacting the clients, * With little additional code the serviceDiscovery can also centralize service dependency and notify parties of service state. * Each service asks the service discovery the base address it should use to open its serviceHost thus no duplicate config info.
|
|
|