开发者

Need to intercept all LINQ-to-SQL entities with Castle Dynamic Proxy, perhaps with AutoMapper

开发者 https://www.devze.com 2023-01-23 02:43 出处:网络
I have a need to encrypt the value of a specific set of fields when stored in the DB. I am using LINQ-to-SQL.

I have a need to encrypt the value of a specific set of fields when stored in the DB. I am using LINQ-to-SQL.

My approach: To transparently encrypt the value of the matching properties in the entity before it is written to the DB.

I have already written a interceptor with Castle Dynamic Proxy that will encrypt the relevant property on the setter and decrypt it on the getter. Here is how I use it:

var secretEntity = <Get a SecretEntity from the DataContext>;
ProxyGenerator pg = new ProxyGenerator();
// (Typing from memory here, so excuse possible method errors, but you get the gist)
// Reassign to now reference the dynamic proxy.
secretEntity  = pg.CreateProxyWithTarget (secretEntity , new EncryptionInterceptor());
secretEntity.BigSecret = "Silentium"; // Does the encryption via the interceptor.
var decryptedSecret = secretEntity.BigSecret; // Does the decryption  via the interceptor.

Now this all works great, but I don't want to have to manually wrap each SecretEntity instance in a dynamic proxy. So I am looking for a way to automate this, such that when ever I get an instance of SecretEntity, it is already wrapped in the proxy.

Is there a way to do this by somehow hooking into the LINQ-to-SQL DataContext, so that it gives back proxies?

I am using MVC, so I use view models to display my data and I use AutoMapper to map back and forth between the entities and the view models. So I was thinking, if the LINQ-to-SQL DataContext approach does not work, perhaps it would be possible to hook into the mapping routine and wrap the entities in proxies before they are mapped to the view models. So I was very excited to find a method called BeforeMap when using AutoMapper. So I tried

.BeforeMap ((se, sevm) => se = pg.CreateProxyWithTarget (se, new EncryptionInterceptor()));
// (Again, typing from memory).

But no luck. I assume it is because reassigning the "se" reference to the proxy has no effect once the BeforeMap method has finished running.

When creating a new SecretEntity, I could automate the proxy wrapping process by using Ninject, but Ninject will not work on the existing entities I get back from the DataContext.

My walk with Castle Dynamic Proxy consists only of a couple of hours, and AutoMapper doesn't know me much better. So I'm hoping somebody can give me a quick pointer where to look.

Thank you.

Edit

For completeness sake, I thought I'd add the implementation of my interceptor for those who might be interested. Not knowing Castle Dynamic Proxy that well, I'm sure there might be a better way of handling the interception and detecting whether it is a getter or a setter, etc. Here it follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Castle.DynamicProxy;
using TunedIn.Base.Model;
using TunedIn.Base.Ninject;
using TunedIn.Base.Extensions.StringExte开发者_StackOverflow中文版nsions;

namespace TunedIn.Base.Encryption
{
    public class PropertyEncryptionInterceptor : IInterceptor
    {
        public void Intercept (IInvocation invocation)
        {
            IPersonEncryptedFields p = invocation.InvocationTarget as IPersonEncryptedFields;
            if (p == null)
                throw new ApplicationException ("{0} expects the InvocationTarget of the dynamic proxy binding to implement {1}, but {2} does not.".FormatI (typeof (PropertyEncryptionInterceptor).FullName, typeof (IPersonEncryptedFields).FullName, invocation.InvocationTarget.GetType ().FullName));

            if (invocation.Method.Name.StartsWith ("set_"))
            {
                string val = (string)invocation.GetArgumentValue (0);
                val = Kernel.Get<IStringCrypto> ().Encrypt (val);
                invocation.SetArgumentValue (0, val);
                invocation.Proceed ();
            }
            else if (invocation.Method.Name.StartsWith ("get_"))
            {
                invocation.Proceed ();
                string ret = invocation.ReturnValue.ToString ();
                ret = Kernel.Get<IStringCrypto> ().Decrypt (ret);
                invocation.ReturnValue = ret;
            }
            else
                invocation.Proceed ();
        }
    }
}


Perhaps I'm being too simplistic, but wouldn't it be sufficient to just add a new property to the partial SecretEntity that allows encrypting and decrypting? You can make the original property that is generated by LINQ to SQL internal:

public partial class SecretEntity
{
    public string BigSecret
    {
        get { return Decrypt(this.BigSecretInternal); }
        set { this.BigSecretInternal = Encrypt(value); }
    }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消