Eastrall / EntityFrameworkCore.DataEncryption

A plugin for Microsoft.EntityFrameworkCore to add support of encrypted fields using built-in or custom encryption providers.
MIT License
326 stars 54 forks source link

Data migration #15

Closed Eastrall closed 3 years ago

Eastrall commented 3 years ago

This PR adds a simple data migration mechanism that allows the following migration types:

Before using the DataMigrator, please ensure you have a backup of you database. For all migration tools, a database backup should be done before executing any migration operation.

Covers issue #7

RichardD2 commented 3 years ago

Wouldn't it be simpler to create a MigrationEncryptionProvider, which takes a source and destination provider - either of which can be null - and delegates calls to them?

Eg:

public class MigrationEncryptionProvider : IEncryptionProvider
{
    public MigrationEncryptionProvider(IEncryptionProvider sourceProvider, IEncryptionProvider destinationProvider)
    {
        SourceProvider = sourceProvider;
        DestinationProvider = destinationProvider;
    }

    public IEncryptionProvider SourceProvider {  get; }
    public IEncryptionProvider DestinationProvider { get; }

    public string Decrypt(string dataToDecrypt) => SourceProvider is null ? dataToDecrypt : SourceProvider.Decrypt(dataToDecrypt);
    public string Encrypt(string dataToEncrypt) => DestinationProvider is null ? dataToEncrypt : DestinationProvider.Encrypt(dataToEncrypt);
}

It's then simply a case of loading the entities in a DbContext using that provider, marking the encrypted properties as modified, and saving the changes.

public static void Migrate(DbContext context)
{
    foreach (var entityType in context.Model.GetEntitiyTypes())
    {
        var encryptedProperties = entityType.GetProperties().Where(p => ShouldEncrypt(p)).ToList();
        if (encryptedProperties.Count == 0) continue;

        IQueryable<object> entities = Set(context, entityType.ClrType);
        foreach (object entity in entities)
        {
            var entry = context.Entry(entity);
            foreach (var property in encryptedProperties)
            {
                entry.Property(property.Name).IsModified = true;
            }
        }
    }

    context.SaveChanges();
}

This covers all of your current cases, and allows for migrations between other providers as well.