Reputation: 1192
I want to build an entity framework code first datalayer that encrypts some of its properties. For that an ICryptographer
should be injected when creating an instance with data loaded from the database.
Is it possible to inject ICryptographer
using a IOC Container such like unity?
DAL example:
public class context : DbContext
{
public DbSet<Credentials> Credentials {get;set;}
}
public CredentialsConfiguration()
{
ToTable("Profiles");
HasKey(p => p.ProfileName);
Ignore(p => p.SecretKey);
Ignore(p => p.AccessKey);
Property(p => p._accessKey)
.HasColumnName("AccessKey");
Property(p => p._secretKey)
.HasColumnName("SecretKey");
}
Common Entities Assembly: (with internals visible to DAL)
public class Credentials
{
private readonly ICryptographer _cryptographer;
public Credentials(ICryptographer cryptographer)
{
_cryptographer = cryptographer;
}
internal string _accessKey { get; set; }
public string AccessKey
{
get { return _cryptographer.Decrypt(_accessKey); }
set { _accessKey = _cryptographer.Encrypt(value); }
}
}
Upvotes: 3
Views: 1159
Reputation: 2530
This is a very contrived example but might push you into the right direction
We need a way to know when to inject a property into the entity class, as such they are marked using
public interface ICryptographerUser {
ICryptographer Cryptographer { get; set; }
}
Where ICryptographer
is defined as
public interface ICryptographer {
string Decrypt(string value);
string Encrypt(string value);
}
The instance that will be injected is defined as
public class Cryptographer : ICryptographer {
public string Decrypt(string value) {
return "Decrypted";
}
public string Encrypt(string value) {
return "Encrypted";
}
}
When adding an entity we need to make use of a Factory
that is cognizant of the fact that we need to inject properties (You can make use of IOC to do this)
public static class EntityFactory {
public static T CreateInstance<T> () {
var entity = Activator.CreateInstance<T>();
if (entity is ICryptographerUser) {
//INJECT INSTANCE HERE
(entity as ICryptographerUser).Cryptographer = new Cryptographer();
}
return entity;
}
}
Now to add and entity we can just use
var entity = EntityFactory.CreateInstance<Credentials>();
entity.SetAccessKey("123");
entity.SecretKey = "456";
entity.ProfileName = "a";
contect.Set<Credentials>().Add(entity);
When objects are queried from Context
the following code will inject the objects, but this is done after their respective properties has already been set
public MyContext() {
IObjectContextAdapter objectContextAdapter = (this as IObjectContextAdapter);
objectContextAdapter.ObjectContext.ObjectStateManager.ObjectStateManagerChanged += ObjectStateManager_ObjectStateManagerChanged;
}
private void ObjectStateManager_ObjectStateManagerChanged(object sender, System.ComponentModel.CollectionChangeEventArgs e) {
// we are only interested in entities that
// have been added to the state manager
if (e.Action != CollectionChangeAction.Add)
return;
IObjectContextAdapter objectContextAdapter = (this as IObjectContextAdapter);
var state = objectContextAdapter.ObjectContext.ObjectStateManager.GetObjectStateEntry(e.Element).State;
// we are only interested in entities that
// are unchanged (that is; loaded from DB)
if (state != EntityState.Unchanged)
return;
OnObjectMaterialized(e.Element);
}
private void OnObjectMaterialized(object e) {
if (e is ICryptographerUser) {
//INJECT INSTANCE HERE
(e as ICryptographerUser).Cryptographer = new Cryptographer();
}
}
As the instance will only be injected after the Entity
has been materialized I needed to modify your Entity
definition as follows
public class Credentials : ICryptographerUser {
public string ProfileName { get; set; }
internal string _secretKey { get; set; }
internal string _accessKey { get; set; }
public string SecretKey { get; set; }
public string AccessKey {
get { return _accessKey; }
private set { _accessKey = value; }
}
public string AccessKeyDecrypted {
get { return Cryptographer.Decrypt(_accessKey); }
}
public void SetAccessKey(string value) {
_accessKey = Cryptographer.Encrypt(value);
}
public ICryptographer Cryptographer { get; set; }
}
Note that AccessKey
has private set { _accessKey = value; }
which allows EF
to set the property when the object
materializes but when you set the property value you must call public void SetAccessKey(string value)
which makes use of Cryptographer
to encrypt the field.
Upvotes: 2