I encountered a strange error wherein NHibernate UPDATE an entity even I'm just selecting it from Linq.
The nature of the problem is similar to this stackoverflow question: http://stackoverflow.com/questions/2001297/nhibernate-doing-updates-on-select/2001389#2001389
Though it's similar, it befuddled me for a while as I thought there's a flaw on my implementation of custom Jsonb .NET type for Postgresql.
I tried commenting all properties that uses Jsonb type, still there's an UPDATE when just selecting an entity.
When I'm down to few properties and commented them, the DateTime type, the unintended UPDATE error is gone.
The error is in the default value of non-null DateTime, if it's not explicitly assigned, the default value is 0001-01-01, so that's what gets saved to the database. That makes NHibernate wonky when reading the entity again, the pushed value to DateTime type and the read value become different.
The timestamp column is not nullable and have a default value of CURRENT_TIMESTAMP, so I thought it's prudent to make the corresponding property on application (.NET) a not nullable type too. There's a problem with making the .NET type non-nullable, if the value is forgotten to be explicitly assigned a value (e.g., DateTime.Now) in the application, the value that will get saved to database is 0001-01-01 since it's the default value of DateTime when it's not assigned a value.
To conclude, it's better to make the .NET DateTime type nullable even if the underlying datetime type is not nullable, so when it's forgotten to be assigned a value, what will be saved to database will be null, and since the timestamp column in the table is not nullable, it will throw an exception, it will fail fast, early error, early fix, no corrupt data.
Happy Coding!
"Simplicity can't be bought later, it must be earned from the start" -- DB
Thursday, February 25, 2016
Wednesday, February 10, 2016
Cannot Load Visual Studio Project
Error:
One possible cause of this error is when you configured your IIS 10 to accept wildcard in host name, think subdomain, or multi-tenant website. To fix it, do Unload Project, then edit the csproj, then change the IISUrl to something like this:
The Web Application Project SomethingGood.Api is configured to use IIS. The Web server 'http://www.localhost.com/api' could not be found.
One possible cause of this error is when you configured your IIS 10 to accept wildcard in host name, think subdomain, or multi-tenant website. To fix it, do Unload Project, then edit the csproj, then change the IISUrl to something like this:
<IISUrl>http://*.localhost.com/api</IISUrl>
Thursday, January 28, 2016
Aggregate
There are only four domain aggregates here, can you spot them?

Here are they:

An Aggregate works as one unit, its parts should be as close together as possible.

Happy Coding!
Here are they:
An Aggregate works as one unit, its parts should be as close together as possible.
Happy Coding!
Wednesday, November 5, 2014
Minimalistic Architecture
Here's a recommended minimalistic and minimum layers for a modern day line of business applications, especially the SPA ones
- App
- RichDomainModel - emphasis on rich
- RichDomainModel.Test
- RichDomainModelMapping
- Dto
- UnitTestFriendlyDal - Domain Access Layer, emphasis on unit-test friendly
1. App
- Hosts the UI
- Serves DTOs as JSON to browser using ASP.NET Web API. ASP.NET Web API gets and pushes DTOs to rich domain models
- Uses DTOs for data bag between UI and domain models
- For wiring dependencies, LightInject is highly recommended, it's a very capable IoC/DI container. For AOP concerns, LightInject has interception capability
- For JSON serializer/deserializer, NetJSON is highly recommended
- References RichDomainModel, Dto, LightInject, NHibernate
2. RichDomainModel
- Not only contains domain model's nouns, this also contains the domain model's business actions, verbs. See Martin Fowler's post regarding rich domain model & anemic domain model: http://www.martinfowler.com/bliki/AnemicDomainModel.html
- Receives and returns DTOs to App project
- Sample implementation:
using Dto;
using System.Collections.Generic;
using System.Linq;
using UnitTestFriendlyDal;
namespace Domain
{
public static partial class ProductionDomain
{
public class ProductCategory
{
public int ProductCategoryId { get; set; }
public string ProductCategoryName { get; set; }
public static IEnumerable<ProductionDto.ProductCategory> GetAll(IDomainAccess ds)
{
// http://www.ienablemuch.com/2014/10/proper-way-to-query-reference-entity.html
return ds.Query<ProductCategory>().MakeCacheable().ToList()
.Select(x => new ProductionDto.ProductCategory { Id = x.ProductCategoryId, Name = x.ProductCategoryName });
}
}
}
}
- Has no virtual keyword on domain models' members even though NHibernate need domain models' members to be virtual. Uses Virtuosity.Fody to automatically make the domain models' members virtual
- Uses static classes to enforce schema-like partitioning of domain models. static partial classes are better than namespace, namespace could be bypassed, read here: http://www.ienablemuch.com/2014/01/i-love-the-word-the.html
- Though I mentioned above I love the word The, it's better to use an apt naming convention for the domain models. For AdventureWorks example, the tables HumanResources.Department, HumanResources.Employee, Person.Address, Person.Person and Production.Product tables domain models counterparts are: HumanResourcesDomain.Department, HumanResourcesDomain.Employee, PersonDomain.Address, PersonDomain.Person, ProductionDomain.Product.
- Do note that the names with Domain suffix are static partial classes, not namespaces, to see why static partial classes are better than namespace, see the link above. As for the need to use suffix/prefix, we cannot nest a class inside another class if it has the same name as the outer class
- References Dto and UnitTestFriendlyDal only
3. RichDomainModel.Test
- Tests the business logic / behavior of the rich domain models
- References RichDomainModel, RichDomainModelMapping, NHibernate, UnitTestFriendlyDal
4. RichDomainModelMapping
- Maps relational entities to RichDomainModel
- Sample implementation: https://github.com/MichaelBuen/DemoSpaArchitectureMvp/blob/master/DomainMapping/Mapper.cs
- References RichDomainModel and NHibernate only
5. Dto - data bag between UI and rich domain model
6. UnitTestFriendlyDal
- DAL is not data access layer nor repository. This is just a domain access layer that needed its Linq be mockable. The data access layer / repository is NHibernate itself, no need to abstract NHibernate. Don't pile abstractions after abstractions on top of NHibernate, especially if the ORM has a repository, unit-of-work and data access layer built in
- This is just a thin layer for ORM. NHibernate's .Query (Linq) is an extension method, extension methods can't be mocked properly, hence this interface is created. Abstracting the ORM is not the goal, it's the testability that we are after. Had NHibernate's .Query is not an extesion method, it can be mocked properly, this layer will not be needed. There's no need to add unnecessary abstractions on top of a capable ORM
- Sample implementation: https://github.com/MichaelBuen/DemoSpaArchitectureMvp/blob/master/UnitTestFriendlyDal/DomainAccess.cs
- References NHibernate only
Here's a sample of an SPA stack that applies the layers above
Sample Code: https://github.com/MichaelBuen/DemoSpaArchitectureMvp
Multi-tenancy concern
For multi-tenancy, it's better not to use schema on NHibernate. NHibernate doesn't have the capability yet to do proper multi-tenancy, Hibernate has
Shoehorning schema on NHibernate's ISessionFactory as a mechanism to do multi-tenancy would entail each tenant to have their own session factory. Disadvantage being, as the second level cache for common reference tables amongst tenants have a copy on each tenant's session factory, the second-level cache can't be shared effectively, or can't be shared at all. So changes on common reference tables on one tenant will not appear on other tenants' second level cache. On the other hand, if we isolate the common reference table to one session factory only, the drawback is we cannot navigate nor join tenant entities to those common reference tables as those entities live in their own session factory
I'll expound and make a simulation of this on another post
Database schema is a bit fancy as a multi-tenancy mechanism, especially if the ORM is not yet capable of mapping entities to schema on-the-fly or elegantly
So for now on NHibernate, it's better to use filters for multi-tenancy concerns
Naming guideline
http://www.ienablemuch.com/2013/01/when-your-codes-names-are-running-afoul.html
Happy Coding!
Tuesday, October 28, 2014
WCF + LightInject + NHibernate (part 2 of 2)
On the part 1 of this post, I showed you how to integrate WCF and LightInject together. On this last part, I'll show you how to integrate NHibernate to them
This has 12 steps, let's begin
1. To continue, let's add a DTO project using Class Library project, name it Dtos. Delete Class1.cs
Add reference to System.Runtime.Serialization assembly
Add a DTO class, name it PersonDto.Member.cs. Static classes enforce schema-like organization of domain models / DTOs
2. On ServiceContracts project, add reference to Dtos project
Then add PersonContract.IMemberService.cs interface:
3. On ServiceImplementations project, add reference to Dtos project
Then add implementation for PersonContract.IMemberService interface, name it PersonImplementation.MemberService.cs, use the following code:
4. On AppService, open ServiceHostingEnvironment.tt, then save it even there's no changes on its code. Saving the text template will trigger the regeneration of the ServiceHostingEnvironment.config. The updated ServiceHostingEnvironment.config shall look like this:
5. Run AppService
To test, run WcfTestCLient, path is: "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\WcfTestClient.exe"
Add Service on My Service Projects, then use this endpoint address: http://localhost:50549/PersonImplementation.MemberService.svc
Invoke GetMembers(), you shall see this output:
6. On NHibernate integration, step 0, run this DDL script:
7. NHibernate's Linq is using extension method, and as such is virtually impossible to mock properly. With that said, we need to add a thin layer for accessing NHibernate, which can be mocked if we need to unit test the rich domain model's behavior
For that thin layer, create a class library project, name it UnitTestFriendlyDal
Add NHibernate from NuGet Packages
Create a class file, name it DomainAccess.cs. Then use this code on DomainAccess.cs:
8. Create a Class Library project. Name it RichDomainModels. Then delete Class1.cs
Then add reference to Dtos and UnitTestFriendlyDal
Add Virtuosity.Fody to the project, this AOP component saves us the need of adding virtual to every members of the domain model. All members of the domain models are required to be virtual by Nhibernate. With Virtuosity.Fody, all members are automagically made virtual for us, so we don't have to type virtual on each members anymore
Add a class file, name it PersonDomain.Member.cs, then use the folllowing code. Notice that we don't add virtual to Member's MemberId and MemberName, that's courtesy of Virtuosity.Fody
As for using static partial class (see PersonDomain on following code) instead of using namespace, we use static class to enforce schema-like way to group related domain models, namespace could be bypassed. Think of AdventureWorks database grouping related tables into their own schema
9. Add a Class Library project, name it RichDomainModelsMapping. Then delete Class1.cs
Add NHibernate and NHibernate.Caches.SysCache to the project:
Add reference to RichDomainModels
Add a class file, name it Mapper.cs, then use the following code:
10. On AppService
Add NHibernate to the project, and add reference to RichDomainModelsMapping and UnitTestFriendlyDal
On Global.asax.cs, add the wiring of NHibernate ISessionFactory and Domain Access Layer, line 38 and 39:
11. On ServiceImplementations, add reference to UnitTestFriendlyDal and RichDomainModels
Then change the PersonImplementation.MemberService to this code:
12. Make sure the connection string is correct on RichDomainModelsMapping
Run AppService
To test, run WcfTestClient and click Invoke on GetMembers, you shall see the following:
Complete code at: https://github.com/MichaelBuen/PlayWcfServiceApplication
Happy Coding!
This has 12 steps, let's begin
1. To continue, let's add a DTO project using Class Library project, name it Dtos. Delete Class1.cs
Add reference to System.Runtime.Serialization assembly
Add a DTO class, name it PersonDto.Member.cs. Static classes enforce schema-like organization of domain models / DTOs
using System.Runtime.Serialization;
namespace Dtos
{
public static partial class PersonDto
{
[DataContract]
public class Member
{
[DataMember]
public int MemberId { get; set; }
[DataMember]
public string MemberName { get; set; }
}
}
}
2. On ServiceContracts project, add reference to Dtos project
Then add PersonContract.IMemberService.cs interface:
using Dtos;
using System.Collections.Generic;
using System.ServiceModel;
namespace ServiceContracts
{
public static partial class PersonContract
{
[ServiceContract]
public interface IMemberService
{
[OperationContract]
string GetMembership();
[OperationContract]
IEnumerable<PersonDto.Member> GetMembers();
}
}
}
3. On ServiceImplementations project, add reference to Dtos project
Then add implementation for PersonContract.IMemberService interface, name it PersonImplementation.MemberService.cs, use the following code:
using System.Collections.Generic;
namespace ServiceImplementations
{
public static partial class PersonImplementation
{
[System.ServiceModel.ServiceBehavior(InstanceContextMode = System.ServiceModel.InstanceContextMode.PerCall)]
public class MemberService : ServiceContracts.PersonContract.IMemberService
{
IEnumerable<Dtos.PersonDto.Member> ServiceContracts.PersonContract.IMemberService.GetMembers()
{
yield return new Dtos.PersonDto.Member { MemberId = 1, MemberName = "John" };
yield return new Dtos.PersonDto.Member { MemberId = 2, MemberName = "Paul" };
yield return new Dtos.PersonDto.Member { MemberId = 3, MemberName = "George" };
yield return new Dtos.PersonDto.Member { MemberId = 4, MemberName = "Ringo" };
}
string ServiceContracts.PersonContract.IMemberService.GetMembership()
{
return "Avengers";
}
}
}
}
4. On AppService, open ServiceHostingEnvironment.tt, then save it even there's no changes on its code. Saving the text template will trigger the regeneration of the ServiceHostingEnvironment.config. The updated ServiceHostingEnvironment.config shall look like this:
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true">
<serviceActivations>
<add relativeAddress="PersonImplementation.MemberService.svc" service="ServiceImplementations.PersonImplementation+MemberService" factory="AppService.LightInjectServiceHostFactory"/>
<add relativeAddress="SampleService.svc" service="ServiceImplementations.SampleService" factory="AppService.LightInjectServiceHostFactory"/>
</serviceActivations>
</serviceHostingEnvironment>
5. Run AppService
To test, run WcfTestCLient, path is: "C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\WcfTestClient.exe"
Add Service on My Service Projects, then use this endpoint address: http://localhost:50549/PersonImplementation.MemberService.svc
Invoke GetMembers(), you shall see this output:
6. On NHibernate integration, step 0, run this DDL script:
/*
drop table Person.Member;
drop schema Person;
*/
create schema Person;
go
create table Person.Member
(
MemberId int not null identity(1,1),
MemberName nvarchar(100) not null,
constraint pk_Person_Member primary key(MemberId)
);
go
insert into Person.Member(MemberName) values
('Ironman'),
('Captain America'),
('Thor'),
('Hulk');
go
7. NHibernate's Linq is using extension method, and as such is virtually impossible to mock properly. With that said, we need to add a thin layer for accessing NHibernate, which can be mocked if we need to unit test the rich domain model's behavior
For that thin layer, create a class library project, name it UnitTestFriendlyDal
Add NHibernate from NuGet Packages
Create a class file, name it DomainAccess.cs. Then use this code on DomainAccess.cs:
using System;
using System.Linq;
using NHibernate.Linq;
namespace UnitTestFriendlyDal
{
public interface IDomainAccessFactory
{
IDomainAccess OpenDomainAccess();
}
public class DomainAccessFactory : IDomainAccessFactory
{
NHibernate.ISessionFactory _sessionFactory;
public DomainAccessFactory(NHibernate.ISessionFactory sessionFactory)
{
_sessionFactory = sessionFactory;
}
public IDomainAccess OpenDomainAccess()
{
return new DomainAccess(_sessionFactory);
}
}
public interface IDomainAccess : IDisposable
{
IQueryable<T> Query<T>();
T Get<T>(object id);
T Load<T>(object id);
object Save(object transientObject);
void Evict<T>(object id);
}
class DomainAccess : IDomainAccess
{
NHibernate.ISessionFactory _sessionFactory;
NHibernate.ISession _session;
NHibernate.ITransaction _transaction;
public DomainAccess(NHibernate.ISessionFactory sessionFactory)
{
_sessionFactory = sessionFactory;
_session = _sessionFactory.OpenSession();
_transaction = _session.BeginTransaction();
}
IQueryable<T> IDomainAccess.Query<T>()
{
return _session.Query<T>();
}
T IDomainAccess.Get<T>(object id)
{
return _session.Get<T>(id);
}
T IDomainAccess.Load<T>(object id)
{
return _session.Load<T>(id);
}
void IDomainAccess.Evict<T>(object id)
{
_sessionFactory.Evict(typeof(T), id);
}
object IDomainAccess.Save(object transientObject)
{
return _session.Save(transientObject);
}
// Because transaction is a cross-cutting concern. It should be automated
void IDisposable.Dispose()
{
// http://www.hibernatingrhinos.com/products/nhprof/learn/alert/donotuseimplicittransactions
_transaction.Commit();
_transaction.Dispose();
_session.Dispose();
}
}//DomainAccess
public static class LinqExtensionMethods
{
public static IQueryable<T> GetPage<T>(this IQueryable<T> query, int pageLimit, int pageNumber)
{
var paged = query.Take(pageLimit).Skip(pageLimit * (pageNumber - 1));
return paged;
}
}
/// <summary>
/// cross-cutting concern
/// MakeCacheable replaces Cacheable, so IQueryable detection provider can be done here
/// Can't use NHibernate's built-in .Cacheable on non-NHibernate IQueryable, it will throw an error
/// </summary>
public static class NHibernateLinqExtensionMethods
{
public static IQueryable<T> MakeCacheable<T>(this IQueryable<T> query)
{
if (query.Provider.GetType() == typeof(NHibernate.Linq.DefaultQueryProvider))
query = query.Cacheable();
return query;
}
}
}
8. Create a Class Library project. Name it RichDomainModels. Then delete Class1.cs
Then add reference to Dtos and UnitTestFriendlyDal
Add Virtuosity.Fody to the project, this AOP component saves us the need of adding virtual to every members of the domain model. All members of the domain models are required to be virtual by Nhibernate. With Virtuosity.Fody, all members are automagically made virtual for us, so we don't have to type virtual on each members anymore
Add a class file, name it PersonDomain.Member.cs, then use the folllowing code. Notice that we don't add virtual to Member's MemberId and MemberName, that's courtesy of Virtuosity.Fody
As for using static partial class (see PersonDomain on following code) instead of using namespace, we use static class to enforce schema-like way to group related domain models, namespace could be bypassed. Think of AdventureWorks database grouping related tables into their own schema
using Dtos;
using UnitTestFriendlyDal;
using System.Linq;
using System.Collections.Generic;
namespace RichDomainModels
{
public static partial class PersonDomain
{
public class Member
{
public int MemberId { get; set; }
public string MemberName { get; set; }
public static IEnumerable<PersonDto.Member> GetMembers(IDomainAccess da)
{
var list = da.Query<PersonDomain.Member>().MakeCacheable().ToList()
.Select(x =>
new PersonDto.Member
{
MemberId = x.MemberId,
MemberName = x.MemberName
});
return list;
}
}
}
}
9. Add a Class Library project, name it RichDomainModelsMapping. Then delete Class1.cs
Add NHibernate and NHibernate.Caches.SysCache to the project:
Add reference to RichDomainModels
Add a class file, name it Mapper.cs, then use the following code:
using NHibernate.Cfg; // .DatabaseIntegration extension method
using System.Linq;
namespace RichDomainModelsMapping
{
public static class Mapper
{
static NHibernate.ISessionFactory _sessionFactory = Mapper.BuildSessionFactory();
// call this on production
public static NHibernate.ISessionFactory SessionFactory
{
get { return _sessionFactory; }
}
public static NHibernate.ISessionFactory BuildSessionFactory(bool useUnitTest = false)
{
var mapper = new NHibernate.Mapping.ByCode.ConventionModelMapper();
mapper.IsEntity((t, declared) => t.Namespace == "RichDomainModels");
mapper.BeforeMapClass += mapper_BeforeMapClass;
mapper.BeforeMapProperty += mapper_BeforeMapProperty;
mapper.BeforeMapManyToOne += mapper_BeforeMapManyToOne;
mapper.BeforeMapBag += mapper_BeforeMapBag;
var cfg = new NHibernate.Cfg.Configuration();
// .DatabaseIntegration! Y U EXTENSION METHOD?!
cfg.DataBaseIntegration(c =>
{
// SQL Server
c.Driver<NHibernate.Driver.SqlClientDriver>();
c.Dialect<NHibernate.Dialect.MsSql2008Dialect>();
c.ConnectionString = "Server=.;Database=PlayTheWcfServiceApp;Trusted_Connection=True";
if (useUnitTest)
{
c.LogSqlInConsole = true;
c.LogFormattedSql = true;
}
});
System.Collections.Generic.IEnumerable<System.Type> entities =
typeof(RichDomainModels.PersonDomain.Member).Assembly.GetExportedTypes()
// exclude static classes (the schema name)
// this will still include the non-static class inside of static classes, i.e., the domain models
.Where(x => !(x.IsAbstract && x.IsSealed));
NHibernate.Cfg.MappingSchema.HbmMapping mapping = mapper.CompileMappingFor(entities);
cfg.AddMapping(mapping);
cfg.Cache(x =>
{
// SysCache is not stable on unit testing
if (!useUnitTest)
{
x.Provider<NHibernate.Caches.SysCache.SysCacheProvider>();
// I don't know why SysCacheProvider is not stable on simultaneous unit testing,
// might be SysCacheProvider is just giving one session factory, so simultaneous test see each other caches
// This solution doesn't work: http://stackoverflow.com/questions/700043/mstest-executing-all-my-tests-simultaneously-breaks-tests-what-to-do
}
else
{
// This is more stable in unit testing
x.Provider<NHibernate.Cache.HashtableCacheProvider>();
}
// http://stackoverflow.com/questions/2365234/how-does-query-caching-improves-performance-in-nhibernate
// Need to be explicitly turned on so the .Cacheable directive on Linq will work:
x.UseQueryCache = true;
});
#if DEBUG
if (useUnitTest)
cfg.SetInterceptor(new NHSQLInterceptor());
#endif
//new NHibernate.Tool.hbm2ddl.SchemaUpdate(cfg).Execute(useStdOut: false, doUpdate: true);
//using (var file = new System.IO.FileStream(@"c:\x\ddl.txt",
// System.IO.FileMode.Create,
// System.IO.FileAccess.ReadWrite))
//using (var sw = new System.IO.StreamWriter(file))
//{
// new SchemaUpdate(cfg)
// .Execute(sw.Write, false);
//}
NHibernate.ISessionFactory sf = cfg.BuildSessionFactory();
return sf;
}
#if DEBUG
class NHSQLInterceptor : NHibernate.EmptyInterceptor
{
// http://stackoverflow.com/questions/2134565/how-to-configure-fluent-nhibernate-to-output-queries-to-trace-or-debug-instead-o
public override NHibernate.SqlCommand.SqlString OnPrepareStatement(NHibernate.SqlCommand.SqlString sql)
{
Mapper.NHibernateSQL = sql.ToString();
return sql;
}
}
public static string NHibernateSQL { get; set; }
#endif
static void mapper_BeforeMapBag(
NHibernate.Mapping.ByCode.IModelInspector modelInspector,
NHibernate.Mapping.ByCode.PropertyPath member,
NHibernate.Mapping.ByCode.IBagPropertiesMapper propertyCustomizer)
{
propertyCustomizer.Cache(cacheMapping => cacheMapping.Usage(NHibernate.Mapping.ByCode.CacheUsage.ReadWrite));
propertyCustomizer.Lazy(NHibernate.Mapping.ByCode.CollectionLazy.Extra);
/*
* class Person
* {
* IList<Hobby> Hobbies
* }
*
*
*/
string parentEntity = member.LocalMember.DeclaringType.Name; // this gets the Person
string foreignKey = parentEntity + "Id";
propertyCustomizer.Key(keyMapping => keyMapping.Column(foreignKey));
// http://www.ienablemuch.com/2014/10/inverse-cascade-variations-on-nhibernate.html
// best persistence approach: Inverse+CascadeAll
propertyCustomizer.Inverse(true);
propertyCustomizer.Cascade(NHibernate.Mapping.ByCode.Cascade.All);
}
static void mapper_BeforeMapManyToOne(
NHibernate.Mapping.ByCode.IModelInspector modelInspector,
NHibernate.Mapping.ByCode.PropertyPath member,
NHibernate.Mapping.ByCode.IManyToOneMapper propertyCustomizer)
{
/*
public class Product
{
protected internal int ProductId { get; set; }
public ProductionDomain.ProductCategory ProductCategory { get; protected internal set; }
public string ProductName { get; protected internal set; }
}
*/
// ProductCategory property name maps to ProductCategoryId column name
propertyCustomizer.Column(member.ToColumnName() + "Id");
}
static void mapper_BeforeMapProperty(
NHibernate.Mapping.ByCode.IModelInspector modelInspector,
NHibernate.Mapping.ByCode.PropertyPath member,
NHibernate.Mapping.ByCode.IPropertyMapper propertyCustomizer)
{
//string postgresFriendlyName = member.ToColumnName().ToLowercaseNamingConvention();
//propertyCustomizer.Column(postgresFriendlyName);
}
static void mapper_BeforeMapClass(NHibernate.Mapping.ByCode.IModelInspector modelInspector,
System.Type type,
NHibernate.Mapping.ByCode.IClassAttributesMapper classCustomizer)
{
classCustomizer.Cache(cacheMapping => cacheMapping.Usage(NHibernate.Mapping.ByCode.CacheUsage.ReadWrite));
string fullName = type.FullName; // example: RichDomainModels.ProductionDomain+Product
string[] fullnameSplit = fullName.Split('+');
string schemaName;
string className;
if (fullnameSplit.Length == 2) // The domain model is in a schema (e.g., ProductionDomain)
{
schemaName = fullnameSplit[0].Split('.').Last();
schemaName = schemaName.Substring(0, schemaName.Length - "Domain".Length); // removes the suffix Domain
className = fullnameSplit[1];
}
else // domain model is not inside schema
{
schemaName = "dbo";
className = fullnameSplit[0].Split('.').Last();
}
// Last() skips the other namespace(s). 3 skips the word The
string tableFullname = schemaName + "." + className;
classCustomizer.Table(tableFullname);
System.Reflection.MemberInfo mi;
System.Reflection.MemberInfo[] memberInfos = type.GetMember(className + "Id",
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (memberInfos.Length == 1)
{
mi = memberInfos[0];
}
else
{
System.Reflection.MemberInfo[] defaultIdNames = type.GetMember("Id",
System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (defaultIdNames.Length == 0)
throw new System.Exception("Impossible. Should have an ID");
else if (defaultIdNames.Length == 1)
mi = defaultIdNames[0];
else
throw new System.Exception("Houston we have a problem. Why there's multiple ID property?");
}
classCustomizer.Id(mi,
idMapper =>
{
idMapper.Column(mi.Name);
idMapper.Generator(NHibernate.Mapping.ByCode.Generators.Identity);
});
}
} // Mapper
}
10. On AppService
Add NHibernate to the project, and add reference to RichDomainModelsMapping and UnitTestFriendlyDal
On Global.asax.cs, add the wiring of NHibernate ISessionFactory and Domain Access Layer, line 38 and 39:
using System;
using System.Linq;
using UnitTestFriendlyDal;
namespace AppService
{
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
DependencyFactory.Container = new LightInject.ServiceContainer();
RegisterIocs();
}
static void RegisterIocs()
{
//// Instead of manually adding each implementation:
//DependencyFactory.Container.Register<ServiceImplementations.ProductImplementation.ProductService>(new LightInject.PerRequestLifeTime());
//DependencyFactory.Container.Register<ServiceImplementations.PersonImplementation.MemberService>(new LightInject.PerRequestLifeTime());
// Just do this:
var serviceImplementations = typeof(ServiceImplementations.SampleService).Assembly.GetTypes()
.Where(t => t.GetInterfaces()
.Any(i => i.GetCustomAttributes(false)
.Any(a => a.GetType() == typeof(System.ServiceModel.ServiceContractAttribute))));
foreach (var item in serviceImplementations)
{
DependencyFactory.Container.Register(item, new LightInject.PerRequestLifeTime());
}
DependencyFactory.Container.Register<IDomainAccessFactory, DomainAccessFactory>(new LightInject.PerContainerLifetime());
DependencyFactory.Container.RegisterInstance<NHibernate.ISessionFactory>(RichDomainModelsMapping.Mapper.SessionFactory);
}
}
}
11. On ServiceImplementations, add reference to UnitTestFriendlyDal and RichDomainModels
Then change the PersonImplementation.MemberService to this code:
using System.Collections.Generic;
using UnitTestFriendlyDal;
namespace ServiceImplementations
{
public static partial class PersonImplementation
{
[System.ServiceModel.ServiceBehavior(InstanceContextMode = System.ServiceModel.InstanceContextMode.PerCall)]
public class MemberService : ServiceContracts.PersonContract.IMemberService
{
IDomainAccessFactory _daf;
public MemberService(IDomainAccessFactory daf)
{
_daf = daf;
}
IEnumerable<Dtos.PersonDto.Member> ServiceContracts.PersonContract.IMemberService.GetMembers()
{
using (var da = _daf.OpenDomainAccess())
{
return RichDomainModels.PersonDomain.Member.GetMembers(da);
}
}
string ServiceContracts.PersonContract.IMemberService.GetMembership()
{
return "Avengers";
}
}
}
}
12. Make sure the connection string is correct on RichDomainModelsMapping
Run AppService
To test, run WcfTestClient and click Invoke on GetMembers, you shall see the following:
Complete code at: https://github.com/MichaelBuen/PlayWcfServiceApplication
Happy Coding!
Subscribe to:
Posts (Atom)




