Both of these technologies are very powerful ways of implementing object to relational mapping. With LINQ to SQL, I feel it should be built on top of “views” so when one makes table changes the whole system doesn’t come crashing down. The Entity Framework seems to cover such scenarios in its mapping functionality.
However, neither seems to place nice in the n-tier model. That is, in which DLL should the EDMX or the DBML files belong? In one sense they do data access and in another they define business entities. On the one hand, I suppose one could put them in the data access layer and use business logic to convert those entities to ones defined in the business tier. Yet, that seems like a lot of work and bit un-refactored. On the other hand, we could put them in the business layer, but then we are doing data access from the business layer.
I would like to see a system where the data access is decoupled from the entities. For now, I am going to add these files to the data access layer. Furthermore, I am going to add the Data Contracts from my service layer to my business layer: not really happy about that. However, this will allow me the ability to use my business layer as a translator to expose my entities to the Silverlight frontend.
I could add the data access to my service implementation project, but I think it is better to do such translations in the business layer. Obviously, we could build duplicate entities in the business layer and translate twice, then again to much work and not that refactored.
I am using Windows XP Professional, Visual Studio 2008 Professional, IE 7.0 and Silverlight 2.0. The weird thing is I must delete my history every time in IE before I can view changes to my Silverlight application. I am assuming that is caching the XAP file in the browser and not reloading it when I make changes. I am sure there is a work around, but I have not had time to look for it. Any ideas?
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.
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.
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>
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>
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.
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. | |