Archive for January, 2008
XPO and Instance scopes in autofac
One of the appealing ideas in the autofac is scope nesting and explicit life management of the component instances.
Imagine the situation of the task server, where every task depends on a wide variety of the services. Let us take “Session” as an example. It could be registered in the container with the following lambda expression:
this.Register(c => c.Resolve<IServerConnector>().GetSession()).
As<Session>().
WithScope(InstanceScope.Container);
Note, that the actual component being registered is a Session factory that resolves some IServerConnector and produces instances with the scope of their container. The container will have only one session and it will be disposed when the container itself get disposed.
Additionally note that this lambda resolution is faster than the reflection, so we can use it for the micro-scopes.
The tasks could be registered in a ITaskRegistrar (just a simple List<Func<IContext, ITask>> for this situation) using the same lambda syntax:
ITaskRegistrar resolve = container.Resolve<ITaskRegistrar>();
resolve.Register(c => new ProcessApprovedApplications(c.Resolve<Session>()));
resolve.Register(c => new RetrieveQueuedResults(c.Resolve<Session>()));
IServerConnector and ITaskRegistrar get registered in the xml configuration file along with modules that contain the component registrations mentioned above.
Then the simple task server itself could be written like this:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterModule(new ConfigurationSettingsReader("core"));
using (Container c = builder.Build())
{
foreach (Func<IContext, ITask> func in c.Resolve<ITaskRegistrar>())
{
using (ITask task = func(c))
{
task.Execute();
}
}
}
In this scenario all tasks share the same scope and the Session instance within.
In order to make every task use its own session (i.e.: for the purposes of making them thread-safe), we could slightly modify the code:
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterModule(new ConfigurationSettingsReader("core"));
using (Container c = builder.Build())
{
foreach (Func<IContext, ITask> func in c.Resolve<ITaskRegistrar>())
{
using (Container taskScope = c.CreateInnerContainer())
using (ITask task = func(taskScope))
{
task.Execute();
}
}
}
And if you want to keep the single session per container, but give the tasks separate UnitOfWork instances (provided UnitOfWork inherits from Session), then you could use the first program snippet but change the Session registration routine to:
this.Register(c => c.Resolve<ServerConnector>().GetSession()).
As<Session>().
WithScope(InstanceScope.Container).
Named("session");
this.Register(c => c.Resolve<Session>("session").BeginNestedUnitOfWork()).
As<Session>().
WithScope(InstanceScope.Factory);
Basically here we intercept the requests to Session and return transient UnitOfWork instances instead.
Organization of xLim solutions: development, svn and integration
In my last article for the xLim 2 series I’ve talked about some simple development principles that proved to be effective in the implementation projects. In this post I’ll start describing the common solution structure that was established in that process.
Note, that I will not explicitly address database management in this structure (folder for the db-create, chained revision-based .cs and .sql upgrade scripts etc), since the XPO manages most part of the complexity here.
Below you will find the folder structure for the root of your version control repository.
SVN:\
Doc - development-related docs
000 Global - "Global" contains all
001 Initialization guidelines, MSP files,
002 Project Planning project scope/chapter etc
003 Prototyping - numbered folders form up
004 Milestone 1.0.0 the simple timeline
...
Extension - extensions are kept and
CRM managed separately. "System"
E-Forms distros go into "Resources"
ConstructionPM in a binary form
System --> - xLim framework
Prototype - those are better than UCs
001 SimpleCAB and represent excellent
002 Web IoC knowledge-base material
003 DxGridSchema for new developers.
004 Workflows
...
Here’s the generic organization of the “System” folder (that’s the core sources for the specific xLim 2 implementation). The extension projects also have similar structure.
SVN:\System\
branch - development branches, if needed
distribute - CC.NET releases into this folder
1.0.0.23 X.Y.Z.Revision format
1.0.1.45
1.0.2.92
...
tag
1.0.0.1 - X.Y.Z.Revision format for every
1.0.0.2 successful commit (autocreated
1.0.0.3 by the integration server)
...
trunk -->
Note, that it is a good idea to place some restrictions on the trunk folders from the very start (if you are using Subversion). Here are the switches that we normally setup:
- svn:ignore
- tsvn:logminsize (20-40 characters is enough)
- bugtraq:warnifnoissue
- bugtraq: logregex (it is enough to set “#(\d+)” pattern to make any #BugId number recognizable by both TortoiseSVN and Trac)
- bugtraq:url (for the purposes of trac integration this points to the tickets path and allows you to open referenced tickets by just clicking on the number in the TortoiseSVN log)
SVN:\System\trunk\
Help - help templates and articles
Resource - everything that's needed to build
Dxperience fresh check-out on a new machine
Castle is here (except .NET SDKs).
EntLib The same rule applies to the
Images specific extensions as well
NAnt
NUnit
7za.exe
Source - that's just the sample. It
xLim.Client.Core will described below.
xLim.Client.DxCore
xLim.Client.Interface
xLim.Client.Host
xLim.Server.Core
xLim.Server.Host
xLim.Server.Interface
xLim.Server.WebServices
xLim.Shared.Business
xLim.Shared.Core
xLim.Shared.Data
xLim.Shared.Interface
xLim.Web.Core
xLim.Web.DxCore
xLim.Web.Host
xLim.Web.Interface
GlobalAssemblyInfo.cs - you keep those shared and
VersionAssemblyInfo.cs accessible, do not you?
Test - unit tests per project
Tool - some tools that could make
DeploymentAid development and maintenance
NavigationBuilder more simple and efficient
SchemaTool
xLim.Core.sln
xLim.Core.build - NAnt build file
go.cmd - simply calls Resource/Nant
Farmenu.ini for the build file
This is how the development checkout copy is usually organized for multiple solutions.
Working folder (i.e.: E:\Projects\xLim):
Docs - check-out of the documents
xLim.ConstructionPM - check-out of the CPM trunk
xLim.CRM
xLim.System - check-out of the System trunk
Build - this folder is autocreated
Here’s the simple folder structure for the xLim 2 integration server.
Integration:\
Logs - rolling logs of CC.NET
xLim.CPM - separate folder per solution
xLim.CRM being built
xLim.System
Artifact - CC.NET build artifacts
Build - NAnt works here
Distrib - integration server creates
1.0.0.23 distributions here and adds them
1.0.1.45 to the SVN. Normally "Distrib"
1.0.2.92 is accessible via secure FTP
...
lastversion.txt - simple, but helps to automate
State - CC.NET state information
Trunk - Working copy
ccnet.config - the name speaks for itself
A word on the changes. The development cycle of the main project is separate from its extensions (that’s convenient for medium-sized development projects and teams). Extensions use the “Interface” and “Core” libraries from the main xLim project (those are distributed in a form of packaged APIs and are stored in “Resources” folder in the binary form).
Additionally the integration server could package the extensions in one complete setup file along with the corresponding version of the actual application hosts (Server, Desktop or Web). That’s where Integration:\xLim.System\Distrib\[Version] are come in handy.
In my next post I’ll concentrate on the logical structure and the internal dependencies of the actual “SVN:\System\trunk\” Visual Studio solution.
Some more insight into the autofac
Here are some updates:
- The code required to compile autofac against .NET 2.0 (in a single file) could be located here.
- As Nicholas pointed out, there is no need to register parent scope or do something exceptional about the lambda expression. The expression already evaluates against the current scope. This nicely deals with the previous Castle MicroKernel problem described here (what’s more important, it is easy to understand what and why happens behind the curtains). See his snippet below
ContainerBuilder cb = new ContainerBuilder();
cb.Register(c =>; new A(c.Resolve<B>())).
WithScope(InstanceScope.Factory);
Container parent = cb.Build();
Container child = cb.CreateInnerContainer();
var childBuilder = new ContainerBuilder();
childBuilder.Register(c => new B());
childBuilder.Build(child);
A a = child.Resolve<A>();
More updates are to come. I’ll try to check out the following areas of the autofac IoC:
- XML configuration (nothing exceptional is expected, but still you never know)
- Lifestyles and scopes
- Possibility of implementing the policy injection with the autofac (may be lambdas would aid a little bit here too…). It is always nice to know if you can route all your business objects through some fast IoC.
One more interesting and fresh .NET IoC container: autofac
Recently I’ve stumbled upon the autofac IoC container.
This container creates an impression of being more flexible and lean than the Castle, StructureMap, and the ObjectBuilder. Additionally, it is supposed to provide inherently good performance, scope and disposal handling.
Well, if the container uses lambda expressions as one of the options to register components, that already tells something about the approach (yes, this means “no Constructor reflection”). If everything is at this level then theoretically we could be talking about cheap and numerous MicroScopes here.
Given that, the autofac could be a better alternative for the Castle in xLim 2 solution.
One usability problem for xLim 2 lies within the .NET framework incompatibility. xLim 2 itself is restricted to 2.0 for a number of reasons; autofac uses 3.5 and VS 2008.
This turned out to be just a small obstacle. In my last post I’ve spoken about using .NET 3.5 features for the .NET 2.0. With that, it took only 10 minutes to compile the sources against .NET 2.0. I’ve simply copied a big chunk of the “System.Linq.Enumerator, System.Core.dll” extension class into the solution. It compiled and passed all the tests flawlessly.
However it took a little bit more time than that to start playing with the container (it is curious that the biggest efficiency drain during these 90 minutes was the absence of the Resharper in the fresh install of VS 2008).
I’ve prototyped a couple of primary usage scenarios and the first impression is that autofac is capable of handling the xLim 2 better than the Castle MicroKernel/Windsor Container currently does (of course there should be more solid testing before the actual replacement would be seriously considered).
The strongest argument against the autofak is that it does not have solid community around it and thus there is no solid production background. In fact, it is being developed right now by Nicholas Blumhardt. However the last point does have some appeal to it, since small systems/groups always seem to have more flexibility and mobility (that’s due to lower inertia and decision-making lags).
Additionally I was not able to get a quick answer for the question in this post. In short the problem is like this:
- A depends on B;
- A has a transient lifestyle (every time a new instance could be created);
- A is registered in the parent scope;
- B is registered in the child scope;
- how do we resolve A in a child scope?
Of course there could be somewhat straightforward solution like this:
- Register parent scope as component in every child scope
- Register A with the lambda expression that will dig get the parent expression and resolve the dependency.
Although this approach is close in spirit to the self-replicating nanorobots, it just does not feel right.
PS: Next post on autofac contains link to .NET 2.0 compatibility .cs file. This file is all you need in order to compile autofac to target .NET 2.0.
How to use .NET 3.5 syntax and compiler features for .NET 2.0?
I’ve just squeezed out a couple of hours to install and try Visual Studio 2008 today. There were two surprises that came with that:
- Unpleasant - Resharper does not fully support .NET 3.5 (it almost hurts). It is planned to have R# 4.0 release around 2008 Q1.
- Pleasant - Some .NET 3.5 compiler and syntax sugar features work with .NET 2.0.
Let me expand on the last item. These .NET 3.5 features work with .NET 2.0:
- local variable inference
- anonymous types
- object initializers
- extensions methods
- query expressions
- lambda expressions
In other words, this code does compile in Visual Studio 2008 when .NET 2.0 framework is targeted:
public static class Program
{
static void Main(string[] args)
{
List<Customer> customers = new List<Customer>
{
new Customer{Name="Bob", Age=23},
new Customer{Name="Donald",Age=10}
};
var query = from c in customers
where c.Age > 11
select c.Name;
query.Print();
}
public static void Print<T>(this IEnumerable<T> collection)
{
foreach (T item in collection)
{
Console.WriteLine(item);
}
}
}
public class Customer
{
public string Name { get; set; }
public int Age { get; set; }
}
To make this happen you just need to add the following namespace and class declarations:
namespace System
{
public delegate TRes Func<TSrc, TRes>(TSrc src);
}
namespace System.Runtime.CompilerServices
{
public class ExtensionAttribute : Attribute { }
}
namespace System.Linq
{
public static class Enumerable
{
public static IEnumerable<TSrc> Where<TSrc>(
this IEnumerable<TSrc> source, Func<TSrc, bool> predicate)
{
List<TSrc> res = new List<TSrc>();
foreach (TSrc s in source)
{
if (predicate(s))
{
res.Add(s);
}
}
return res;
}
public static IEnumerable<TRes> Select<TSrc, TRes>(
this IEnumerable<TSrc> source, Func<TSrc, TRes> selector)
{
List<TRes> res = new List<TRes>();
foreach (TSrc s in source)
{
TRes t = selector(s);
res.Add(t);
}
return res;
}
}
}
And if you remove the code above (i.e.: using C# preprocessor directive #if), and switch then target framework to .NET 3.5, the program will still compile.
It is sweet, is not it?
If this works as it is expected I’d be glad to have those features in .NET 2.0 projects. This will help to produce more efficient code.
Update: In the next post I’ll explain how to compile autofac project (it extensively uses lamda expressions and extensions) against .NET 2.0. And there’ll be some compatibility code in a single file.
Update: alternatively you can use LinqBridge library to compile against .NET 2.0 or simply manually reference System.Core.dll (requires editing of project file).
Common development principles for xLim
In the previous post on xLim 2 I’ve talked about the software requirements and recommendations for this kind of architecture solution (IDE, components, libraries and tools).
In this post I’ll attempt to cover briefly major guidelines and principles that proved useful for this approach.
The following principles are recommended to be followed for the efficient xLim 2 development:
- Employ version control and continuous integration (using only dedicated server for the releases)
- Great deal of xLim solutions’ business value is stored in the specific configuration settings (i.e.: schema files, configuration presets for the GridView handlers, layout presets for the single editors, permission settings and workflow chains). They also should be included in the version control and continuous integration.
- Employ test-driven development where appropriate (i.e.: UI handlers and controllers would benefit from the unit tests, workflow micro-controllers are too straightforward to be tested).
- It should take less than 30 minutes on a new machine to setup integration environment, get the sources, compile xLim 2, test it and initialize specific pre-configured solution.
- Develop xLim system vertically not horizontally (create and deploy into production simple full-layered solution first, and then start extending it).
- Stick to the Don’t Repeat Yourself principle (refactor and reuse code, controllers and handlers where appropriate)
- Perform rapid prototyping in the first phase of the software development project and then follow by the iterative development (2 week development phases with planning of 2-3 phases into the future). Planning should include logically working through all the changes-to-be-implemented and aligning them with the evolution of the architecture.
- Always schedule time for xLim configuration and tuning after each milestone.
- Regular backup policy should obviously be in place.
- Do not waste time writing throw-away use cases that are used only for formal acceptance. It is more efficient to stay in close contact with the customer and understand his real business requirements.
- Writing composite business functionality overviews and visualizing workflows, on the other hand, is recommended. xLim is extremely flexible, so it is too easy for the customers to get lost in their own choices about configuring views, workflows and the security. Composite overviews and workflow maps will serve as an excellent way of staying on the same page and initial documentation.
- xLim 2 solutions do not favor big resource pools and parallel distribution of tasks. That’s because of the complex nature of the solution. This implies greater development risks and the need to manage them carefully.
- Do not implement configurability and flexibility just for the sake of doing that. I’ve seen good information management systems turn into an unmaintainable and logical mess, just because they were made too flexible. Flexibility requires separate management UI and adds additional fragile points in the system.
- Work closely with the end-users. xLim 2 is extremely flexible, so they should get the feeling/understanding of the functionality that is easy to achieve (i.e.: adding new business objects with the grids and complex workflows) and is not so easy (i.e.: adding completely new type of shared object that requires complex web and desktop UI handlers).
Additionally, here are the references that proved themselves to be quite useful while establishing the guidelines for the specific xLim solutions and introducing new developers to them:
- Project Management Body of Knowledge - that’s the universal "vocabulary" for all the projects. It does not say a single word about the software development, yet it is extremely useful for understanding and applying any methodology.
- Article on agile methodologies by Martin Fowler - this article is the classics.
- SSW Rules to better… - this is an incredibly useful resource (note, however, that this Australian company is a Microsoft-oriented shop; this means a lot of "TFS", "SourceSafe" and "Microsoft SQL Server"). The following topics are definitely worth reading:
- C# Coding standards (or you could pick any other good one. Being consistent in the project is more important than the debate "whether to prefix private members with underscore or not to prefix")
- How to set up a .NET development tree
- The Fine Art of Commenting
Although the XPO takes care of the 80% of database management, the remaining 20% still require some attention. Here’s the recommended reading on that:
- Database change management best practices
- Evolutionary Database Design by Martin Fowler
- Continuous Database Integration
In the next post on xLim 2 I’ll get closer to the development specifics of the actual solutions: recommended folder structure for the development, SVN and integration purposes; build scripts and change propagation in the big solutions.
How to simplify complex maintenance tasks of your information management solution
Here’s one more advantage of having universal (and secure) data gateway service on the server side (I’m talking about the xLim approach again): you can easily write small or large tools that simplify complex maintenance tasks.
Basically you can consider this console application to be an extremely light-weight client for the xLim 2 server architecture.
The advantages of this approach (as opposed to working directly with the database):
- The tool will have the access rights to the database that derive from the login permissions, audit log will have all events recorded for this login, all the workflows (if invoked) will be initiated by this login also (if there are permissions)
- You get low-level access to the database, but there is still no SQL to write.
- You can perform high-level operations that are not easy to do in the SQL (like spell-checking all Forms against MS Word dictionary)
- Imagine live system that uses XPO distributed caching. Normally it is not a good idea to work directly with the database, since all the changes go directly without even touching the cache. You’ll have to connect to the server anyway, since the cache will need to be notified of the changed tables. This approach, on the other side, already routes all the changes through the cache and the system will automatically stay consistent (actually the xLim2 server should not even expose the gateway to IDataLayer, but rather the cache communications, so it would be impossible to work without notifying the cache)
- In some hosting scenarios the database is not accessible outside the server. This approach does not need the direct access anyway - the gateway would do it.
Search
Archives
Recent Comments
- aCoder on Extension methods for interfaces
- Requirements for the Photon .NET project | Rinat Abdullin on IRepository, cross-cutting concerns and flexibility
- Nicholas Blumhardt on Extension methods for interfaces
- aCoder on IRepository, cross-cutting concerns and flexibility
- Rinat Abdullin on Blog upgraded
- Rinat Abdullin on Extension methods for interfaces
- IRepository, cross-cutting concerns and flexibility | Rinat Abdullin on Extension methods for interfaces
- Bill Pierce on Blog upgraded
- aCoder on Extension methods for interfaces
