DCSIMG
March 2008 - Posts - public: class Alon : IArchitect, IAzure, ICPP, ISmartHome, IHomeServer

Syndication

March 2008 - Posts

Technorati Tags: ,,

Sasha has notify me about a post of Oren Eini about internal stripping. I have a deep respect to Oren, but I don't agree with his thought about the subject. Developers today are talking a lot about SOA and other "new" ideas, but forget sometimes the good old ideas of Object Oriented Programming. This time we are talking about encapsulation and decoupling.  Here is my point of view: 

  1. Using private or internal types/members is something that should be avoid in most of the cases. There are many reasons that a developer doesn't expose an internal implementation to the public. Just to give a few good reasons:
  2. The object has to keep its own invariants. It can be a nightmare finding who changes or uses in a wrong way an internal part of a module/class.
  3. The internal method has a certain "safe" ways to be used, and internally the developer knows how to use it, but a very bad side effect can be happen in a wrong usage (wrong protocol)
  4. It's not an easy task to make something public, to make extension points (protected) and so on. You have to think a lot and you have to test it. Making it internal at first place tells that it is an implementation code for the internal dev team.
  5. Coupling, Coupling, Coupling. A good software engineering is to reduce coupling by exposing only few interfaces. Making everything public will not help.
  6. Maintenance and upgrading, do you want each time a hot fix, service pack or new version of .NET/Windows is installed to go over your million lines of code software and fix all your "internal" usages. O.K maybe not everything has changed, but you have to test your software again anyway.
  7. You will not get any breaking change document about internal code, and you may not get Microsoft support if you use internal mechanism.

I think that you have got the point. I know that sometimes you will not agree with the internal/private keyword. But this is the privileges of the developer; he does not have to give you everything. And if you don’t get enough in the interface (public) don't use it at all.

There are times that you may use internals API. For example sometime I use ntdll functions even though there are not documented APIs. I even implemented many FXCop custom rules when I knew that the SDK will be changed. But I am doing it with a known risk. I document it; I try to find a better way and if there is no one I take my chances. But at least I know the risks.

Posted by Alon Fliess | 5 comment(s)
תגים:,

 

DEV337 - Back to the Future

From Object RPC (COM+) to Services (WCF)

In the beginning, Microsoft created MTS and COM. And Microsoft said "Let there be COM+" and there was COM+. And Microsoft saw that COM+ was good. Then Microsoft said, "Let companies build securable & scalable enterprise applications." And it was so. 
In the second creation, Microsoft created .NET. This created a better world, easier and simpler. And Microsoft saw that .NET was good. Than Microsoft created System.EnterpriseServices as a bridge to the first creation.
Then Microsoft said, "Let us make WCF, in our likeness, and let it rule over all communication mechanisms and become the foundation for enterprise applications". And Microsoft saw that it was good, or is it?

Click here for a short video introduction

Technorati Tags: ,,,,

Today, I had to show how to host many services in one place. Since I did not find many example on the Web, I decided to do share it with you (with the permission of my client...)

Visual Studio lets you easily create a WCF Library, VS 2008 also offers a default host (which after, you think it is a cool feature, you understand that it is annoying and you delete the project type GUID from the proj file)

When VS 2008 WCF Library application startup template creates the default service1 service, it also creates a default app.config file with all the needed configuration to host the service. When you crate one server (EXE or NT service) that should host many different services (for example a WCF plug-in) you start copying and merging all those config files to one. Manu has wrote a post about splitting the configuration file by sections. I'd like to show you how you can take those app.config file from all the services and without merging them to one, you can still host the services they configure in one place. Another side benefit is that by hosting each of the service assembly in a private isolated app domain you can also close and unload a specific service host.

The attached code is for example purposes; there is no one line of validation & error handling, but it can be a good starting point for more robust solution.

The concept:

In the attached VS 2008 solution, there are three services in two assemblies with two app.config. There is a post build action that copies the assembly file and the app.config to one directory (services). This is not a must, but it is an easy solution for discovering service libraries.

To let WCF configuration mechanism to have many app.config file, we create and load each app.config configure services in new WCF Host in a new AppDomain. To do that we need an object that will do the AppDomain creation, the services discovery and the WCF host creation. This is the task of the IsolatedServiceHost class that resides in its own assembly. We load this assembly in two places, in the main AppDomain and in a new WCF Host AppDomain. IsolatedServiceHost has a static function HostServices() that take a path to a WCF Service library. The app.config file must be in the same directory with the name of the XXX.dll.config

In the example all the assembly files and the config file are in the same directory.

The HostServices() function, creates the AppDomain with a new configuration file, by doing it the WCF configuration mechanism "knows" to configure the relevant assembly.

    public interface IIsolatedServiceHost
    {
        void Init(string assemblyFilePath);
        void Start();
        void Shutdown();
    }

    public class IsolatedServiceHost : MarshalByRefObject, IIsolatedServiceHost
    {
        IList<Type> _services;
        List<ServiceHost> _hosts;

        public static IIsolatedServiceHost HostServices(string assemblyFilePath)
        {
            AppDomainSetup setup = new AppDomainSetup();
            setup.ApplicationBase = Path.GetDirectoryName(assemblyFilePath);
            setup.ApplicationName = Path.GetFileNameWithoutExtension(assemblyFilePath);
            setup.ConfigurationFile = assemblyFilePath + ".config";

            EnsureServiceHostHelpperAssemblyExists(assemblyFilePath);

            AppDomain hostAppDomain = AppDomain.CreateDomain(setup.ApplicationName, AppDomain.CurrentDomain.Evidence, setup);
            IIsolatedServiceHost host = hostAppDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, "ServiceHostHelpper.IsolatedServiceHost") as IIsolatedServiceHost;
            host.Init(assemblyFilePath);

            return host;
        }
 

After creating the AppDomain, we create the IsolatedServiceHost instance in the context of the new AppDomain and we get a transparent proxy to it. There is one Quick & Dirty thing that I have done to solve a problem that can be solved in much more elegant way. I copy the ServiceHostHelpperAssembly assembly to the new ApplicationBase directory to let the CLR loader to find it. This is the purpose of the following code:
        private static void EnsureServiceHostHelpperAssemblyExists(string assemblyFilePath)
        {
            try
            {
                File.Copy(Assembly.GetExecutingAssembly().Location, Path.Combine(Path.GetDirectoryName(assemblyFilePath), Path.GetFileName(Assembly.GetExecutingAssembly().Location)), true);
            }
            catch
            {
            }
        }
 

Now we call the Init() method that does the service discovery by examine the WCF configuration and taking the service name as the service instance type. Another approach is to find all the types in the assembly that have the ServiceBehavior attribute. Since this attribute is not a must I preferred the first way.

        
        public void Init(string assemblyFilePath)
        {
            _services = new List<Type>();
            Assembly serviceAssembly = Assembly.LoadFrom(assemblyFilePath);

            System.Configuration.Configuration config =
              System.Configuration.ConfigurationManager.OpenExeConfiguration(System.Configuration.ConfigurationUserLevel.None);

            ServiceModelSectionGroup serviceModelSectionGroup = config.GetSectionGroup("system.serviceModel") as ServiceModelSectionGroup;
            
            foreach (ServiceElement serviceElement in serviceModelSectionGroup.Services.Services)
            {
                _services.Add(serviceAssembly.GetType(serviceElement.Name));
            }
        }
       

Now, we just need to start hosting the discovered service. This is the task of the Start() method:

 public void Start()
 {
     _hosts = new List<ServiceHost>();

     foreach (Type serviceType in _services)
     {
         ServiceHost serviceHost = new ServiceHost(serviceType);
         _hosts.Add(serviceHost);
         serviceHost.Open();
         System.Diagnostics.Trace.WriteLine(string.Format("Host for {0} has been started...", serviceType.Name));
     }
 }

       

We keep the hosts to allow shutting down the hosts when it is needed.

public void Shutdown()
{
    foreach (ServiceHost serviceHost in _hosts)
    {
        serviceHost.Close();
        System.Diagnostics.Trace.WriteLine("Host for {0} has been closed...", serviceHost.Description.ServiceType.Name);
    }
}

To host many services with many app.config file, the EXE gets all the matched (dll) file in a directory and create a host to each of them:

namespace WCFHost
{
    class Program
    {
        private static List<IIsolatedServiceHost> _hosts = new List<IIsolatedServiceHost>();

        static void Main(string[] args)
        {
            HostAll();
            Console.WriteLine("Press <Enter> to exit...");
            Console.ReadLine();
        }

        private static void HostAll()
        {
            string [] assemblyFiles = Directory.GetFiles(@"C:\Users\alon\Documents\Visual Studio Codename Orcas\Projects\KLA\GenericHostExample\WCFHost\Services", "*.dll", SearchOption.TopDirectoryOnly);

            foreach (string assemblyFile in assemblyFiles)
            {
                IIsolatedServiceHost host = IsolatedServiceHost.HostServices(assemblyFile);
                _hosts.Add(host);
                host.Start();
            }
        }

        private static void ShutdownAll()
        {
            _hosts.ForEach((host) => host.Shutdown());
        }
    }
}
 

As I said, this is just for the example, you should use another configuration file that will tell you where to look for all of your services assembly (of course, without full path :)

 

You can download the solution from here.