.NET Enterprise SOA – NIntegrate Practice
1. Enterprise SOA framework & platform.
In software development,
A framework is more like a set of DLLs to be shared by different applications.
A platform is an integration of software (like frameworks, applications, architect, databases, etc), IT environment (like servers to deploy your applications, network accessibility to limit the behaviors of your applications, etc) and the operation processes (executed by people manually, not by machine automatically).
In enterprise SOA, what there is really needed is a platform rather than some software elements only. So as a programmer in enterprise SOA, when designing & implementing software elements, you should always realize that your application consists of not only software elements. The architect & the behavior of your software application is limited by and should be optimized for your specific IT environment; and sometimes, operation processes could play the roles more practical & economic than making everything automatic but behaves and thinks ugly and more like a machine.
2. What is Nintegrate?
NIntegrate is open source .NET enterprise SOA framework. It provides several reusable SOA components to solve specific enterprise SOA problems. Different from some other one-in-one SOA solution, NIntegrate is designed to be easily integrated with your company’s application frameworks.
3. Problems NIntegrate solves.
1) Decouple WCF configuration from Web.config/App.config files.
The most often complained limitation of WCF for enterprise SOA is you have to put WCF configuration in your Web.config/App.config files. Although there are some samples of publishing or consuming WCF services through programming, you could quickly find, too many WCF functions highly coupled with application configurations files. NIntegrate provides a set of classes to decouple this limitation.
With the help of NIntegrate, you could easily store your WCF configuration anywhere, no matter for publishing or consuming services. For example, you can store your WCF configuration in database instead of in Web.config files.
Sample code for storing WCF configuration outside Web.config:
The .svc file:
Factory="DummyEnterpriseFramework.DummyWcfServiceHostFactory" %>
The DummyWcfServiceHostFactory.cs file:
{
public override WcfService LoadServiceConfiguration(Type serviceType)
{
//get wcf service configuratin from somewhere, e.g. DB
//WcfService is a wcf datacontract, so you could persist it anywhere
//…
}
}
In code above, DummyWcfServiceHostFactory class inherits from WcfServiceHostFactory which is a class provided by NIntegrate framework. It easily overrides the LoadServiceConfiguration method to load the configuration for publishing a WCF service from a specific storage other than the Web.config file.
The DummyFramework class:
{
private readonly IDummyFrameworkConfiguation _config;
private static readonly Dictionary<Type, ChannelFactory> _cfCache = new Dictionary<Type, ChannelFactory>();
private static readonly object _cfCacheLock = new object();
public DummyFramework(IDummyFrameworkConfiguation config)
{
if (config == null)
{
throw new ArgumentNullException("config");
}
_config = config;
}
public string GetAppVariable(string key)
{
return _config.GetAppVariable(key);
}
public T CreateInstance<T>()
{
var type = _config.GetImplementationType(typeof (T));
return (T)Activator.CreateInstance(type);
}
public ConnectionStringSettings GetConnectionString(string key)
{
return _config.GetConnectionString(key);
}
public WcfChannelWrapper<T> CreateWcfChannel<T>()
where T : class
{
ChannelFactory cf;
if (!_cfCache.TryGetValue(typeof(T), out cf))
{
lock (_cfCacheLock)
{
if (!_cfCache.TryGetValue(typeof(T), out cf))
{
var endpoint = _config.GetWcfClientEndpointConfiguration(typeof (T));
cf = WcfChannelFactoryFactory.CreateChannelFactory<T>(endpoint);
_cfCache[typeof (T)] = cf;
}
}
}
return new WcfChannelWrapper<T>((cf as ChannelFactory<T>).CreateChannel());
}
}
Consume the service:
using (var wrapper = df.CreateWcfChannel<IDummyService>())
{
Response.Write(wrapper.Channel.SayHello());
}
2) Dynamic query through WCF.
Another always asked problem in SOA is how to implement dynamic query in SOA service, which means you have to serialize & deserialize the query object through WCF channel.
NIntegrate provides a set of classes to construct WCF serializable quey objects by LINQ style strong typed query language. With the help of these classes, you could easily implement not only high performance dynamic query but also complex query like paging and sorting through WCF.
Sample code for defining a serializable dynamic query object to query the “TestTable” table:
var criteria = table.Select(table.Int32Column, table.StringColumn, table.StringColumn)
.SetIsDistinct(true)
.SetMaxResults(10);
.SetSkipResults(10);
.SortBy(table.Int32Column, true).ThenSortBy(table.StringColumn, false);
.And(table.Int32Column == 1 && table.StringColumn.Like("%abc%"))
.Or(!(table.DateTimeColumn < DateTime.Now || table.GuidColumn != new Guid()));
3) Flexible object mapping.
Object mapping is always an essential function in enterprise applications.From IDataReader to entities, from entities in one application layer to another layer, from service to service, etc.
NIntegrate provides a set of classes to ultimately flexible map objects with code style mapping logic.
Sample code for mapping objects and their mapping logic:
var intToNullableDecimalMapper = fac.GetMapper<int, decimal?>();
var intListToNullableDecimalArrayMapper = fac.GetMapper<IList<int>, decimal?[]>();
var nullableDecimalArray = intListToNullableDecimalArrayMapper(new List<int> {1, 2, 3});
var intArrayToLongListMapper = fac.GetMapper<int[], List<long>>();
var longList = intArrayToLongListMapper(new[] {1, 2, 3});
fac.ConfigureMapper<string, double>()
.From(from => from).To<string>((to, val) => double.Parse(val));
var stringListToDoubleArrayMapper = fac.GetMapper<List<string>, double[]>();
var doubleArray = stringListToDoubleArrayMapper(new List<string> { "1.1", "2.2", "3.3" });
fac.ConfigureMapper<MappingFrom, MappingTo>(true, true, true)
.From(from => from.Other)
.To<double>(
(to, val) =>
{
to.Other2 = val.ToString();
return to;
});
var customMapper = fac.GetMapper<MappingFrom, MappingTo>();
var guid = Guid.NewGuid();
var customFrom = new MappingFrom { FromID = 1, Name = "name", Status = MappingFromStatus.Value2, Guid = guid };
var customTo = customMapper(customFrom);
4) LRU dictionary & caching.
The LruDictionary is a capacity limited dictionary, only LRU items are cached in it. It can be used as a simple cache with much better hit-rate than the common Dictionary class. There is also a LruDependingDictionary inherits the LruDictionary, providing the ability to expire items in the dictionary by specified dependency.
The LruItemCache is a smart cache supporting item indexing and LRU caching. It can be used to easily implement a more powerful cache.
5) Single thread worker queue.
The SingleThreadQueue is a queue attached with a thread & a process handler. Items added to the queue are always processed by specified process handler in a single thread. This class is very useful when you want to implement a single thread logging and processing in multithreading application.
4. Download NIntegrate.
Components discussed in this article based on the coming NIntegrate version 0.9 and later. So far, there is no zipped package for download. To download the source code of NIntegrate and the sample, please check out the code by SVN client according to: http://code.google.com/p/nintegrate/source/checkout.