2016-10-14 12 views
2

樣品輸入:C#的LINQ替代的lodash省略功能

// remove sensitive data  
var user = db.Users.Where(x => x.Id == '123').ToList() 
     .Omit("PasswordHash", "PasswordSalt", "SecretDataAsInteger"); 

輸出:

{ 
    FirstName: "...", 
    LastName: "...", 
    PasswordHash: "", // default value instead of real value 
    PasswordSalt: "", 
    SeretDataAsInteger: 0 
} 

類似: https://lodash.com/docs/4.16.4#omit

首:

  • 我喜歡的解決方案w^ithout選擇一個新的對象,並設置每個屬性除了我想省略即

var user = ..Users.Select(x => new User {/* here I set each property */})

,因爲這會導致更新所有參考的,萬一我已經更新了User模特屬性後

  • 傳遞對象中的omit函數而不是屬性名稱作爲字符串也是可接受的
+1

什麼這個問題到底是什麼?如何實現'省略'方法? – Codor

+0

@JonSkeet我不認爲omit方法會修改任何東西,我認爲它基本上是「給我所有沒有這些指定屬性的對象」,這對排除密碼字段或我認爲的敏感數據很有用。 – DavidG

+0

@DavidG:就我能說的而言,這不是關於*對象*,而是*屬性*的問題,所以,它仍然可以作爲與「選擇」等價的方式來完成,但是這就是比'Where' ... –

回答

1

Belo w是我爲你創建的一個類,它應該使用提供的屬性名稱列表將它們設置爲Property類型的默認值。使用你的例子:

// remove sensitive data  
var user = db.Users.Where(x => x.Id == '123').ToList() 
    .OmitProperties("PasswordHash", "PasswordSalt", "SecretDataAsInteger"); 

請記住,包括System.Reflection

public static class EnumerableExtensions { 
     public static IEnumerable<T> OmitProperties<T>(this IEnumerable<T> enumerable, params string[] propertyNames) { 
      PropertyInfo[] propertiesToOmit = typeof(T).GetProperties() 
       .Where(p => propertyNames.Contains(p.Name)) 
       .ToArray(); 

      foreach (var item in enumerable) { 
       foreach (var property in propertiesToOmit) { 
        property.SetValue(item, null); 
       } 

       yield return item; 
      } 
     } 
    } 
+0

我沒有寫高級C#一段時間,我想問一下:是不是用這種方式反射極其緩慢?例如,如果我寫這樣的東西,而不是'db.Users.Where(x => x.Id == 123).Select(x => new User {Name = x.Name,Id = x.Id}) ' - (手動省略),會不會更快? (雖然沒有回答答案...) –

+0

我同意手動省略屬性而不是使用它會更快。然而,在創建'User'類的新實例時,每次可以佔用更多的內存(在GC或對象創建中)並且可能導致它運行得更慢。取決於答案。 – Luke

+0

@LukeFisher創建'User'類的新實例或匿名類型的新實例很便宜:它可以降低原始'User'對象的成本,因爲這會在沒有任何引用的情況下變爲短暫的。 GC非常善於擺脫這些問題。肯定比爲每個用戶使用反射便宜。 – hvd

1

如果你有過User類控件,您可以添加一個構造採取User。使這個構造函數複製所有的字段。你可以使用反射來做到這一點。如果您通過動態生成一個可以完成您所需工作的幫助程序方法來實現此操作,則只需要生成一次幫助程序方法。鑑於此構造函數,你可以寫

var user = db.Users.Where(x => x.Id == 123).AsEnumerable() 
    .Select(x => new User(x) { 
    PasswordHash = "", 
    PasswordSalt = "", 
    SecretDataAsInteger = 0 
    }).ToList(); 

這使得它的類型安全:你可以不小心忽略不具有User特性(比如,你試圖忽略PasswordSlat而非PasswordSalt),你可以不小心忽略通過將它們設置爲屬性類型不支持的值(例如,試圖通過將其設置爲null來嘗試省略SecretDataAsInteger),並且無論如何將嘗試這樣做將被編譯器標記,而不是在運行時爆炸。

+0

感謝提到這一點,但在我的情況寧願不爲此創建一個ctor。 – amd

+0

如果您穿過安全邊界,應該有一個全新的對象,而不僅僅是一個構造函數。 – Stilgar

1

這是一個不是Linq的選項,它實際上更新了基礎對象。可能不是很你想要的,但它的工作:

public static IEnumerable<T> ApplyToAll<T>(this IEnumerable<T> sequence, 
    params Action<T>[] action) 
{ 
    foreach (var element in sequence) 
    { 
     foreach (var action in actions) 
     { 
      omission(action); 
     } 
     yield return element; 
    } 
} 

強似屬性的字符串名稱和,您在輸入行爲的一些行動傳遞:

var redactedUsers = users.ApplyToAll(
    u => u.Password = "", 
    u => u.Name = "Hello " + u.Name); 
+0

感謝提到這一點,但在這種情況下,我更願意將它命名爲'應用'或別的東西,而不是'省略' – amd

+0

其實你是對的,一個更好的名字是或可能'ApplyToAll' – DavidG