Entities dependencies best practices
The question came up in the ALT.Net list, and it is fairly common in the DDD circles as well.
I have an User with Email address, which I want to keep encrypted in the database, but visible to the application. How do I handle this scenario in the entity?
We can do it in our service layer, and encrypt / decrypt that outside the entity, but that has two issues. It couple the entity to the service and it means that we can forget to encrypt the email at some point.It also smells.
Another option, which I call the brute force option, is to pass the dependency to the entity and let it handle its own concerns. Here is implementation:
Yes, it is ugly, and even using this approach you can improve upon it, but let us focus on the interesting tidbits. First, not only does our entity know about an infrastructure service, it is also aware of how to create it. It has to do so, because we have to provide an empty ctor for NHibernate to use.
We can generalize this a bit, leading us to this scenario:
This piece of code is akin to nails on board to me. It isn't how I want to see code written.
A better way exists. We will start by making a slight modification to the User class, we remove the empty ctor. Well, we can't remove it at the moment, there is check in NHibernate for this, which I need to find and remove, so for now, we have this:
Note that this also requires that you'll specify the unsaved value of the identifier in the configuration.
Now that we have established that we have no way of creating the User using the empty ctor, we let NHibernate know how it can create it:
public class EntityDependenciesInterceptor : EmptyInterceptor { public override object Instantiate(string clazz, EntityMode entityMode, object id) { if(entityMode!=EntityMode.Poco) return base.Instantiate(clazz, entityMode, id); if(clazz != typeof(User).FullName) return base.Instantiate(clazz, entityMode, id); return new User(CryptoProvider.Instance); } }
Just for User, we override the way NHibernate create entities. Now we can register it in the session, and we are done:
using (ISession session = sessionFactory.OpenSession(new EntityDependenciesInterceptor())) { IList list = session.CreateCriteria(typeof(User)).List(); }
This is still not something that I particularly like. I don't see encryption as part of the responsibilities of the entity. It is just something we need in order to save encrypted values to the DB. So let make it explicit.
NHibernate has the notion of IUserType, which let you take actions on save / load of values from the DB.
First, we make User POCO again, remove all traces of the previous ugliness. Now, we change the mapping for User so the email property is defined so:
<property name="Email" type="Samples.EncryptedStringUserType, Samples" />
And now we need to create the user type:
public class EncryptedStringUserType : IUserType { public bool Equals(object x, object y) { return object.Equals(x, y); } public int GetHashCode(object x) { return x.GetHashCode(); } public object NullSafeGet(IDataReader rs, string[] names, object owner) { object r = rs[names[0]]; if (r == DBNull.Value) return null; return CryptoProvider.Instance.Decrypt((string) r); } public void NullSafeSet(IDbCommand cmd, object value, int index) { object paramVal = DBNull.Value; if (value != null) paramVal = CryptoProvider.Instance.Encrypt((string) value); IDataParameter parameter = (IDataParameter)cmd.Parameters[index]; parameter.Value = paramVal; } public object DeepCopy(object value) { return value; } public object Replace(object original, object target, object owner) { return original; } public object Assemble(object cached, object owner) { return cached; } public object Disassemble(object value) { return value; } public SqlType[] SqlTypes { get { return new SqlType[]{new StringSqlType()}; } } public Type ReturnedType { get { return typeof(string); } } public bool IsMutable { get { return false; } } }
And that is it, you can now get transparent encryption / decryption when you are going to the DB, at no cost to the application design.
Comments
This is more of a NHibernate question, but why NHibernate requires empty ctor on classes? AFAIK it uses Castle Dynamic Proxy for facilitating lazy loading and other stuff... can't it use Windsor to create objects? Or is it something deeply buried in its design that requires that?
"at no cost to the application design.... "
and
with the cost of spending so much of development time to just do a email address encryption ... very funny!!
I like the solution Ayende ... just one thing that annoys me: I consider user types be part of the Domain Model ... and here, NHibernate is clearly leaking into the Domain Model. While NHibernate usually allows us to have PI in entities, it doesn't give the same level of PI for user types.
Interesting description of the stream of thought! But isn't this more a text on cool NHibernate feature than on loose coupling?
Wouldn't a property accessor be more elegant? That way you can deal with the encryption independently of the data access code.
Side note, but I'm fairly positive the entity constructor doesn't have to be public. Just like serialization scenarios, it just has to exist. Private would work as well, AFAIK.
I have one issue with the final solution.
Why is it acceptable for the user type know about the infrastructure service? Wouldn't it be preferable here to have the service injected, or even access it via the service locator? I know you don't like using the service locator approach, but why not in this case?
At first I wondered what you were doing...
I like the user type approach because the User entity doesn't really care anything about encryption. Encryption is ONLY a persistence concern. Using a user type makes the domain model much more flexible and removes the infrastructure concerns.
What I can't remember is can you still declare the Email property as a string even though you've declared it in the mapping as type EncryptedStringUserType?
What happens when an entity must be serialized?
That is because NH isn't supposed to force you to take dependencies on other infrastructure concerns.
You can use NH without Ioc,
Stiff,
User Types are a bridge between you and NHibernate, they have nothing to do with the domain
Julian,
What you mean, property accessor?
To avoid NHibernate checks for empty constructor you can use private empty ctor instead of public with ugly exception :) this approach is working for me still I don't use lazy loading and proxy mechanisms.
Sneal,
Yes, you can define this as a string.
A guy who don't like IOCs,
Um, what long amount of time are you talking about?
Konstantin ,
It is demonstrating loose coupling using an NHibernate feature.
I am not going to build an OR/M just so I could show how loose coupling works, sorry
John,
IUserType is part of the infrastructure
I was thinking of NHibernate.Properties.IPropertyAccessor.
I wrote a post about it here: http://www.colourcoding.net/Blog/archive/2008/07/23/denormalize-data-using-nhibernates-property-accessors.aspx
The problem, of course, is that then you need to write the property access code again, although that would be ameliorated by inheriting by an NHibernate standard accessor.
That is a way, but IPA is not something that we intend for people to use.
IUserType is
@Ayende
Yes, I know that it does not require me to use IoC.
but what if I don't want to have default ctor?
For example I want my cannonical Customer Entity to have his Name and Address be read-only and as such - I can set them only in the ctor.
I'm aware that there is some way of doing it, but IIUC it is per-entity-type solution and it requires quite a lot of work.
Does it sound reasonable or am I approaching the problem from the wrong angle?
IInterceptor.Instantiate is how you do it, doesn't take a lot of work
What about creating an interceptor that encrypts / decrypts the fields for you? The interceptor could be driven either off of some configuration if you wanted to keep your POCOs clean or an [Encrypt] attribute if you didn't mind putting that sort of thing in your domain model.
Would this be a reasonable solution?
That is not what an interceptor is for.
You could do it at the proxy factory level, though
Interesting, in one project I am using AR and the AR Base and use events hooked in OnFlushDirty/BeforeSave to intercept properties and change their values before something is committed to the database. (I do this without putting these events in the entity classes themselves so it isn't that messy)
I was under the impression that these methods in ARBase were fired via an NH interceptor. Is what I am doing a bad way of doing this?
I would recommend changing that to a user type, yes
Why not make the email address a class of its own with encrypted and decrypted property getters? The NH mapping would map to the encrypted property, the application would access the decrypted one.
This way the behaviour of the email address would be made explicit in the business doamin.
Stefan,
That is an excellent suggestion!
Why doesn't NHibernate have the feature like a typeconverter ? Then you can specify it in the mapping. The value is passed to the type converter by the o/r core the value returned is sent to the db .When the entity is fetched, the value is passed to the type converter and the value returned is placed in the entity.
You can do encryption/decryption but also all kinds of nasty conversions you don't want to deal with at a class level, like 'Y'/N conversions to boolean if you want to work with booleans but have to work with Oracle for example.
If you did what Stefan suggests, wouldn't you still need the ICryptoProvider dependency within an Entity. It would be in the EmailAddress entity instead of the User one.
Frans,
Yes, it has. IUserType :-)
@Ayende
I'm still unsure about this approach. If I understand it correctly from looking at method's signature and your above example all I get is string telling me the name of the type I'm asked to create, it's entity mode and id.
If I have Customer data fetched from DB and I'm about to create one (assuming I have only one ctor: the one that takes CustomerName and Address) I can't create the customer since I don't have those two properties.
The way I understand NHibernate's instantiation works is:
Get data from the DB
Do the IUserType conversions the way you showed in the last example
Create the object with default ctor.
Set fetched data to properties
What I was asking about, is if it's possible to replace step 3 and 4 with other logic, like registering fetched dependencies and properties with Windsor and let it take care of instantiation (and selecting ctor that should be used).
Does it make sense?
Krzysztof,
Yes, that is the way it works, and no, there isn't such option.
consider the case of lazy loading an entity, for example
Hmmmm,
For last 10 minutes i've been trying hard to find a counter argument for that, but the more I thing about it, the more I see that given constraints current design is the best choice available.
Thanks.
And as a sidenote, it's yet another time when you answer a question fully and completely with one few-words sentence and there's not really much more to say after that.
@Krzysztof,
Isn't there an option in the mapping files to tell it to populate an entity using the private backing fields? Mixed with Ayende's solution, would this not give you the behaviour you're looking for?
@Geoff
would that work with readonly fields as well? i mean readonly as in 'c# readonly' keyword, not 'without public setter'. I'm afraid not.
@Krzysztof,
Well, you're right there. :) However, what is the actual desired behaviour you're trying to achieve? Is it that a consumer of the class can't alter the property value or reference? Then relaxing the readonly requirement and adding the private setter might get you the desired result.
@Geoff
Are you suggesting code like this?
public class Customer
{
public Name Name { get; private set; }
private Customer(){} //to satisfy NHibernate
public Customer(Name name) {Name = name; }
}
Yes, this would get the job done, in this case.
However it smells. I don't want to throw buzzwords at you, but now I have to design my entity having limitation of specific persisting framework in mind, whereas it should really be persistence agnostic.
What will the next guy do, when he comes to change the code two years from now, when I'm long gone from that project? This approach simply can't guaranty that he won't change the field somewhere in his new added code, breaking my whole assumptions about it.
Sure I can add NDepend rule to check that the field is modified only from within NHibernate and fail the build but it's really a lot of overhead to satisfy 3rd party framework in my domain model.
No flames please :)
Letting 3rd parties in would really be not a good idea, but maybe extending NHibernate to handle simple cases like that is a goal worth giving a thought? As for lazy loading, well, maybe I don't care about it that much. Maybe I always want to use Name wherever I use Customer so lazy loading does not make sense for it anyway?
Should encryption not be only one way?
Is it not bad practice to have an algorithm that can be decrypted?
Krzysztof ,
Extending NH is the way to go in this scenario
Paul,
One way encryption is fairly useless.
How do I get the email back ?
You cannot get the email back but it is the safest way.
It all depends on the level of security you are after.
You store the users password as encrypted in the database and when they come to log in, you encrypt their password input and check it against the database.
It is not reversible and therefore the safest way.
Saying that SHA1 is no longer deemed safe.
We use SHA256.
Paul,
You seem to be confusing the issue of needing to find a match and needing to get the data.
Password vs. Email is the simple case.
I don't care what your password is, I just need to match it: hash it.
I don't want your email in clear text in my DB, but I still need to access it so I can send you stuff: encrypt it.
What if I wanted to pass in the CryptoProvider instance to the IUserType class via dependency injection instead of through static singleton? Does NHibernate support this?
Or in this particular case, would having the IUserType class have a reference to the dependency container be "ok" practice?
No, NHibernate doesn't support this.
And I consider this an OK solution, because IUserType is part of the infrastructure
Ayende, what are your thoughts on using this approach versus setting up the dependencies in an implementation of IUserRepository?
No issue with that, the UserRepositoryImpl is another infrastructure service
Ayende, are you sure your below method is complete? Looks like it never actually persists anything:
public void NullSafeSet(IDbCommand cmd, object value, int index)
Ah - never mind - reflecting around, I see that the job of this method is just to mutate the IDbCommand appropriately.
Comment preview