2013-05-31 41 views
0

我目前正在涉及現有數據庫中幾列加密的項目中工作。已經有相當多的代碼已經寫入當前模式,其中很多代碼都是以自定義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的引用不是參數,但訪問者一定要考慮到它!

回答

1

代表DB行的A MemberExpression將是​​的後代。內存中的對象不會,它們很可能來自某種形式的FieldExpression

在你的情況,這樣的事情會在大多數情況下(添加一個方法到您的代碼,並修改您的VisitMember方法工作:

private bool IsFromParameter(MemberExpression ma) 
{ 
    if(ma.Expression.NodeType == ExpressionType.Parameter) 
     return true; 

    if(ma.Expression is MemberExpression) 
     return IsFromParameter(ma.Expression as MemberExpression); 

    return false; 
} 


protected override Expression VisitMember(MemberExpression ma) 
{ 
    if (datactx != null && IsEncryptedColumnReference(ma) && IsFromParameter(ma)) 
     return MakeCallExpression(ma); 
    } 

    return base.VisitMember(ma); 
} 
+0

由於很多@Shlomo你的代碼解決我張貼的情況下,但是實際上打破了我以前的一個測試,請參閱編輯。無論如何,謝謝你,我相信你的回答讓我更加接近一個通用的解決方案。 – alfredochv