ServicesResourcesConferencesOur TeamWeblogsAboutContact
   
Dynamic Data: Partial/Scaffolding Class Generator

If you've worked with ASP.NET Dynamic Data before, you know that you can customize the scaffoling by creating a partical class for the generated Entity Framework/Linq-to-SQL classes. In this partial class, you can specify another class as the metadata class, and in the metadata class, you can create public fields with the same name as the public properties in your data-access classes and use the set of attributes in System.ComponentModel.DataAnnotations and System.ComponentModel to customize the scaffolding.

There's, of course, no intellisense ... you manually have to get all the field names right. Given that there are no "real" dependencies, there won't be any Exceptions or compile-time errors, either.

For me, this gets pretty old pretty quickly. I've written a small code generator which helps you get the initial scaffolding classes without writing them manually. It does this by loading an assembly, looking for all Entity Framework and Linq-to-SQL classes and generating the partial classes and metadata classes as a starting point for your customization.

The code is so little that I hardly dare to post it --- but I figured that it might help one or another developer. It should work for Entity Framework and Linq-to-SQL. (Note: you can also download the complete project and a ready-to-run binary at the end of this post).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data.Objects.DataClasses;
using System.Data.Linq.Mapping;

namespace DynamicDataCodeGenerator
{
  public class MetadataGenerator
  {
    public static string GenerateMetadata(string assemblyName)
    {
      StringBuilder bld = new StringBuilder();

      Assembly asm = Assembly.LoadFrom(assemblyName);

      foreach (Type type in asm.GetTypes())
      {
        EdmEntityTypeAttribute[] atts = 
(EdmEntityTypeAttribute[])type.GetCustomAttributes(
typeof(EdmEntityTypeAttribute), true);
if (atts.Length== 1) { AppendCodeForType(bld, type); } TableAttribute[] tableAtts =
(TableAttribute[])type.GetCustomAttributes(
typeof(TableAttribute), true);
if (tableAtts.Length == 1) { AppendCodeForType(bld, type); } } return bld.ToString(); } private static void AppendCodeForType(StringBuilder bld, Type type) { bld.Append("[MetadataType(typeof(Metadata))]").AppendLine(); bld.Append("public partial class ").Append(type.Name).AppendLine(); bld.Append("{").AppendLine(); bld.Append(" [ScaffoldTable(true)]").AppendLine(); bld.Append(" public class Metadata").AppendLine(); bld.Append(" {").AppendLine(); foreach (PropertyInfo prop in type.GetProperties()) { bld.Append(" [ScaffoldColumn(true)]").AppendLine(); bld.Append(" public object ").Append(prop.Name).AppendLine(";"); bld.AppendLine(); } bld.Append(" }").AppendLine(); bld.Append("}").AppendLine(); bld.AppendLine(); bld.AppendLine(); } } }


Download: DynamicDataCodeGenerator.zip (51KB)

posted Monday, March 09, 2009 8:03 AM with 3 Comments

Multiple Form Configurations with ASP.NET Dynamic Data

As some of you know, I enjoy working at high-throughput web applications. One thing I however don't particularily like, is the creation of the necessary backend administration tools. In a lot of the applications I've worked on, data is fed using various XML-ish or proprietary data feeds and is usually 99.9% correct. For the remaining 0.1%, support personell needs some possbility to maintain and change data in every way imaginable. This usually means that it's necessary to create CRUD-style data maintainance applications for dozens or - more likely - hundreds of tables.

As this doesn't really sound like a lot of fun, I had high hopes that ASP.NET Dynamic Data would help our clients a bit in this regard. And, yes, it does! Now, to be fair: it's Version One and lacks a few features which would be nice. But the cool thing is that it's extremely extensible.

As those of you who've looked at dynamic data before will know, ADD by default bases its view of the world on annotated partial classes based on code generated from an Entity Framework EDMX or on Linq-To-SQL classes. This of course also means that, by default, you will get exactly one representation for each class (one set of configured columns). There is no built-in (automatic) way to simply configure different views, including different columns in different orders for each table. For most of the applications my clients work with, this is unfortunately not enough, as their users usually want to show/hide different columns based on the particular use case.

What I'd like to do instead was to define multiple views (say, in an XML file) and define key for each view which can then be used to retrieve the desired layout. (Note: the following sample is based on an Entity Framework Dynamic Data Web Site based on the Northwind database ... But I'm sure that you can follow even without this setup):

The first thing was to define the different columns I'd like to show in the different contexts. To do this, I've created a file called ColumnConfiguration.config in the Web's root directory:

<ColumnConfigurations>
  <Configuration Table="Customers" Name="Phonelist" Columns="CompanyName, ContactName, ContactTitle, Phone, Country" />
  <Configuration Table="Customers" Name="Complete" Columns="*" />
  <Configuration Table="Products" Name="Shortlist" Columns="ProductName, UnitsInStock, QuantityPerUnit" />
</ColumnConfigurations>

I've then created a custom field generator (loosely based on the one which has been published last year with the ASP.NET dynamic data futures ... from which I also took the ColumnOrder-Attribute which I include for completeness' sake).

 public class ConfigurationBasedFieldGenerator : IAutoFieldGenerator
{
  private MetaTable _table;
  private bool _multiItemMode;
  private string _configurationName;

  public static Dictionary> _configurations = new Dictionary>();
  public static DateTime _nextRefresh = DateTime.MinValue;

  private void EnsureConfigurations()
  {
      lock (_configurations)
      {
        if (_nextRefresh > newConfigs = new Dictionary>();

          using (XmlReader rdr = new XmlTextReader(HttpContext.Current.Server.MapPath("~/ColumnConfiguration.config")))
          {
            rdr.Read();
            rdr.ReadToDescendant("Configuration");

            while (rdr.Name == "Configuration")
            {
              string name = rdr.GetAttribute("Name");
              string table = rdr.GetAttribute("Table");
              string columns = rdr.GetAttribute("Columns");
              List tmp = new List(columns.Split(','));
              for (int i = 0; i ().DefaultIfEmpty(ColumnOrderAttribute.Default).First();
  }

  public ICollection GenerateFields(Control control)
  {
    EnsureConfigurations();

    bool isWildcardConfig= true;

    string key = _table + "|" + _configurationName;

    List columnsToInclude=null;

    bool hasKey = _configurations.TryGetValue(key, out columnsToInclude);
    if (hasKey)
    {
      if (columnsToInclude.Count==0 || columnsToInclude[0] != "*")
      {
        isWildcardConfig = false;
      }
    }


    if (isWildcardConfig)
    {
      // use standard config, ordered by ColumnOrderAttribute - borrowed from an older version 
      // of the ASP.NET futures
      var fields = from column in _table.Columns
                   where IncludeField(column)
                   orderby ColumnOrdering(column)
                   select new DynamicField()
                   {
                     DataField = column.Name,
                     HeaderText = column.DisplayName
                   };

      return fields.ToList();
    }
    else
    {
      var allFields = from column in _table.Columns
                   where IncludeField(column)
                   select new DynamicField()
                   {
                     DataField = column.Name,
                     HeaderText = column.DisplayName
                   };

      List fields = new List();

      foreach (string col in columnsToInclude)
      {
        DynamicField field = allFields.FirstOrDefault(p=>p.DataField == col);
        if (field != null) fields.Add(field);
      }
      return fields;
    }
  }
}

// borrowed from an older version of the asp.net futures
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
public class ColumnOrderAttribute : Attribute, IComparable
{

  public static ColumnOrderAttribute Default = new ColumnOrderAttribute(0);

  public ColumnOrderAttribute(int order)
  {
    Order = order;
  }

  /// 
  /// The ordering of a column. Can be negative.
  /// 
  public int Order { get; private set; }

  public int CompareTo(object obj)
  {
    return Order - ((ColumnOrderAttribute)obj).Order;
  }
}

To enable this custom field generator, I've changed the default List.aspx (and the other view as well) to explicitly use it:

protected void Page_Init(object sender, EventArgs e)
{
  table = GridDataSource.GetTable();
  string configurationName = Request.QueryString["config"];
  DynamicDataManager1.RegisterControl(GridView1, true /*setSelectionFromUrl*/);
  GridView1.ColumnsGenerator = 
       new ConfigurationBasedFieldGenerator(table, configurationName, true);
}

Et voilà. If I browse to http://localhost/Customers/List.aspx, I get the full list (as configured in the metadata), but if I browse to http://localhost/Customers/List.aspx?config=Phonelist, I only get the fields which have been configured in ColumnConfiguration.config (CompanyName, ContactName, ContactTitle, Phone, Country .... exactly in this sequence!). I could of course also define more than one additional view just by adding the entries to the configuration file.

And to take this one step further, you could imagine to also add attributes like "RoleName" to the configuration file to automatically check the user's role membership to ensure that only users with the correct access rights can view certain combinations of columns.

posted Sunday, March 08, 2009 7:27 PM with 4 Comments

Azure and Mesh - Delegated Authentication via HTTP/REST

At next week's TechDays conference in Antwerp/Belgium, I wanted to show an example of how an Azure worker role can update data in a user's Live Mesh Desktop. Instead of doing it in the common "demo" way of simply supplying a hardcoded username/password combination (or, in a similar vain, by simple storing the user's Mesh username/password in a database), I wanted to make this as real as possible.

Reality of course means delegated authentication. Now, there are a few things harder than getting this to work for the first time, but I'd be hard pressed to name a lot of them. To make a long story short, it seems that, currently, delegated authentication is only possible for Mesh Applications (web sites which run directly inside the users mesh ... think "Facebook App" for Mash). But that's not what I wanted ... I wanted to asynchronously, update data in the Mesh at a time when the user is not online.

Turns out that this is absolutely possible: you can simply act as if your external web site is a mesh application by registering for a "new mesh application" (in the azure portal) and sending the given AppID to the Live's delegation authentication URL when your external web site needs the user's consent to interact with Mesh data. This will however - behind the scenes - also cause your application to be installed in the user's Mesh Desktop so that I ended up with an "Empty" application on my desk. But that's something I can absolutely live with.

What I can't live with is something I noticed a few hours later. I planned to deploy the part of my application which uploads a file to the user's Mesh into Azure. For my first tests, I've simply used a console application which references Microsoft.LiveFX.Client, Microsoft.LiveFX.ResourceModel and Microsoft.Web from the Live Framework SDK. I've created a folder called "DemoFolder" in my Mesh desktop and ran code similar to the following (which worked perfectly well in my console application):

static void UploadWithAPI()
{
  string token = "... delegation token I've received from Live ....";

  LiveOperatingEnvironment loe = new LiveOperatingEnvironment();

  Uri serviceUrl = new Uri("https://user-ctp.windows.net/V0.1");

  Console.WriteLine("Connecting");
  loe.Connect(token, AuthenticationTokenType.DelegatedAuthToken, 
              serviceUrl, new LiveItemAccessOptions(true));

  Console.WriteLine("Connected");

  var demoFolder = 
     loe.Mesh.MeshObjects.Entries.First(p => p.Resource.Title == "DemoFolder");

  string path = @"C:\images\";
  string filename = "IMG_3957.jpg";

  var feed = demoFolder.DataFeeds.Entries.First(p => true);
  using (FileStream fs = File.OpenRead(path + filename))
  {
    feed.DataEntries.Add(fs, filename);
  }
  Console.WriteLine("File added");
}

Nice and short. Even very understandable for a non-REST person like myself. Only problem: It won't work in Azure. You'll get a SecurityException telling you that the assembly does not allow partically trusted callers. I think that this is simply an oversight (to forget APTCA) or at least I don't fully understand the rationale. But the end-result is absolutely the same: I can't run this code in Azure.

Fortunately enough, Live Mesh's base API is actually HTTP/REST-based and the .NET API is just a small wrapper on top of it. So now I just needed to find out how to pass my delegated authentication token to the Live Mesh service. I've googled quite a bit (and used Fiddler ... thank god for SSL interception!) before I found that the missing HTTP header was called AppDelegationToken. Now I could remove the reference to the client DLLs and go about my business in a very low-level way:

static void UploadWithREST()
{
  string token = "... delegation token I've received from Live ....";
  string path = @"C:\images\";
  string filename = "IMG_3957.jpg";
  string serviceUrl = "https://user-ctp.windows.net/V0.1/";
  string folderName = "DemoFolder";

  string relativeUrl = GetRelativeUrlForFolder(token, serviceUrl, folderName);
  PostToLive(serviceUrl + relativeUrl, token, path, filename, "image/jpeg");
}

// return the URL for a folder's MediaResources based on the folder's name
private static string GetRelativeUrlForFolder(string token, string baseUrl, string folderName)
{
  string relativeUrl = "Mesh/MeshObjects?$filter=(Title%20eq%20'" + folderName + "')&$expand=DataFeeds";
  XmlDocument doc = GetLiveResponse(baseUrl + relativeUrl, token);
  XmlNamespaceManager mgr = new XmlNamespaceManager(doc.NameTable);
  mgr.AddNamespace("atom", "http://www.w3.org/2005/Atom");
  mgr.AddNamespace("win", "http://user.windows.net");

  XmlNode nod = doc.SelectSingleNode("/atom:feed/atom:entry/atom:link[@rel='LiveFX/DataFeeds']" + 
"/win:Inline/atom:feed/atom:entry/atom:link[@rel=" +
"'LiveFX/MediaResources']", mgr);
relativeUrl = nod.Attributes["href"].Value; return relativeUrl; } // execute a simple GET request and return the XmlDocument static XmlDocument GetLiveResponse(string url, string token) { HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url); req.Headers.Add("AppDelegationToken", token); req.Method = "GET"; XmlDocument doc = new XmlDocument(); using (HttpWebResponse res = (HttpWebResponse) req.GetResponse()) { using (Stream st = res.GetResponseStream()) { doc.Load(st); } } return doc; } // POST a file to the Live Mesh static void PostToLive(string url, string token, string path, string filename, string contentType) { HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url); req.Headers.Add("AppDelegationToken", token); req.ContentType = "image/jpeg"; req.Headers.Add("Slug", filename); req.Method = "POST"; using (Stream st = req.GetRequestStream()) { byte[] buf = File.ReadAllBytes(path + filename); st.Write(buf, 0, buf.Length); } using (HttpWebResponse resp = (HttpWebResponse)req.GetResponse()) { // not really needed ... } }

I have to admit that I didn't yet try this in Azure, but I think that it should work ...

posted Friday, March 06, 2009 9:54 AM with 1 Comments

Returning to the cutting edge ..

Is this thing still on? Is anyone still subscribed after all these years?

After taking a two-years-and-a-bit hiatus from the cutting edge of technology, I'm now about to return full time. But before we're back to the regular program, let me answer the question inevitable question: what was up with you??

What happend was that I've had the pleasure to find a few really amazing clients and decided to work with them on some of their medium/longer-term projects (longer term meaning substantially more than the usual three-day-visit to a client to help them with a particular issue or question they might have). I really like to do this every couple of years to get a more "real" feeling about the day-to-day pain points in software development projects. So instead of doing only the regular short-term consulting (which of course still was going on as well), I went back to the trenches and cranked out some real, production code in some real, production projects for a change. It was fun. Really!

On the research side, I've concentrated not so much on tomorrow's technologies, but more on currently shipping stuff which could be used in these projects. I've also worked a *lot* with WinDbg (and its possibilities constantly amaze me ... I still think that every serious development team should have at least one WinDbg expert). With this tool, it's just *so* much easier to find problems in production code which happen only on production machines far away from Visual Studio or any other debugger.

During the second half of 2008 I started to feel the urge to return back to the cutting edge (and the other thinktecture guys were already making some serious fun of me and my dedication to already-shipping ASP.NET stuff). But I didn't find the compelling topics to look into. WPF would be nice, but frankly, I'm more of a server-side person. WF? Yes, sure. Doing this on a regular basis. WCF? Just a tool for me, no religious feelings about it.

But then along came October. And as it was the case for a lot of developers, last year's PDC gave me the opportunity to see some really cool upcoming stuff. (Publicly and even more so in private meetings). I've then started to scale back the longer-term committments to return to researching the cutting edge. In 2009, I'll return to the conference track with these topics, plan to write more blog posts (well ... it would be hard to write less than what I did, anyway) and I'm actually also currently discussing an idea for a new book. I think there's some fun time ahead.

posted Friday, March 06, 2009 9:27 AM with 6 Comments

Slides, Demos and Notes for TechDays Ghent and DevWeek London
Thanks for attending one of my sessions at the recent TechDays in Ghent or at DevWeek in London. As announced during the sessions, I have uploaded all materials and you can access them at the following locations:
posted Sunday, March 16, 2008 7:10 PM with 3 Comments

Slides and Demos for Prio Conference
I've just uploaded the slides and demos for my debugging talks at Prio Conference. (Most of the materials will be in German).

Download
posted Thursday, November 15, 2007 3:33 PM with 1 Comments

Notes for my TechEd Europe Talk - Hardcore Production Debugging
As announced during my talk, I've uploaded my notes and samples for the session on production debugging. You can download them here:
Thanks for being there! I hope that you've enjoyed the session.
posted Saturday, November 10, 2007 9:32 AM with 7 Comments

Dominick Baier joins thinktecture
Well ... it's been inofficially known for quite a while, but today I'm proud to finally publicly annouce that .NET security wizard Dominick Baier has joined thinktecture as a consultant. I think that this is especially great as his in-depth security knowledge and experience (before going to .NET he used to work as a network penetration tester and a ISO certified security auditor) is really adding a lot of value to our combined knowledge. (And most importantly ... he's just a great guy to work with ...)

Dominick is also the one wrote one of the only .NET books I've read cover-to-cover in the recent months because it contained so much valueable content: Developing More Secure ASP.NET Applications.
posted Monday, February 12, 2007 4:54 PM with 1 Comments

thinktecture @ TechEd Europe Developers
It's that time of the year again and in a few days, the European thinktects will get together at Microsoft's biggest gathering of developers in Europe. This time, we have a pretty intense schedule:

Monday
PRE008, 12:00 - 19:00, Ingo Rammer and Christian Weyer, Windows Workflow Foundation (WF) - Free Workflow Support for your Applications

Wednesday
DEV001, 13:30 - 14:45, Neno Loje, DEMO: Adopting ClickOnce for Real World Applications

Thursday
DEVWD15, 09:00 - 10:15, Ingo Rammer, Hardcore .NET Production Debugging
DEVWD17, 09:00 - 10:15, Neno Loje, Team System Adoption Best Practices
DEVWD15, 13:30 - 14:45, Ingo Rammer, Hardcore .NET Production Debugging (repeat)
DEVWD17, 15:45 - 17:00, Neno Loje, Team System Adoption Best Practices (repeat)
ARC309, 17:30 - 18:45, Dominick Baier, Security is a Feature - Best Practices for Designing Secure Distributed .NET Applications

Friday
DEV403, 09:00 - 10:15, Ingo Rammer, Optimizing Performance and Scalability of Distributed .NET Applications
DEV004, 13:30 - 14:45, Christian Weyer, DEMO: Technology in Action! Building a Distributed Solution with .NET Framework 3.0

I'm really looking forward to meeting you in Barcelona!

Update: Slides, samples and notes are online at http://www.thinktecture.com/Conferences/slides/TechEd2006/default.html.
posted Friday, November 03, 2006 1:08 PM with 6 Comments

Slides and Demos for the MSDN Event in Brussels
As announced during the event, I have uploaded my slides and demos from yesterday's MSDN Event in Brussels to http://www.thinktecture.com/Conferences/slides/MsdnBrussels. Enjoy!
posted Tuesday, September 26, 2006 8:37 AM with 5 Comments

Off to Basta!2006
In a few hours I'm off to Basta!2006. I'm really looking forward as this year thinktecture has a massive presence - even running in some parallel slots - at this conference. It's also the first time that the four of us in Europe meet in the current line-up as Neno Loje joined a few weeks ago to add his great VS Team System experience to our team.
  • Sept 18: Neno Loje - Full Day Tutorial on Team Development with Visual Studio 2005 Team System
  • Sept 19
    • 10:15-11:30 - Neno Loje - Visual Studio 2005 Team System
    • 10:15-11:30 - Ingo Rammer - Hardcore Production Debugging
    • 14:15-15:30 - Christian Nagel - C++/CLI
    • 17:15-18:35 - Christian Nagel - System.Transactions
  • Sept 20
    • 10:15-11:30 - Christian Weyer - Windows Communication Foundation: Think First, Speak Later
    • 11:45-13:00 - Christian Weyer - Windows Workflow Foundation: Because Workflow is Everywhere
    • 11:45-13:00 - Ingo Rammer - The Smart Client Software Factory
  • Sept 21: Christian Weyer, Ingo Rammer - Full Day Tutorial on Windows Workflow Foundation
It's going to be fun - see you in a few hours.
posted Monday, September 18, 2006 2:07 PM with 2 Comments

Defining KnownTypes in a configuration file

WCF's serialization of data contracts supports the transmission of derived types which have not been available when the original application (or just contract) has been built. In the following example, ApplicationExtensionObject is just a placeholder instead of which a derived class should be transferred at runtime.

[DataContract]
public class Customer
{
  [DataMember]
  public string Firstname;
  [DataMember]
  public string Lastname;
  [DataMember]
  public Address DefaultDeliveryAddress;
  [DataMember]
  public Address DefaultBillingAddress;

  [DataMember]
  public ApplicationExtensionObject Extension;
}

[DataContract]
public class ApplicationExtensionObject
{
}

You would normally accomplish this using the [KnownType] attribute on the field Extension. But what if you can't do this as the type is not yet known at compile time? Well, for one thing you could use [KnownType(methodName)] to define a method which returns an array of Types at runtime. Another alternative is to use the section to define the mapping between declared types and known subtypes.

In the following snippet, the class ProjectSpecificExtension.CustomerExtension is defined as a valid known subtype of ApplicationExtensionObject for the serializer.

<configuration >
  <system.runtime.serialization>
    <dataContractSerializer>
      <declaredTypes>
        <add type="Shared.ApplicationExtensionObject, Shared">
          <knownType type="ProjectSpecificExtension.CustomerExtension, ProjectSpecificExtension" />
        </add>
      </declaredTypes>
    </dataContractSerializer>
  </system.runtime.serialization>
  ...
</configuration>
Update: This has actually been documented and is a supported practice. I guess I just made it too much of a habit to look into Reflector instead of searching long enough in the docs ;-)
posted Wednesday, September 06, 2006 7:03 PM with 3 Comments

Start ServiceHosts for all configured Services
Important Update: The originally presented code only works if the service is defined in the same assembly which hosts the service (because the name="" attribute in <service> may not contain the assembly name of the service). See at the end of the article for a slightly different version which works in all cases --- but which involves adding a second config file.

As WCF has reached RC1 stage, I find myself cleaning up a few bits of older WCF code. While playing around with it, I always found myself having to start more and more ServiceHosts for different configurations. The following snippet iterates over all <service> entries in configuration/system.serviceModel and opens a ServiceHost for each of them.

Update: Added code to close all services.

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Configuration;
using System.ServiceModel.Configuration;
using System.ServiceModel;

public class ServiceHostGroup
{
    static List<ServiceHost> _hosts = new List<ServiceHost>();

    private static void OpenHost(Type t)
    {
        ServiceHost hst = new ServiceHost(t);
        hst.Open();
        _hosts.Add(hst);
    }

    public static void StartAllConfiguredServices()
    {
        Configuration conf =
          ConfigurationManager.OpenExeConfiguration(Assembly.GetEntryAssembly().Location);

        ServiceModelSectionGroup svcmod =
          (ServiceModelSectionGroup)conf.GetSectionGroup("system.serviceModel");
        foreach (ServiceElement el in svcmod.Services.Services)
        {
            Type svcType = Type.GetType(el.Name);
            if (svcType == null)
              throw new Exception("Invalid Service Type " + el.Name + " in configuration file.");
            OpenHost(svcType);
        }

    }


    public static void CloseAllServices()
    {
        foreach (ServiceHost hst in _hosts)
        {
            hst.Close();
        }
    }
}

As mentioned above, I have in the meantime learned (after long and hard fighting against the "this service has no non-metadata endpoints"-exception), that the name="" attribute in <service> must not contain an assembly name. I guess I have been too used to Remoting configuration files after all ;-). To still get dynamic service configuration without code changes, I've added a second configuration file called services.xml and changed the code for ServiceHostGroup to the following:

Services.XML:

<configuredServices>
  <service type="ServiceImplementation.ArticleService, ServiceImplementation" />
</configuredServices>


ServiceHostBase.cs:

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Configuration;
using System.ServiceModel.Configuration;
using System.ServiceModel;
using System.Xml;
using System.Xml.Serialization;
using System.IO;

public class ServiceHostGroup
{
  static List<ServiceHost> _hosts = new List<ServiceHost>();

  private static void OpenHost(Type t)
  {
    ServiceHost hst = new ServiceHost(t);
    Type ty = hst.Description.ServiceType;
    hst.Open();
    _hosts.Add(hst);
  }

  public static void StartAllConfiguredServices()
  {
    ConfiguredServices services = ConfiguredServices.LoadFromFile("services.xml");

    foreach (ConfiguredService svc in services.Services)
    {
      Type svcType = Type.GetType(svc.Type);
      if (svcType == null) throw new Exception("Invalid Service Type " + svc.Type + " in configuration file.");
      OpenHost(svcType);
    }
  }

  public static void CloseAllServices()
  {
    foreach (ServiceHost hst in _hosts)
    {
      hst.Close();
    }
  }
}

[XmlRoot("configuredServices")]
public class ConfiguredServices
{
  public static ConfiguredServices LoadFromFile(string filename)
  {
    if (!File.Exists(filename)) return new ConfiguredServices();

    XmlSerializer ser = new XmlSerializer(typeof(ConfiguredServices));
    using (FileStream fs = File.OpenRead(filename))
    {
      return (ConfiguredServices) ser.Deserialize(fs);
    }
  }

  [XmlElement("service", typeof(ConfiguredService))]
  public List<ConfiguredService> Services = new List<ConfiguredService>();
}

public class ConfiguredService
{
  [XmlAttribute("type")]
  public string Type;
}

posted Tuesday, September 05, 2006 2:47 PM with 6 Comments

WCF server startup slow on W2K3 in VM
Just a quick and unrelated tip: If you - like I'm doing it right now - run a WCF server in a Virtual Machine running W2K3 Server and your VM is supposed to be standalone and not part of domain you might experience a delay when a WCF server application is starting. When looking at the issue with Wireshark/Ethereal (yes, I do what I preach and a Network Sniffer really is one of the first things I use when I experience some kind of unexpected delay ;-)) I noticed that the machine is broadcasting eight Netbios name service queries on its internal subnet to find a domain controller. These queries time out after about eight seconds, but I really didn't want to wait that long.

I'm sure that there are dozens of ways around it, but I simply converted my standalone W2K3 VM to a standalone Domain Controller. Startup time decreased from 8 seconds to less than one second for each time I hit F5 in Visual Studio.
posted Monday, September 04, 2006 9:20 PM with 2 Comments

Coming to Brussels on September 25

As David has already blogged, I will be back in Brussels on September 25. I think that's great as I really like the city and wasn't able to make it to the last DevDays there.

Microsoft is hosting (for free!) a full day on .NET Framework 3.0 in which Peter Himschoot will present on Windows Presentation Foundation and CardSpace, and I'll focus on Windows Communication Foundation and Workflow Foundation. It's going to be fun. (And: You can still vote for the session contents!)

posted Tuesday, August 15, 2006 6:58 AM with 0 Comments


Powered by Community Server, by Telligent Systems