Windows Support Number

  • Subscribe to our RSS feed.
  • Twitter
  • StumbleUpon
  • Reddit
  • Facebook
  • Digg

Monday, 19 October 2009

Property setters are just wrong!

Posted on 01:43 by Unknown
In general I think the use of property setters on classes is a sign of bad class design and a lack of OO principles - in most cases when you modify the 'state' of a class some 'behaviour' is invoked whether it be implicit or explicit;

e.g. when I change the address on a user account I want to make sure the address object has been populated (at least).

So these days I've started to design classes that have public getters & private setters, and if you want to modify state you are required to call a method, e.g. 'ChangeAddress'. This is nothing new, there are plenty of examples out there.

So in the example below 'Token' is property that's defined when the object is constructed and 'Path' is a property which is modified by calling the method 'ChangePath'.
    public class File : ObservableEntity<int>
{
private string _path;
private Guid _token;

private readonly IExtractFileProperties _propertiesExtractor = new FilePropertiesExtractor();


public File()
{
_token = Guid.NewGuid();
}


public virtual string Path
{
get { return _path; }
private set { ChangePropertyAndNotify(ref _path, value, x => Path); }
}

public virtual Guid Token
{
get { return _token; }
private set { ChangePropertyAndNotify(ref _token, value, x => Token); }
}


public File ChangePath(string path)
{
Path = path;

Size = _propertiesExtractor.Size(Path);
Format = _propertiesExtractor.Format(Path);
FileCreatedOn = _propertiesExtractor.CreatedOn(Path);
FileLastModified = _propertiesExtractor.LastModified(Path);

return this;
}
}


Now this attitude has been going well as it's made my classes more behaviour rich with better encapsulation & abstraction.

The only problem I've come across is when I'm trying to re-hydrate objects from the database where the properties I'm attempt to set have some kind of special behaviour - properties representing timestamps & versions that are historically set in the database via some function.

So in the following example I've got 3 properties - 'CreatedOn' 'LastModified' & 'Version'. I don't want to expose a method to set this values - I want them to be immutable...

But this is going to be a problem with an ORM, how am I going to update these values when saving or updating - I want them appear to be auto-updating from a users perspective.
    public abstract class ObservableEntity<T> : IEntity<T>, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };

private T _id;

private DateTime? _createdOn;
private DateTime? _lastModified;
private int? _version;

public virtual T Id
{
get { return _id; }
private set { ChangePropertyAndNotify(ref _id, value, x => Id); }
}

public virtual DateTime? CreatedOn
{
get { return _createdOn; }
private set { ChangePropertyAndNotify(ref _createdOn, value, x => CreatedOn); }
}

public virtual DateTime? LastModified
{
get { return _lastModified; }
private set { ChangePropertyAndNotify(ref _lastModified, value, x => LastModified); }
}

public virtual int? Version
{
get { return _version; }
private set { ChangePropertyAndNotify(ref _version, value, x => Version); }
}

protected void ChangePropertyAndNotify<T2>(ref T2 value, T2 newValue, Expression<Func<object, T2>> expression)
{
value = newValue;
Notify(expression);
}

protected void Notify<T2>(Expression<Func<object, T2>> expression)
{
var propertyName = ((MemberExpression)expression.Body).Member.Name;
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}

Now when using nHibernate as your ORM this becomes very easy because you can attach Listeners and do custom actions.

So I've got a custom action when NH saves or updates an entity to set these properties with the correct values.
public class EntitySaveOrUpdateEventListener : IPreInsertEventListener, IPreUpdateEventListener
{
public bool OnPreInsert(PreInsertEvent @event)
{
var hasType = (@event.Entity.GetType().GetInterface(typeof(IVersionable).Name, true) != null);
if (!hasType)
return false;

var now = DateTime.Now;
var createdOnIndex = new List<string>(@event.Persister.PropertyNames).FindIndex(x => x == "CreatedOn");
@event.State[createdOnIndex] = now;
SetPrivateVariable("CreatedOn", now, @event.Entity);

var lastModifiedIndex = new List<string>(@event.Persister.PropertyNames).FindIndex(x => x == "LastModified");
@event.State[lastModifiedIndex] = now;
SetPrivateVariable("LastModified", now, @event.Entity);

var versionIndex = new List<string>(@event.Persister.PropertyNames).FindIndex(x => x == "Version");
var version = GetPrivateVariable<int?>("Version", @event.Entity);

version = version.GetValueOrDefault() + 1;
@event.State[versionIndex] = version;
SetPrivateVariable("Version", version, @event.Entity);

return false;
}

public bool OnPreUpdate(PreUpdateEvent @event)
{
var hasType = (@event.Entity.GetType().GetInterface(typeof(IVersionable).Name, true) != null);
if (!hasType)
return false;

var now = DateTime.Now;
var lastModifiedIndex = new List<string>(@event.Persister.PropertyNames).FindIndex(x => x == "LastModified");
@event.State[lastModifiedIndex] = now;
SetPrivateVariable("LastModified", now, @event.Entity);

var versionIndex = new List<string>(@event.Persister.PropertyNames).FindIndex(x => x == "Version");
var version = GetPrivateVariable<int?>("Version", @event.Entity);

version = version.GetValueOrDefault() + 1;
@event.State[versionIndex] = version;
SetPrivateVariable("Version", version, @event.Entity);
return false;
}

public void SetPrivateVariable(string name, object value, object entity)
{
var entityType = entity.GetType();

var pi = entityType.GetProperty(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
pi.ReflectedType.BaseType.InvokeMember(name,
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.SetProperty | BindingFlags.Instance,
null,
entity,
new[] { value });
}

public T GetPrivateVariable<T>(string name, object entity)
{
var entityType = entity.GetType();

var pi = entityType.GetProperty(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

return (T)pi.GetValue(entity, null);
}
}

Now if you follow this code, when an entity is saved for the first time then the 'CreatedOn' & 'LastModified' property values are set to the current date & time and the 'Version' property is incremented (the initial value is 0) via the private backing properties. When the entity is updated only the 'LastModified' & 'Version' properties are set via the private backing properties.

This event listener is then instantiated via the fluent NH config.
private static ISessionFactory CreateSessionFactoryImpl()
{
return Fluently.Configure()
.Database(MsSqlConfiguration.MsSql2005.ConnectionString(c => c.FromConnectionStringWithKey("Pronunciations")).ShowSql())
.Mappings(m => m.FluentMappings.AddFromAssemblyOf<IMap>())
.ExposeConfiguration(c =>
{
c.EventListeners.PreInsertEventListeners = new IPreInsertEventListener[] { new EntitySaveOrUpdateEventListener() };
c.EventListeners.PreUpdateEventListeners = new IPreUpdateEventListener[] { new EntitySaveOrUpdateEventListener() };
})
.BuildSessionFactory();
}



Awkward Coder
Email ThisBlogThis!Share to XShare to FacebookShare to Pinterest
Posted in nHibnerate Entities Events Listeners | No comments
Newer Post Older Post Home

0 comments:

Post a Comment

Subscribe to: Post Comments (Atom)

Popular Posts

  • Unit testing Rx methods Timeout & Retry with moq
    Earlier this week I was trying to unit test an asynchronous service (Foo) which used another asynchronous service (Bar) internally and ran i...
  • Understanding RefCount in Reactive Extensions
    A couple of weeks ago  @LordHanson  & I ran into an issue converting a stateless async service exposed as an Rx cold observable to a  co...
  • StructureMap: ILifecycle
    The other day I wanted to control the scope of a service inside a web based app with semantics which didn't fit either 'HttpContextS...
  • MVVM anti-pattern: Injecting the IoC container into a View Model
    This is another anti-pattern I've seen a lot recently, the dynamic use of the IoC container inside a view model to resolve child view mo...
  • How many pins can Bing Maps handle in a WP7 app - part 1
    part2 -  http://awkwardcoder.blogspot.com/2011/10/how-many-pins-can-bing-maps-handle-in.html part3 -  http://awkwardcoder.blogspot.com/2011/...
  • Bad developers love 'The Daily WTF'
    When 'The Daily WTF' started up back in 2003/2004 it was a great laugh looking at shocking code other developers wrote, but after a ...
  • Using CompositeDisposable in base classes
    To help make an object eligible for collection by the GC (garbage collector) one would implement the IDisposable interface. Executing the di...
  • Implementing a busy indicator using a visual overlay in MVVM
    This is a technique we use at work to lock the UI whilst some long running process is happening - preventing the user clicking on stuff whil...
  • Daily Dilbert Service - the most important service I've ever written...
    NuGet package available here ... First off a big shout to  @hamish  &  @leeoades  on this one - I'm just blogging about it. At work ...
  • Comparing performance of .Net 4.5 to .Net 4.0 for WPF
    Currently I'm working on a .Net 4.0 WPF app and we've had some discussion about moving to .Net 4.5, we don't get to make the dec...

Categories

  • .Net
  • .Net 4.5
  • Abstractions
  • Advertising
  • Agile
  • Agile Courage
  • AOP
  • Async
  • automated testing
  • Azure
  • Azure IIS RESTful development
  • BDD
  • Bing Maps
  • Bounded Context
  • C#
  • C# 5.0
  • Caching
  • Chocolatey
  • CLoud
  • CodePlex
  • Coding
  • Coding Building CI Testing
  • Coding C#
  • coding C# IoC StructureMap
  • Coding Functional-Programming
  • Coding REST Knowledge
  • Coding Services
  • Coding TDD Refactoring Agile
  • Command
  • continuous testing
  • coupling
  • CultureInfo
  • DAL
  • databases
  • DDD
  • DDD Coaching
  • DDD Domain Events Auditing nHibernate
  • DDD Entities Value Objects
  • Debugging
  • Design Patterns
  • Design Patterns Databases Auditing
  • Developement
  • Development
  • Development Coding
  • Development Process
  • Development unit testing
  • Development VS 2011
  • Diagnostics
  • Disposable
  • Exceptions
  • FINDaPAD
  • FindaPad Property Rental Windows Phone 7 Mobile Devices
  • Fun Coding Duct-Tape
  • Hotfixes
  • integration testing
  • IoC
  • jasmine
  • javascript
  • Jobs Development
  • LINQ
  • marketplace
  • Mobile Devices
  • Mocking
  • MSDN Coding
  • MSpec
  • Multilingual
  • MVC
  • MVVM
  • nCrunch
  • nHbiernate Repository Pattern Criteria
  • nHibernate Auditing Design Fluent
  • nHibnerate Entities Events Listeners
  • node.js
  • nodes.js
  • Nokia
  • NoSQL RavenDB Azure Development
  • Observations
  • OO
  • ORM
  • Performance
  • Portable Class Library
  • Portable Library
  • PostSharp
  • Process
  • Rants
  • RavenDB IIS 7.5 Development
  • Reactive
  • Reactive Extension
  • Reactive Extensions
  • ReadOnlyCollections
  • Resharper
  • REST Distributed-Systems
  • REST HTTP
  • rest web
  • RESTful
  • Rx
  • Serialization
  • Silverlight
  • Silverlight Installation
  • Task
  • TDD
  • TDD IoC DI
  • TDD Mocking
  • TDD Team Observation
  • Telerik
  • testing
  • threading
  • TPL
  • UI
  • Undo-Redo
  • unit testing
  • ViewModels
  • VS 2012
  • wcf
  • web api
  • Web Services
  • web services mobile devices data
  • WebAPI
  • Windows
  • Windows 8
  • windows phone
  • Windows Phone 7
  • WP7
  • WP7 Bing Maps Development Network HTTP
  • WP7 Bing Maps Development UK Crime
  • WP7 Bing Maps Development UK Crime Clustering
  • WP7 Bing Maps Development UK Polygons Clustering Performance
  • WP7 cryptography bouncy castle
  • WP7 Cultures C#
  • WP7 feedback development app store
  • WP7 Javascript web browser
  • WP7 MSBuild
  • WP7 ORM Databases performance
  • WP7 Serialisation
  • WP7 SilverlightSerializer C#
  • WP7 sqlite performance development
  • WP7 WP7Contrib Bing Maps Development
  • WP7 WP7Contrib Bing Maps Polygon Development
  • WP7 WP7Contrib CodePlex
  • WP7 WP7Contrib CodePlex Bing Maps Development
  • WP7 WP7Contrib CodePlex ObservableCollection
  • WP7 WP7Contrib ILMerge .Net
  • WP7 WP7Contrib Phone Maps
  • WP7 WP7Contrib SilverlightSerializer C#
  • WP7Contrib
  • WP7Contrib Bing Maps WP7
  • WP7Contrib WP7 Geo-Location development C#
  • WP7Contrib WP7 HTTP Compression
  • WP7Contrib WP7 Url Development Rx
  • WP7Dev
  • WPF
  • WPF Cultures
  • WuApi
  • XAML

Blog Archive

  • ►  2013 (16)
    • ►  November (5)
    • ►  September (3)
    • ►  August (1)
    • ►  July (1)
    • ►  June (3)
    • ►  May (2)
    • ►  January (1)
  • ►  2012 (44)
    • ►  November (2)
    • ►  October (8)
    • ►  September (5)
    • ►  August (2)
    • ►  July (4)
    • ►  June (3)
    • ►  May (1)
    • ►  April (2)
    • ►  March (13)
    • ►  February (4)
  • ►  2011 (52)
    • ►  December (3)
    • ►  November (5)
    • ►  October (7)
    • ►  September (7)
    • ►  August (11)
    • ►  July (4)
    • ►  May (2)
    • ►  April (1)
    • ►  March (5)
    • ►  February (3)
    • ►  January (4)
  • ►  2010 (1)
    • ►  August (1)
  • ▼  2009 (32)
    • ►  December (3)
    • ►  November (7)
    • ▼  October (6)
      • Auditing user actions
      • Unstructured development opportunity
      • Property setters are just wrong!
      • Lambda beats magic string
      • Using repositories inside a domain entity
      • Functional intro
    • ►  September (11)
    • ►  April (1)
    • ►  March (4)
Powered by Blogger.

About Me

Unknown
View my complete profile