Using Entity structure code first with IOC container to create objects without default constructor
I want to create an entity framework code of the first datalayer that encrypts some of its properties. To do this, when creating an instance with data loaded from the database, enter ICryptographer
.
Is it possible to inject ICryptographer
with an IOC container 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");
}
Assembly of shared objects: (with internally 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); }
}
}
source to share
This is a very contrived example, but may lead you in the right direction.
We need a way to know when to inject a property into the entity class, so they are marked with
public interface ICryptographerUser {
ICryptographer Cryptographer { get; set; }
}
Where is ICryptographer
defined as
public interface ICryptographer {
string Decrypt(string value);
string Encrypt(string value);
}
The instance to 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 use Factory
that is aware of the fact that we need to inject properties (you can use IOC for 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 for adding 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 requested from Context
, the following code will inject the objects, but this is done after their respective properties have 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();
}
}
Since the instance will only be injected after it has Entity
been materialized, I needed to change your definition Entity
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 it AccessKey
has private set { _accessKey = value; }
one that allows EF
you to set the property when it materializes object
, but when you set the property value, you have to call public void SetAccessKey(string value)
that uses the Cryptographer
field to encrypt.
source to share