Blog Home  Home Feed your aggregator (RSS 2.0)  
A Random Walk Around .Net - Friday, April 25, 2008
 
 Friday, April 25, 2008

It has been a couple weeks since my last post.  As with many juggling family, career, socializing and hobbies can be quite a circus act.  Nonetheless, the DBinsor solution has not yet reached a critical mass for usability.

It is great that one can pull the configuration from the database, but how does one put it there in the first place.  Moreover, this process of insertion should be intuitive as well as allow for a holistic view of the data.

The end user of choice for DBinsor would be a system which utilized a plug in methodology meshed with a SOA topology.  More specifically, one who utilizes services contracts where the implementation is defined by configuration settings.  Thus utilizing DBinsor as a centralized configuration store should be quite appealing.

With this type of user in mind, a web user interface is the only sensible approach.  For example, an administrator could seamlessly change configurations of any service from any computer with accessibility to the DBinsor server without installing a windows client.

We could have chosen web forms or the new model view controller framework; however Silverlight 2.0 seems the most intriguing.  Moreover, we get the chance to work with XAML instead of HTML and Java Script.  The “Learn” section has been quite helpful in the process of learning Silverlight.

We will post something once we have basic functionality working.

Friday, April 25, 2008 5:15:16 PM (Central Standard Time, UTC-06:00)  #    Comments [0]    | 
 Sunday, April 13, 2008

            We uploaded the working code to the Code Gallery Site.  This should allow readers of this blog the ability to see exactly what is happening underneath the hood.  One very interesting use of this application is a centralized configuration repository.  That is because of the parameters feature of the Windsor Library, a user should use those instead of a configuration file.

If anyone is interested in contributing to this project, please send me an email.  The next features will include:

·         A web GUI to access the database

·         A windows application using reflection to auto generate data from an assembly

·         Using card space to add security

·         Exposing all the features of the container.

·         Performance Enhancements

·         And others.

We have also added Code Gallery to Navigation Section.

Sunday, April 13, 2008 6:07:53 PM (Central Standard Time, UTC-06:00)  #    Comments [0]    | 
 Saturday, April 12, 2008

To test this, we decided to setup an interface and two derived classes.  Then we added corresponding data to the database (we will build an intuitive graphical user interface eventually).  Finally, we used the Container Singleton class to resolve our types and print out the results.

 

            Class Diagram:

Database:

 

            Windsor Component View

Lookup Name

Namespace

Contract

Implementation

Component Id

Contract Id

Test1

DBinsor.Svc.WCF.Client

IMath

Add

1844d2e0-455e-4d44-a9ff-1c63f78e407b

0aa71152-91d4-4e8f-a4a0-4124a98e2851

Test1

DBinsor.Svc.WCF.Client

IMath

Subtract

5c3ee79a-926c-4438-8391-44a437bd8f47

0aa71152-91d4-4e8f-a4a0-4124a98e2851

           

            Windsor Parameters View

Component Id

Contract Id

Parm Name

Default

Override

5c3ee79a-926c-4438-8391-44a437bd8f47

0aa71152-91d4-4e8f-a4a0-4124a98e2851

a

1

13

5c3ee79a-926c-4438-8391-44a437bd8f47

0aa71152-91d4-4e8f-a4a0-4124a98e2851

b

2

NULL

1844d2e0-455e-4d44-a9ff-1c63f78e407b

0aa71152-91d4-4e8f-a4a0-4124a98e2851

a

1

NULL

1844d2e0-455e-4d44-a9ff-1c63f78e407b

0aa71152-91d4-4e8f-a4a0-4124a98e2851

b

2

NULL

 

            Classes:

 

            Test:

IMath proxy = ContainerSingleton.Instance.Resolve<Add, IMath>();

Console.WriteLine(proxy.Calculate().ToString());

 

proxy = ContainerSingleton.Instance.Resolve<Subtract, IMath>();

Console.WriteLine(proxy.Calculate().ToString());

 

Console.WriteLine(ContainerSingleton.Instance.Xml);

           

            Result:

3

 

11

 

<configuration>

  <components>

    <component id="DBinsor.Svc.WCF.Client.Add"

service="DBinsor.Svc.WCF.Client.IMath, DBinsor.Svc.WCF.Client" type="DBinsor.Svc.WCF.Client.Add, DBinsor.Svc.WCF.Client">

      <parameters>

        <a>1</a>

        <b>2</b>    

      </parameters>

    </component>

    <component id="DBinsor.Svc.WCF.Client.Subtract"

service="DBinsor.Svc.WCF.Client.IMath, DBinsor.Svc.WCF.Client" type="DBinsor.Svc.WCF.Client.Subtract, DBinsor.Svc.WCF.Client">

      <parameters>

        <a>13</a>

        <b>2</b>

      </parameters>

    </component>

  </components>

</configuration>

Saturday, April 12, 2008 5:19:55 PM (Central Standard Time, UTC-06:00)  #    Comments [0]    | 
 Friday, April 11, 2008

One thing we have found useful over the years is to wrap a service proxy in a public interface Dynamic Link Library.  Though this in one sense may contradict the idea of Service Oriented Architecture (SOA); in another sense it holds up the idea of refactoring.

In this project, we have created a singleton that loads the configuration file from the database.  Furthermore, this singleton aggregates Windsor Container for easy of accessibility throughout ones program.  One draw back is (depending on usage) the load time of ones program may suffer due to Service / Database Access.

            Previously, we stated that DBinsor required each class to implement an interface.  Thus, we ensured this by added our custom resolve method in the singleton.

        public T Resolve<T, S>() 
            where T : class, S
        {
            return this._container.Resolve<S>(typeof(T).ToString()) as T;
        }

            In future releases of this library, we will add more methods to the singleton to provide access to other functionality of the Windsor Container.

            Again, we used the factory methodology to allow for multiple sources for the Windsor Container.  That is, we plan to allow a user to pick from our database service, an application configuration file, a web configuration file or a standalone xml file as their source for the singleton.

            The public interface uses the “app.config” or the “web.config” files for three things: lookup name, resource source and service binding.  Here is a sample:

 

<?xml version="1.0" encoding="utf-8" ?>

<configuration>

<appSettings>

<add key="LookupName" value="Test1" />

<add key="ResourceSource" value="Database" />

</appSettings>

<system.serviceModel>

<bindings>

<basicHttpBinding>

<binding name="ConfigSvcEndPoint" closeTimeout="00:01:00" openTimeout="00:01:00"

receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false"

bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard"

maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536"

messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered"

useDefaultWebProxy="true">

<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"

maxBytesPerRead="4096" maxNameTableCharCount="16384" />

<security mode="None">

<transport clientCredentialType="None" proxyCredentialType="None"

realm="" />

<message clientCredentialType="UserName" algorithmSuite="Default" />

</security>

</binding>

</basicHttpBinding>

</bindings>

<client>

<endpoint address="http://localhost:3837/DBinsor.Svc.WCF.Host/Configuration.svc"

binding="basicHttpBinding" bindingConfiguration="ConfigSvcEndPoint"

contract="ConfigurationServiceProxy.ConfigurationServiceContract"

name="ConfigSvcEndPoint" />

</client>

</system.serviceModel>

</configuration>

 

 

Friday, April 11, 2008 4:10:54 PM (Central Standard Time, UTC-06:00)  #    Comments [0]    | 
 Thursday, April 10, 2008

In the last post, we purposely did not address the core question of how does one generate the xml configuration from the database.  This used to require quite a bit of code; however, we have LINQ (praise the Microsoft Gods).  I am by no means a LINQ expert, nevertheless I know enough to make it work.

The code is pretty straight forward; we used LINQ to SQL to select all rows from the database where the lookup name equals our requested name.  Then we used LINQ to XML to put those rows into an XML file.  This was almost too trivial of a task.

using System;
using System.Linq;
using System.Xml.Linq;
using DBinsor.Svc.WCF.BusinessEntities;
using DBinsor.Svc.WCF.DataAccess;

namespace DBinsor.Svc.WCF.BusinessLogic
{
    public class ResourceFromDatabase : IResource
    {
        public ConfigurationEntity GenerateXml(ConfigurationEntity entity)
        {
            if (entity == null)
            {
                throw new ArgumentException("Configuration Entity Was Null.", "entity");
            }

            if (string.IsNullOrEmpty(entity.LookupName) == true)
            {
                throw new ArgumentException("Lookup Name Was Null or Empty.", "entity");
            }

            using (DBinsorViewDataContext windsorDataAccess =
                                new DBinsorViewDataContext())
            {
                entity.Xml = new XElement("configuration",
                                  new XElement("components",
                                       from components in windsorDataAccess.WindsorComponents
                                       where components.LookupName == entity.LookupName
                                       orderby components.ImplementationName
                                       select new XElement("component",
                                                 AddRequiredAttributes("id", components.Namespace + "." + components.ImplementationName),
                                                 AddRequiredAttributes("service", components.Namespace + "." + components.ContractName + ", " + components.Namespace),
                                                 AddRequiredAttributes("type", components.Namespace + "." + components.ImplementationName + ", " + components.Namespace),
                                                 AddOptionalAttributes("inspectionBehavior", components.InspectionBehavior),
                                                 AddOptionalAttributes("lifestyle", components.Lifestyle),
                                                 AddOptionalAttributes("customLifestyleType", components.CustomLifestyleType),
                                                 AddOptionalAttributes("initialPoolSize", components.InitialPoolSize.ToString()),
                                                 AddOptionalAttributes("maxPoolSize", components.MaxPoolSize.ToString()),
                                                        AddParameters(components.ComponentId, components.ContractId)
                                                )
                                       )
                                    );
            }
            return entity;
        }

        private static XAttribute AddRequiredAttributes(string name, string value)
        {
            return new XAttribute(name, value);
        }

        private static object AddOptionalAttributes(string name, string value)
        {
            if (string.IsNullOrEmpty(value) == true)
            {
                return null;
            }
            return new XAttribute(name, value);
        }

        private static XElement AddParameters(Guid componentId, Guid contractId)
        {
            using (DBinsorViewDataContext windsorDataAccess =
                                new DBinsorViewDataContext())
            {
                return new XElement("parameters",
                                    from parameters in windsorDataAccess.WindsorParmaters
                                    where (parameters.ComponentId == componentId && parameters.ContractId == contractId)
                                    orderby parameters.ParmName
                                    select new XElement(parameters.ParmName,
                                               ReturnDefaultOrOverride(parameters.ParmDefault, parameters.OverrideValue))
                                    );
            }
        }

        private static string ReturnDefaultOrOverride(string defaultValue, string overrideValue)
        {
            if (string.IsNullOrEmpty(overrideValue) == false)
            {
                return overrideValue;
            }
            return defaultValue;
        }
    }
}

            The only concern I have is in the “AddParameters” function; does it make a round trip to the database on every select?  I think it probably does; thus this may be a huge performance bottleneck.  However, first we should build for proof of concept.  Then we can go back and optimize.

Thursday, April 10, 2008 3:11:33 PM (Central Standard Time, UTC-06:00)  #    Comments [0]    | 
 Wednesday, April 09, 2008

Quite shockingly, we have etched out an n-tier service oriented system without writing a single line of code.  Moreover, we are following best practices to decouple the data layer, business layer, service layer and presentation layer. Generally, we do this for two reasons: one, to promote the idea of separation of concerns and two, to allow for future flexibility when addressing change sets.

The wiring process can be summed up as adding business entities and logic to link the data layer to the service layer.  Business entities should be though of as objects that hold state.  Business logic should be thought of functions or processes that affect business entities.  This is gray definition, because in some cases business entities might have functionality.  An example of a business object is a person; who has a name and an age.  However, this person may have an internal process called heartbeat: thus the grayness.

We will define one business entity (for now) called “ConfigurationEntity”.  This entity will have two properties: “LookupName” and “Xml”.  The “LookupName” will be used to find the castle configuration in the database.  The “Xml” will hold the dynamically generated xml configuration from the database.

Before we proceed, generally I would unit test at this point.  Test Driven Development (TDD) states: we should write tests first then code.  However, because it would require the use of mocks, I will refrain until a later post.

            Next, we will use a factory pattern to generate the xml from the database.  Again, we are choosing a design that provides for flexibility during future change sets.  The factory uses an enumeration to decide which type of process to return.

            Finally, we have to generate translators to and from our service layer data contracts to our business layer business entities.  These translators will be used in our service implementation to allow access to our business logic.

            

Class Diagram:

Factory:

namespace DBinsor.Svc.WCF.BusinessLogic
{
    public static class ResourceFactory
    {
        public static IResource GenerateResouce(ResourceLocations location)
        {
            switch (location)
            {
                case ResourceLocations.Database:
                    return new ResourceFromDatabase();
                default:
                    throw new ArgumentException("Location Not Supported.", "location");
            }
        }
    }
}

Finally, we have to generate translators to and from our service layer data contracts to our business layer business entities. 

These translators will be used in our service implementation to allow access to our business logic.

 

namespace DBinsor.Svc.WCF.ServiceImplementation
{
    public static class TConfigurationEntityAndConfigurationResponse
    {
        public static ConfigurationResponse Convert(ConfigurationEntity from)
        {
            ConfigurationResponse to = new ConfigurationResponse();
            to.ConfigurationPart = new ConfigurationPart();
            to.ConfigurationPart.XmlConfiguration = new XmlConfiguration();
            to.ConfigurationPart.XmlConfiguration.Config = from.Xml.ToString();
            return to;
        }
    }
}
namespace DBinsor.Svc.WCF.ServiceImplementation
{
    public static class TConfigurationEntityAndConfigurationRequest
    {
        public static ConfigurationEntity Convert(ConfigurationRequest from)
        {