我目前正在涉及現有數據庫中幾列加密的項目中工作。已經有相當多的代碼已經寫入當前模式,其中很多代碼都是以自定義linq-to-sql查詢的形式出現的。查詢的數量在5位數附近,因此修改和重新測試每個人和每個人都會太昂貴。確定LINQ表達式樹中MemberExpression的根對象
我們發現另一種方法是保持DB模式相同--only改變列長度略,這意味着我們不需要改變我們當前的實體類definitions--,而是,改變了表達式樹導通在它們到達l2sql IQueryProvider
之前,並在我需要的列上應用解密函數。我通過使用自定義IQueryable<TEntity>
實現來包裝我的DataContext
的相關Table<TEntity>
屬性來實現此目的,該實現允許我預覽系統中的每個查詢。
在我目前的執行情況,說我有此查詢:
var mydate = new DateTime(2013, 1, 1);
var context = new DataContextFactory.GetClientsContext();
Expression<Func<string>> foo = context.MyClients.First(
c => c.BirthDay < mydate).EncryptedColumn;
,但是當我趕上了查詢,我將其更改爲讀取:
Expression<Func<string>> foo = context.Decrypt(
context.MyClients.First(c => c.BirthDay < mydate).EncryptedColumn);
我做這個使用ExpressionVisitor
類。在VisitMember
方法中,我檢查並查看當前的MemberExpression
是否指向加密列。如果這樣做,我替換表達式的方法調用:
private const string FuncName = "Decrypt";
protected override Expression VisitMember(MemberExpression ma)
{
if (datactx != null && IsEncryptedColumnReference(ma))
return MakeCallExpression(ma);
}
return base.VisitMember(ma);
}
private static bool IsEncryptedColumnReference(MemberExpression ma)
{
return ma.Member.Name == "EncryptedColumn"
&& ma.Member.DeclaringType == typeof(MyClient);
}
private Expression MakeCallExpression(MemberExpression ma)
{
const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public;
var mi = typeof(MyDataContext).GetMethod(FuncName, flags);
return Expression.Call(datactx, mi, ma);
}
datactx
是與在當前的datacontext(我查找在先前通過)指向表達的參考實例變量。
我的問題是,如果我有一個查詢,如:
var qbeClient = new MyClient { EncryptedColumn = "FooBar" };
Expression<Func<MyClient>> dbquery =() => context.MyClients.First(
c => c.EncryptedColumn == qbeClient.EncryptedColumn);
我希望它變成:
Expression<Func<MyClient>> dbquery =() => context.MyClients.First(c =>
context.Decrypt(c.EncryptedColumn) == qbeClient.EncryptedColumn);
,而不是什麼我得到是這樣的:
Expression<Func<MyClient>> dbquery =() => context.MyClients.First(c =>
context.Decrypt(c.EncryptedColumn) == context.Decrypt(qbeClient.EncryptedColumn));
我不想要的,因爲當我有一個內存中的對象時,數據已經是未加密的(此外,我不想要一個討厭的數據庫樂趣ction對我的對象!)
所以,這基本上是我的問題:擁有一個MemberExpression
實例,我如何確定它是指數據庫中的內存對象還是行?
在此先感謝
編輯:
@什洛莫的代碼實際上解決了我貼的情況,但現在的我以前的測試之一得到了破:
var context = new DataContextFactory.GetClientsContext(); Expression<Func<string>> expr = context.MyClients.First().EncryptedColumn; Expression<Func<string>> expected = context.Decrypt( context.MyClients.First().EncryptedColumn); var actual = MyVisitor.Visit(expr); Assert.AreEqual(expected.ToString(), actual.ToString());
在這種情況下,對
EncryptedColumn
的引用不是參數,但訪問者一定要考慮到它!
由於很多@Shlomo你的代碼解決我張貼的情況下,但是實際上打破了我以前的一個測試,請參閱編輯。無論如何,謝謝你,我相信你的回答讓我更加接近一個通用的解決方案。 – alfredochv