Today's task, a somewhat detailed code analysis of one of our Windows CE projects. In addition to reviewing from a lead architect point of view (design and implementation), I also needed to take a look at general coding practices. For this I turned to the Code Analysis tool in Visual Studio, and NDepend. I had remembered reading about NDepend on Scott's blog, and went back there to re-read his post. I highly recommend it, as it's a great quick start and introduction to NDepend. Scott also has a pod cast available, and the the NDepend web site has links to several video tutorials showing how to use it. One thing I found lacking, was there were no support forums on the NDepend web site. I had a couple of issues getting my Compact Framework application to be property recognized by NDepend. The main issue, was NDepend was trying to use the full framework version of System.Data instead of the Compact Framework version. I noticed that there was an option when selecting assemblies to resolved missing ones (well, some it let me, others it didn't), and figured the resolved location was stored in the project configuration file. So I opened it up in note pad and sure enough, there were all the directory paths. I removed all of the paths that were added by default, and added my own paths to my project. I also added paths to the SQL CE 3.5 directory, as well as the location of the compact framework dlls (See update below). Unfortunately I still had issues with NDpend detecting multiple dlls (System.Data and System.Windows.Forms) with the same name. I decided to remove the path statements for everything except my project. This resulted in warning about not being able to resolve dependencies, but the analysis completed. There is allot of information presented, almost to the point of information overload. I highly recommend printing out and reviewing the NDepend placemat as a quick reference. I concentrated primarily on the metrics, which use NDepend's Code Query Language to identify possible problems. I found myself viewing all of the queries to figure out what they were looking at. The documentation of the queries is excellent, and each one includes a link to the NDepend web site which defines the metric is pretty good detail. I am quite impressed with the amount of information that NDepend gave me about my project. There were/are a few usability issues, but they can be overcome. I hope that I can continue to find time to make use of this excellent tool. I spent some time watching the video tutorials, and they were very helpful. I've been trying to get a side project started at home to try out all the tools, techniques, patterns, etc that I don't have time for at work, and this will definitely find it's way onto my to do list. I think it will be very interesting to use this from the beginning and see how the different metrics change as I build out my project. Update: - 1/2/2008 - After watching the videos, I discovered that when you are setting up your project in VisualNDepend, there is a screen that lest you add/remove directories in the application. This is much easier then editing the project file manually like I was initially doing.
Other Links:
Yesterday, a co-worker of mine passed on a link for the Microsoft Sync Runtime, suggesting that we may be able to leverage it in the future in order to meet some requirements we have on our project. Microsoft Sync Framework is a comprehensive synchronization platform that enables collaboration and offline access for applications, services and devices. It features technologies and tools that enable roaming, sharing, and taking data offline. Using Microsoft Sync Framework, developers can build sync ecosystems that integrate any application, with any data from any store using any protocol over any network. Reading thru the initial article, I was quite impressed, but it left out a key piece of information, how does the sync runtime work over the Internet. Not just the internet, but also any decent corporate network were clients do not have direct database access. I made my way to an overview of the Sync Services for ADO.Net, which is what I would be most interested in (although syncing file systems has an interesting appeal as well), and they clearly showed the Internet cloud, but not really how the sync framework worked with it. I actually had to dig into the documentation, and finally found some details on how to get this to work on the Internet. If you read towards the bottom of the documentation, you can see that you implement a proxy on a middle tier. On the middle tier, the service inherits from and exposes the same methods as ServerSyncProvider (the abstract class from which DbServerSyncProvider inherits). The methods are then executed by the application code over a direct connection to the server database. The results are routed through the middle tier and back to the client. Basically the middle tier exposes the same interface that the client application would normally use if it could connect directly to the database. There was a link which gives a specific example of setting up a n-Tier environment and provides a good start. However, I could not find exactly what I was looking for, which was an example of using WCF as the transport mechanism. With WCF I could have 1 middle tier service with multiple end points configured, such as a Basic Http for Internet, and TCP for intranet. WCF also adds support for encryption and authentication, which I didn't see called out specifically in the sync runtime. Other items of interest include, how to setup a database for different sync scenarios, and peer to peer synchronization. All in all, the Sync runtime looks like a very promising framework for building disconnected applications. It will require some re-architecting for existing solutions, specifically the data store, but it might be worth it in the long run. I will be sure to post updates if I get a chance to play around with it at all. It is defiantly something that would save me some time on a side project I am hoping to start working on soon.
Just when I thought I was on a roll, my unit tests stopped working, all of them. At least when they all fail, and fail in the same way, you know it's not your code that's causing the issue. Switching to running the unit tests in debug showed that they were failing when I was trying to leave the service domain as part of my "database rollback" functionality (idea and code I based my work off of was from a 2004 MSDN article), which uses Enterprise Transactions. I had a previous issue with MS DTC and my unit tests, but this is different. No helpful messages in the event log (first place I looked this time) on my development server. My laptop, where I am running the unit tests from, have a couple of entries for DistributedCOM but they are kind of vague. I decided to jump over to dtctester and see what that showed. It hung on "Enlisting Connection in Transaction" for at least 1 minute (I left to get a soda) before finishing. The most recent changes I've made to my development server are: - Upgrade to BizTalk 2006 R2
- Install .Net Framework 3.5 RTM
- Install Windows Server 2003 SP2
My bet is on a fix in SP2, or the SP2 installer resetting the MS DTC configuration. I found an excellent MSDN article on trouble shooting MS DTC in an effort to verify how I should have MSDTC configured, specifically the security settings. I noticed that mutual authentication was required, and from previous issues with DTC and the fact that my machines are not on a domain, I set this to no authentication and tried again, no go. I did find a new entry in the application event log on the dev server which seems to point with an issue on the dev server itself. MS DTC is unable to communicate with MS DTC on a remote system. MS DTC on the primary system established an RPC binding with MS DTC on the secondary system. However, the secondary system did not create the reverse RPC binding to the primary MS DTC system before the timeout period expired. Please ensure that there is network connectivity between the two systems. Error Specifics:d:\nt\com\complus\dtc\dtc\cm\src\iomgrsrv.cpp:1318, Pid: 2728 No Callstack, CmdLine: C:\WINDOWS\system32\msdtc.exe Nope, it wasn't the dev server. I decided to run dtctester on 2 other computers and they both passed. The first time I ran the tool on one of the computers, I got an error that was very similar. The only difference was the native error. On my laptop (Vista) the native error is -2147168242 and on the other computer it was -2147168220. The issue on the other computer was that I had not setup DTC for network communication. So now I knew it was an issue with my laptop, which is running Vista. A couple of things I've done recently were: - Remove VS 2008 Beta 2 and all related components and install RTM
- Install various updates from Windows Update
- Installed VPN software
- Was playing around with network settings trying to get wireless working at my parents over the holidays
- Uninstalled and reinstalled newer version of VMWare
I know I had problems with the firewall before, so I turned off the Vista firewall, and success, dtctester passed. En-enabling the firewall caused the dtctester to fail again. In the simple firewall UI, distributed transaction coordinator was selected. I went back and looked at a post I wrote on our internal network, and I had mentioned that the VMWare network adapters change the zone that the firewall thinks you are in. Well, I was in the public zone, and couldn't change it. There should have been a Customize option on the Network and Sharing center. I remembered that I had gone into Services, and disabled some that I "thought" I could do without. One of them was Network Location Awareness, which caused Network List Service to fail to start. Once those 2 were running, the firewall knew I was on the private network, and DTC worked again. My firewall exception was for the private network only. Wow, that was a trip. Download MS DTC Tester: http://support.microsoft.com/default.aspx?scid=kb;en-us;293799
So I was updating another computer from Visual Studio 2008 Beta 2 to RTM over Christmas and noticed something during the install process. You know, the install process, where they show pictures of people I never saw in my computer science classes, interviewed, or seen at any of development/industry events. Got to love marketing. Back to what I saw though, the Sql Publishing Wizard. A quick Google search showed me this was previously an add in which has made it's way into Visual Studio 2008 as a default component. You can download and read about it on CodePlex. From the home page on the CodePlex, the Sql Publishing wizard supports two modes of operation: - It generates a single SQL script file which can be used to recreate a database when the only connectivity to a server is through a web-based control panel with a script execution window.
- It connects to a web service provided by your hoster and directly creates objects on a specified hosted database
I tested out the single SQL script mode on our new database, which is quite complex and has challenged other scripting tools. I am extremely happy to report, that the single generate script reproduced our database perfectly. What's even better, is that it is an open source project, so you can make some changes, maybe add some schema object filters, or separate the objects into separate files.
Inversion of Control (IoC) and Dependency Injection (DI) are two practices, patterns, techniques, that I have been very interested to take a look at in greater detail. I came across a tutorial (the tutorial) which goes over IoC and DI using the Castle Windsor Container (and Micro Container), part of the Castle Project. Two other similar implementations presented in the tutorial are Spring.NET and StructureMap, as well as Microsoft's ObjectBuidler DI framework. The ObjectBuilder framework is used in the Enterprise Library, and Composite UI Application Block (CAB), which is good enough for me to steer clear of it and try out the Windsor Container instead. I have never really been impressed with the Enterprise Library, and the CAB seemed overly cumbersome. Granted, it has been over a year since I last looked at the CAB and EntLib in any great detail, so maybe things have gotten better. I know allot of people live and breath EntLib, but I'm trying to experience some other thoughts outside the typical Microsoft sphere, such as Alt.Net. Microsoft is starting to catch onto the Alt.Net movement especially with the introduction of the Asp.Net MVC framework, a topic for a future post. Part I So what exactly is inversion of control? It helps if you consider how an application is traditionally written and define what "control" means. "Control" can be thought of as adding references to various assemblies, declaring instances of specific specific classes by calling the appropriate constructor and passing in parameters. The tutorial refers to this as a "canonical programming process". Now if we take the opposite, or invert the control, then our application does not reference specifically assemblies and does not instantiate concrete classes. Instead your application makes use of interfaces or abstract classes and relies on a framework to instantiate the classes as needed. This is commonly referred to as the Hollywood Principle, and what post on design patterns would be complete without a link to Martin Fowler. The tutorial talks about another design principle, which is the separation of concerns (SoC). Separation of Concerns states that a component should do only one task, and do it very well. In the tutorial's example application, the functionality to download HTML data should be separated from the logic to parse the HTML itself. By separating the two pieces and applying IoC, if a new, faster algorithm for parsing was written, it could be swapped out with the original with no modification to the application. Under a traditional model, you would have to edit the single class, change the parsing routine, and then recompile the class and application. Then what happens when management says, "well the first algorithm was slower, but it was written by the boss's nephew....". The tutorial defines a component as: "... a small unit of reusable code. It should implement and expose just one service, and do it well. In practical terms, a component is a class that implements a service (interface). The interface is the contract of the service, which creates an abstraction layer so you can replace the service implementation without effort." The power of an Inversion of Control framework does not become apparent until you have components with dependencies on other components, each with their own dependency. The Castle project refers to it's IoC as a container which is offered in two versions. The first exposes the core IoC functionality and is called the MicroKernel. In the simplest of terms, you add components by specifying a key (ID), type and assembly to load. You then ask the container for an instance, which it returns, loading all required dependencies. The 2nd version of the container, Windsor, extends the Micro Kernel and is used most often. It adds supports for generics to reduce casting and external configuration (app.config and web.config) options to name a few. Part II: - Constructor, Setter, and Component Injection
- Constructor arguments, and [property] setter arguments can be specified in the configuration file.
- If more then one component of a service is specified, you can specify which component to use, using the ${ComponentName} syntax in the configuration file.
- The name of the XML Elements in the configuration file should match a constructor parameter and property name.
- Collections in the configuration file
- Arrays, Dictionaries and Lists can be specified in the configuration file.
- This allows you to pass in a collection of components and allow the parent container to decide which one to use.
- Collections can be specified for constructor parameters and property setters.
Part III - Separation of configuration and data
- The configuration file can get complex pretty fast, especially when you are specifying a large set of data for a collection.
- Windsor allows you to work around this by moving the data to the top of the configuration file under a <properties /> section. Child elements can be referenced in the configuration section by using the #{ChildPropertyName}
- The tutorial points out that the real power of this, lies in the fact that you can put the properties in a separate file.
- Custom Converters
- Windsor supports converting from strings (in the configuration file) to other types.
- You need to implement AbstractTypeConvertor, and a new class inheriting from WindsorContainer to register your TypeConvertor
- Decorator Pattern
- The decorator pattern basically has you wrapping a component that implements IInterface, with another component that implements IInterface, taking the original component as a constructor parameter.
- Once you have the new implementation, you can perform things like benchmarking around specific method calls.
- Since everything is setup via the configuration file(s), there was no need to re-compile the main container.
- Define and If
- Windsor supports an IF/Else syntax in the configuration file
- Windsor also supports using DEFINED variables, such as DEBUG. This is not the same as the compiler flags, you have to add your own DEFINE statements in the configuration file. this can be in either the main component configuration, or your properties configuration.
Party IV - Lifestyles
- Windsor supports 4 object lifecycles
- Singleton (Default) - Each call to Resolve returns the same instance
- Transient - Each call to Resolve returns a new reference
- Thread - Each thread gets a single instance
- Pooled - Instances are retrieved from a pool. If there are no more available instances in the pool, a new one is created. All instances are created upon the container being instantiated.
- Custom
- Specify the lifestyle using attributes (i.e. [Transient]), or via the configuration file
- Facilities
- From the official documentation (taken from the tutorial): Facilities augment the MicroKernel capabilities by integrating it with a different project or technology, or by implementing new semantics.
- Built in facilities (Covered by the tutorial):
- Factory - Allows you to use classes which implement an Interface but use the factory pattern
- Startable - Components are "started" as soon as possible after the application is started, and stop when it's released. You can implement an IStartable interface, or, specify start and stop methods in the configuration file.
- The logging facility is mentioned as one being worth while to investigate, but it's not covered.
- Commission and Decommission
- Implement the ICommission and IDecommission to specify code which should be run when the component is created and destroyed by the container. Depending on the lifestyle chosen, the actual time at which the decommission code is called varies, and without understanding this, you may get unexpected results.
- IDisposable
- If you implement IDispoable, the container will automatically call the dispose method when the component is no longer needed.
Questions: - Is there a utility that can analyze an assembly, namespace and/or class and generate a skeleton configuration file?
- The StructureMap lists some tools that make configuration file management easier.
- Other ways to store configuration data. It would be great if you could load configuration, or the properties from a SQL data store.
- StructureMap supports full programmatic configuration.
Conclusion: I can't wait to try out the Micro Kernel and Windsor Container in a project. The flexibility is phenomenal, and the decorator pattern makes it extremely easy to extend functionality. I only took a quick look at the StructureMap and Spring.Net, and they have some interesting features which need further investigation.
I thought I was missing something in SQL Management Studio, as I couldn't believe the functionality to export or script database diagrams wasn't included. I went off to Google to search for something, either how to script it myself, or some missing function. First I came across this post, but wasn't to inclined to copy/paste the script and clean it up. I later came to find out it was for SQL 2000, and would not work in 2005 (good, saved some time there). I continued my search and found an article on Code Project that provided SQL scripts to do exactly what I needed. The scripts are actually 2005 updates of the 2000 scripts I found on my first search. Even if my Google search fails, I need to remember to hit up Code Project, it's a wealth of information.
Stumbled across Volta by accident today. Volta is a prototype developer tool developed by Microsoft Labs. What I got from the FAQ was that You to develop an entire application from the perspective of the client, as a single Tier, using out of the box tools like Visual Studio 2008. You then add declarations to your code which Volta will use to refractor your code (as a 2nd phase of compilation) into multiple tiers, targeting java script enabled browsers (IE and Fire Fox are supported), and traditional Windows Forms. Volta actually takes the default compiled MSIL, scans for the Volta declarations you added, and generates new MSIL which will run on your various Tiers. I think it sounds like pretty exciting stuff (or I wouldn't have blogged it). However, I have a few questions/comments/thoughts. - Volta is currently a CTP, and is referred to at least once as experimental. I would not want to develop any production code unless Volta was going to be actively developed.
- What options exist for tweaking the generated application, specifically on the client pieces. You always need to tweak something on the generated stuff, and then those tweaks are usually lost when you re-generate.
- How would Volta work with something like CSLA, which I use allot. CSLA provides a mechanism for running as 2 or 3 tier with a configuration file change.
- You can still do more with a WinForms application then a Web Application, even with things like java script and Ajax and Silver Light. Technologies like WPF add another layer of complexity as well. As with other things in the land of programming, it sounds like you would have to give up some performance and features you get with targeting a specific client environment, in exchange for a single code base.
If everyone else is using existing programming methodologies, targeting specific environments, reusing code where possible, they are going to have a better looking and performing application IMO. In the end, the customer doesn't care if you don't have to do as much work, they only care how the application looks and performs. You would have to save enough money by using Volta, that you could pass that savings onto the customer. I'm not sure how you could justify this for internal customers. I have a backlog of technologies to take a look at, so I probably won't get to Volta any time soon. That might be a good thing, as by waiting, I will either give it more time to develop, or get dropped.
I'm using a wiki document library on Share Point as a central repository for design and implementation documentation on a project that I am working on (The selection of the wiki as the documentation repository is the topic for another post). At some point in the future, I know I will be asked to provide a hard copy of my documentation, and I didn't have a really easy way to print the wiki. My first thought, was that there must be a program out there, that lets you specify a set of URL's, and then it will go print them for you. Maybe I wasn't searching on the correct terms, but I couldn't find anything. I remembered there was a print linked documents function in IE, so I went back to investigate that. The print links option, is in the options tab of the IE 7 print dialog box. If selected, it will print all 1st level linked documents of the page you are currently on. So, all you really need to do is create a .html file someplace on your computer, and add URL's to it, open in IE, and print it with the print linked documents option. You can create several different .html files each with a different set of URLs. I wish you could specify to print n levels deep, as that way you could build up a hierarchy of .html files. Perhaps this would be worthy of an IE plug-in? Getting back to the Share Point wiki, I wanted a quick way to get the URLs for the pages I wanted to print. There are the default list views, which list all content in the wiki, which almost work, but there are too many extra hyper links. I decided to create a new view, that only contained the wiki page name (as a hyperlink to the document), as well as various non-linked text fields, like date created, modified and version. I could then copy this to a .html file and print. So why not just print from that view? Well, there are all the other URLs on the page, such as the side bar, and admin menus, that would also get printed. However, if the pages you want to print are displayed in a grouping (and with the way I name and categorize my wiki pages, this is pretty easy), you can just highlight the rows, and choose the print selection option in the print dialog box (and print linked documents), and then you don't even have to maintain a list of .html files. This meets my needs for the foreseeable future. I came up with the following list, which outlines what I'd like to see in a standalone application, or IE plug in. - Site spider to gather URLs (with options to filter specific links using regex)
- Drag Drop URLs to organize them into logical groupings for printing
- Define lists which build form other lists
- More formatting options for printing
- Ability to add meta data to the pages referenced by the URL
- Print a table of all links found in the documents
- Actually this is an option already in IE, but I'd like to see it print a single list, instead of 1 per page, so there are no duplicate URLs. Then add a footnote to each link on the actual document which can be used to reference the table of links.
While searching for information and ideas on configuration stores, I came across an article on better WMI scripting. Defiantly something I will need to review in the future as I get into automated BizTalk management.
It seems as if there is someone at Microsoft, who's sole purpose in life is to figure out how I might want to use something (WCF), and then make sure I can't use it in the way I want to. Today's issue, programmatically configuring a WCF client application. Background I have created a central repository store in the form of a SQL database, that put simply, contains key/value pairs that I can query. All of my applications (or each machine.config) only need to specify a single database connection string to this configuration database. All other configuration settings are retrieved from the database (and cached), making it easy to keep track of configuration, view all current configuration settings, and deploy the applications on new servers. Expanding upon my simple definition, there is a second "key", which corresponds to what environment (production, staging, development) the application is deployed. So my single configuration database controls the configuration of all applications in all environments. For those settings which are the same across all environments, no deployment key is specified. So far this has worked great, and I wanted to add configuration information about a WCF client service. WCF WCF needs two properties set, a binding, and an endpoint, and each of these 2 properties have sub properties which may or may not have values that need to be set. Out of the box, WCF is most easily configured using application or web configuration files. All of the tools support the configuration files and it's easy to see what's going on (well sort of). I basically wanted to take the XML sections that define the binding and endpoint, and place it into my configuration database. For some reason, I got it in my head that this would be really easy to do, but found out that it's not. Searching thru Google yielded no direct solution. While you can programmatically set the Binding and Endpoint properties of a proxy class client object, I would either have to have 1 setting per property (and sub property) in my configuration database, or come up with an XML schema, parse the schema and set the values. This was the path I was pursing, but instead of making my own schema, I just used the XML configuration schema provided out of the box, in hopes that one day I could just pass those sections to binding or endpoint objects and have it parse for me. After creating classes to parse a single binding (WsHttpBinding), I decided there must be a better way. The code to parse the configuration files must exist somewhere in System.ServiceModel, so I decided to go spelunking using reflector to see what was going on, and determine if I could hack something together. Implementation Overview After spending a couple of hours in reflector, I realized the main roadblock was the use of the System.Configuration namespace to handle the configuration data for System.ServiceModel. System.Configuration only supports the file system, and provides no extension methods at this time (something I'd like to see from Microsoft in the future). This left me with one option, which was to create temporary XML file with my configuration data from my database, and then read that configuration file using the proper classes/methods in System.Configuration. It took me awhile to accept this option, as writing a temporary file seemed messy, but then I realized, temporary files are a fact of life in .Net, programming, operating systems, etc. Configuration Store My configuration store was already defined as I mentioned previously. The 2 XML samples below are stored as separate entries in my configuration database. Below is the XML I stored for handling the bindings: <bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_ITwoWayAsyncVoid" closeTimeout="00:01:00"
openTimeout="00:00:30" receiveTimeout="00:02:00" sendTimeout="00:00:15"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text"
textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00"
enabled="false" />
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None"
realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true"
algorithmSuite="Default" establishSecurityContext="true" />
</security>
</binding>
</wsHttpBinding>
</bindings>
I am able to store multiple bindings, and binding configurations in a single entry in my configuration database (see below). In this example, I only have a single binding and binding configuration. I was originally planning to have 1 entry for each binding, but I don't think that will be necessary, if anything I will have a single entry for all necessary bindings per environment, and even that might be overkill.
This is the XML I stored for the end point:
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_ITwoWayAsyncVoid" />
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://tersodemodev1/TsiCbsEsbReciever/WcfService_TersoSolutions_CBS_Fusion_BizTalk_InitialProcess.svc"
binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ITwoWayAsyncVoid"
contract="TsiCbsEsbReceiver.WcfService_TersoSolutions_CBS_Fusion_BizTalk_InitialProcess"
name="WSHttpBinding_ITwoWayAsyncVoid">
<identity>
<userPrincipalName value="TERSODEMODEV1\BizTalkWebServices" />
</identity>
</endpoint>
</client>
Notice the repeat of the <bindings> section. This was required if I wanted to use the the classes in System.ServiceModel for parsing the endpoint, I needed to define a skeleton for the bindings.
I separated the endpoints from the bindings, as that is what is going to change between environments the most for me. The endpoint specifies which server (test, production, staging), and identity (for single server test scenarios, the account will be local, for multi server production enviroments, the identity will be a domain account).
Code
I created 2 classes under a namespace called ConfigSystem. The first class, called ConfigMgr contains code that allows me to go from my configuration database to a System.Configuration.Configuration object by way of a temporary file. The second class called Wcf, contains code specific to my Wcf configuration implementation, and contains code inferred from reflector.
All of the code in ConfigMgr uses public classes and is pretty simple.
public static class ConfigMgr
{
#region MemberVars
private const string configFileHeader = "<?xml version=\"1.0\"?><configuration>";
private const string configFileFooter = "</configuration>";
#endregion
#region Methods - Public
public static ConfigurationSection GetSection(string configKey,
string xmlConfigFileHeaderExtra, string xmlConfigFileFooterExtra, string sectionName)
{
//Get an xml document from the configuration database to store as a tempory file.
System.Xml.XmlDocument doc = PrepareXmlDocument(configKey, xmlConfigFileHeaderExtra, xmlConfigFileFooterExtra);
//Now write out to the temp file.
string path = System.IO.Path.GetTempFileName();
using (System.Xml.XmlWriter writer = System.Xml.XmlWriter.Create(path))
{
doc.WriteTo(writer);
writer.Close();
}
//Setup configuration file map so that we can use standard file based config file
ExeConfigurationFileMap map = new ExeConfigurationFileMap();
map.ExeConfigFilename = path;
System.Configuration.Configuration config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
//Now return the specified section
return config.GetSection(sectionName);
}
private static System.Xml.XmlDocument PrepareXmlDocument(string configKey, string xmlConfigFileHeaderExtra, string xmlConfigFileFooterExtra)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
//Append header
sb.Append(configFileHeader);
sb.Append(xmlConfigFileHeaderExtra);
sb.Append(ConfigHandler.GetValue(configKey));
//Append Footer
sb.Append(xmlConfigFileFooterExtra);
sb.Append(configFileFooter);
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
doc.LoadXml(sb.ToString());
return doc;
}
#endregion
}
The code in the Wcf class uses some methods I copied via reflector, and because I don't know the legal ramifications of that, I am only providing an outline of what I did.
GetBinding:
- Calls ConfigMgr.Get section and returns the <bindings> section. This is cast to a BindingsSection object.
- Code taken from System.ServiceModel.Configuration creates a BindingCollectionElement which is the correct type associated with the binding stored in the XML config.
- A new binding object is created using Activator.CreateInstance(BindingCollectionElement.BindingType)
- Additional code taken from System.ServiceModel.Configuration loops thru BindingCollectionElement.ConfiguredBindings and initializes the binding object previously created. A check is made to make sure multiple bindings of the same type are not created.
- Finally, the binding object is returned to the caller.
GetChannelEndpointElement: This class uses only public classes and methods so I am including it. EnpointElement contains the configuration data for an endpoint, including the URI, identity, binding, and binding configuration. The binding and binding configuration data is actually passed to my GetBinding method to return the correct binding with the correct binding configuration.
public static ChannelEndpointElement GetChannelEndpointElement(string configKey)
{
ClientSection section = (ClientSection)ConfigMgr.GetSection(configKey, system_ServiceModelAsOpenXml, system_ServiceModelAsCloseXml, clientSection);
if (section == null)
throw new ArgumentException(string.Format("Client section returned from config db for key {0} was null", configKey));
if (section.Endpoints.Count != 1)
throw new ArgumentException(string.Format("There must be exactly 1 endpoint returned from the configuration. {0} endpoints were returned for key {1}", section.Endpoints.Count, configKey));
return section.Endpoints[0];
}
GetEndpointAddress: Another method I created which almost uses all public methods. It takes a ChannelEnpointElement and returns a EndpointAddress which can be assigned to a wcf proxy class.
public static EndpointAddress GetEndpointAddress(ChannelEndpointElement element)
{
//Create a new builder, as the endpoint address is an immutable class
EndpointAddressBuilder builder = new EndpointAddressBuilder();
builder.Identity = LoadIdentity(element.Identity);
builder.Uri = element.Address;
return builder.ToEndpointAddress();
}
LoadIdentity: This final method was taken from System.ServiceModel.Description.Configloader. This method takes a IdentityElement (a property of ChannelEnpointElement) and returns a EndpointIdentity which is passed to the EndpointAddressBuilder in my GetEndpointAddress method. The method basically checks the the existence of certain properties, and if one exists, it returns the appropriate identity using public factory methods.
- EndpointIdentity.CreateUpnIdentity
- EndpointIdentity.CreateSpnIdentity
- EndpointIdentity.CreateDnsIdentity
- EndpointIdentity.CreateRsaIdentity
- EndpointIdentity.CreateX509CertificateIdentity
There is a identity type of certificate reference which I was unable to add support for, because it referenced additional internal classes which I didn't want to duplicate at this time since I had no need for that identity type.
Issues
Since this is completely unsupported by Microsoft, and uses code that was never meant for external use, in a future version of the framework, something could break. I'm also not an legal expert when it comes with what someone can do with MSIL code discovered thru reflector, so for now, the actual code I wrote is not available publicly. Hopefully I provided enough information to allow someone to reproduce my steps and implement something on their own.
Another somewhat disappointing realization I came to, was that this might make it hard to support write operations to my configuration database. Currently the configuration database is managed thru scripts and direct editing. If write support to the config database became a requirement, I'd probably try to do the reverse and use the built in file save functionality in System.Configuration to save a temporary xml file, read it and then store it into my config database.
Conclusion
This was my first attempt at doing something like this, digging into framework code to see how it really works and trying to come up with a solution. I'm going to go forward using the code I wrote outlined in this post, as I have full read support for WCF XML configuration. The amount of code I had to write and test is significantly less then if I would have wanted to get the same amount of functionality (configuration support for all bindings and endpoint configurations).
|