Skip to content

jcain/EntityFramework.UserTypes

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

11 Commits
 
 
 
 
 
 
 
 

Repository files navigation

EntityFramework.UserTypes

Use this framework if you want to overcome some of Entity Framework's limitations without leaking too many persistence implementation details into your domain model. This implementation is similar to NHibernate's user types capability. It allows you to create custom types by giving you control over how properties are persisted and materialized.

Note: Only use this feature for properties that you DO NOT need to filter by. The public properties of your model classes are not actually mapped so EF will not know how to generate a WHERE clause for those properties.

Usage Scenarios

  • Column level encryption
  • Persist enums as strings
  • Persist complex types as JSON/XML
  • Persist lists as delimited strings

Configuration

To enable user types, you must add the following to the constructor of your DbContext.

 public class ExampleContext : DbContext
 {
    public ExampleContext()
    {
       this.EnableUserTypes();
    }
}      

To configure user type properties, you can use the built-in extenstion method.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
   modelBuilder.Entity<Customer>()
      .UserTypeProperty<Customer, string, CryptoUserType<Customer, string>>(x => x.Secret);
}

Or create your own extension to improve the readability of your code.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
   modelBuilder.Entity<Customer>().CryptoProperty(x => x.Secret);
}
public static class CryptoUserTypeExtension
{
   public static StringPropertyConfiguration CryptoProperty<TEntity, TValue>(this EntityTypeConfiguration<TEntity> mapper, Expression<Func<TEntity, TValue>> expression, string backingPropertyName = null) where TEntity : class
   {
      return mapper.UserTypeProperty<TEntity, TValue, CryptoUserType<TEntity, TValue>>(expression, backingPropertyName);
   }
}

Model Classes

Your model classes must contain backing properties for each user type property. Unfortunately, this is just a limitation of entity framework, but don't worry, these properties can be marked as private.

 public class Customer
 {
    public CustomerStatus Status { get; set; }
    public string Secret { get; set; }
    public CustomerPreferences Preferences { get; set; }

    #region Backing Fields

    private string StatusBacking { get; set; }
    private string SecretBacking { get; set; }
    private string PreferencesBacking { get; set; }

    #endregion Backing Fields
 }

Implementation

To create a user type, your class must implement the IUserType interface.

public interface IUserType
{
   void OnObjectMaterialized(object entity);
   void OnSavingChanges(object entity);
}

Or your class must derive from the UserTypeBase class. The UserTypeBase class handles most of the work for you and only requires that you implement 2 abstract methods.

public class CryptoUserType<TEntity, TValue> : UserTypeBase<TEntity, TValue> where TEntity : class
{
   public CryptoUserType(string propertyName, string backingPropertyName)
      : base(propertyName, backingPropertyName)
   {
   }

   protected override string GetBackingValue(object targetValue)
   {
      var cipher = Encryption.EncryptRijndael(targetValue.ToString());
      return Convert.ToBase64String(cipher);
   }

   protected override object GetTargetValue(string backingValue)
   {
      byte[] cipher = Convert.FromBase64String(backingValue);
      string targetValue = Encryption.DecryptRijndael(cipher);
      return targetValue;
   }
}

Examples

Column-level encryption.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
   modelBuilder.Entity<Customer>().CryptoProperty(x => x.Secret);
}

Persist enums as strings (not currently possible with EF).

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
   modelBuilder.Entity<Customer>().EnumProperty(x => x.Status).IsRequired().HasMaxLength(15);
}

Persist complex types as JSON.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
   modelBuilder.Entity<Customer>().JsonProperty(x => x.Preferences);
}

About

User types implementation for Entity Framework

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages