wuxsh wrote:
That's sort of what I'm doing.
Here's an example entity class:
Code:
Namespace Domain.Resources
<DataAccessRestricted(DataAccessRestrictionFlags.Delete)> _
Public Class Manufacturer
Inherits AuditableEntity(Of Integer)
Public Property Code() As String
Get
Return _code
End Get
Set(ByVal value As String)
_code = value
End Set
End Property
Public Property Comments() As String
Get
Return _comments
End Get
Set(ByVal value As String)
_comments = value
End Set
End Property
Public Overloads Overrides Function GetHashCode() As Integer
Return ([GetType]().FullName & _code).GetHashCode()
End Function
Private _code As String
Private _comments As String
End Class
End Namespace
As you see, AuditableEntity is inherited and provided the ID (primary key) type. When a programmer is working with a Manufacturer object, they can't see the auditable entity members without using the IAuditable interface. Since I've got NHibernate wrapped with a generic dao I don't need to write NHibernate interceptors (although I have in the past).
Here's the interface for my DAO
Code:
Public Interface IDao(Of EntityType, IdType)
Function GetById(ByVal id As IdType) As EntityType
Function GetById(ByVal id As IdType, ByVal shouldLock As Boolean) As EntityType
Function Load(ByVal id As IdType) As EntityType
Function Load(ByVal id As IdType, ByVal shouldLock As Boolean) As EntityType
Function GetAll() As List(Of EntityType)
Function GetAll(ByVal propertyName As String, ByVal orderBy As OrderBy) As List(Of EntityType)
Function GetByExample(ByVal exampleInstance As EntityType, ByVal ParamArray propertiesToExclude As String()) As List(Of EntityType)
Function GetUniqueByExample(ByVal exampleInstance As EntityType, ByVal ParamArray propertiesToExclude As String()) As EntityType
Function Save(ByVal entity As EntityType) As EntityType
Function SaveOrUpdate(ByVal entity As EntityType) As EntityType
Sub Delete(ByVal entity As EntityType)
Sub CommitChanges()
End Interface
The purpose of the DAO interface is that if the client pulls the plug on NHibernate (they've got a thing against open source it seems) I can put in my own little persistence layer implementation without mucking with too much of the code. I'll just change the back end, run my unit tests, and pray.
The AbstractNHibernateDao implements IDao and you're right, the user information is gleaned from the application context. The DAO factory currently takes the current userName from user context and feeds it to DAOs that it generates through an internal constructor. Like this:
Code:
Public Function GetManufacturerDao() As IManufacturerDao Implements IDaoFactory.GetManufacturerDao
Return New ManufacturerDao(GetAuditInfo())
End Function
So the ManufacturerDao will have the current userName when its created. When the Save() or SaveAndUpdate() methods are called, the dao will add the user name to ModifiedBy and add the DateModified using the current time. If the object is transient, AddedBy and DateAdded get populated too.
I didn't bother with other operations because I only care about save right now. Also, I'm currently being lazy with dirty data and using session.IsDirty() to determine if I muck with IAuditable classes.
The solution is pretty simple...I probably could have come up with something a little more elegant, but the client wants everything in VB.Net and this is my first job ever using it and I'm still a bit uncomfortable with the language (confused regarding support of delegates for one).
Regarding triggers, I don't have ability to update the DB schema but really don't favor using triggers anyway, they tend to be abused and can cause maintenance issues.
From a programming standpoint, day-to-day development isn't even aware of IAuditable or related features.