2010-01-10 102 views
0

成員我有一個模型C#:篩選對象

public class User : EntityObject { 
    public int Id { get; set; } 

    public string Username { get; set; } 
    public string Password { get; set; } 
} 

和我想返回給客戶端僅包含用戶的用戶名和密碼成員JSON結果。

我目前做這個,像這樣

return Json(new { MyUser.Username, MyUser.Password }); 

我希望能夠有一個接口

public interface ClientAvailableUser { 
    string Username { get; set; } 
    string Password { get; set; } 
} 

,並用它來知道該怎麼回客戶端

如何使用inte rface ClientAvailableUser從用戶創建一個新的對象,只有來自用戶的成員也出現在界面中?

User MyUser = new User(); 

// MyUser has an Id, Username and Password 

Object FilteredMyUser = // Filter MyUser using the ClientAvailableUser interface so that 

// FilteredMyUser has only Username and Password according to ClientAvailableUser interface 
+1

你的問題是什麼? – jason 2010-01-10 19:44:23

+0

我編輯了我的問題!對不起。 – 2010-01-10 19:52:38

+0

查看http://stackoverflow.com/questions/1302946/asp-net-mvc-controlling-serialization-with-jsonresult – Jacob 2010-01-10 19:53:38

回答

3

另一種選擇(和我個人的pereference)是簡單地把System.Web.Script.Serialization.ScriptIgnoreAttribute上,你不希望序列化的模型類的成員(或創建一個隱式轉換DTO類做同樣的事情) 。

例:

using System.Web.Script.Serialization; 

public class User 
{ 
    [ScriptIgnore] 
    public int ID { get; set; } 

    public string Username { get; set; } 
    public string Password { get; set; } 
} 

這樣,你並不需要定義一個特殊的接口,你可以把這個元數據就在你的模型。


更新:顯然,這不是一種選擇,因爲類是一個派生類,它是從應該被隱藏的(不可修改)基類成員。

可能以您想要的方式動態生成類,可以使用Emit或動態代理庫(如Castle),但這會非常麻煩。如果可以的話,我真的建議使用一個簡單的代理類,而不是:

public class UserResult 
{ 
    public UserResult(User user) 
    { 
     Username = user.Username; 
     Password = user.Password; 
    } 

    public string Username { get; set; } 
    public string Password { get; set; } 
} 

或者,如果你真的不能保持這種處理,你可以建立一個「通用」代理實例化:

static class ProxyInstantiator 
{ 
    public static TProxy CreateProxy<TProxy>(object source) 
     where TProxy : new() 
    { 
     TProxy proxy = new TProxy(); 
     CopyProperties(source, proxy); 
     return proxy; 
    } 

    protected static void CopyProperties(object source, object dest) 
    { 
     if (dest == null) 
     { 
      throw new ArgumentNullException("dest"); 
     } 
     if (source == null) 
     { 
      return; 
     } 
     Type sourceType = source.GetType(); 
     PropertyInfo[] sourceProperties = 
      sourceType.GetProperties(BindingFlags.Instance | BindingFlags.Public); 
     Type destType = dest.GetType(); 
     PropertyInfo[] destProperties = 
      destType.GetProperties(BindingFlags.Instance | BindingFlags.Public); 
     var propsToCopy = 
      from sp in sourceProperties 
      join dp in destProperties on sp.Name equals dp.Name 
      select new { SourceProperty = sp, DestProperty = dp }; 
     foreach (var p in propsToCopy) 
     { 
      object sourceValue = p.SourceProperty.GetValue(o, null); 
      p.DestProperty.SetValue(dest, sourceValue, null); 
     } 
    } 
} 

然後,你可以寫一個簡單的代理(不是接口):

public class UserResult 
{ 
    public string Username { get; set; } 
    public string Password { get; set; } 
} 

而且在這樣的控制器方法調用它:

​​

謹慎的關於這個字:

這並不考慮到索引的屬性,如果有任何這些將失敗。它沒有考慮到「深層複製」 - 如果你的源類包含引用類型,它只會複製引用 - 也許這就是你想要的,也許它不是。

就個人而言,我會採取前一種方法,只是建立個別代理類而不使用通用代理,因爲如果我犯了一個錯誤,我寧願編譯時錯誤超過運行時錯誤。但你問,所以你去!

+0

這是一個很好的答案,我嘗試了這一點,但我忘了提及我的User類擴展了一個EntityObject,而EntityObject有他自己的成員,我無法使用ScriptIgnoreAttribute進行過濾。謝謝 ! – 2010-01-10 20:05:11

+0

@Christian - 你有沒有考慮包裝和委託給你的班級實體,而不是擴展它? – TrueWill 2010-01-10 20:24:26

+0

@Aarounaught - 你的榜樣效果很好,但我認爲我可以採取更直接的方法來做到這一點。非常感謝你 !這非常有幫助! – 2010-01-10 20:42:44

0

像這樣的東西會給你的屬性列表中的對象具有相同的名稱作爲界面歡迎使用屬性(沒有編譯這個還,但它應該是接近)。這應該讓你開始。

public IEnumerable<PropertyInfo> GetPropertiesToTransfer(User user); 
{ 
    Type userType = user.GetType(); 
    Type clientAvailableType = typeof(ClientAvailableUser); 

    PropertyInfo[] userProps = userType.GetProperties(); 
    IEnumerable<string> caUserProps = clientAvailableType.GetProperties().Select(p => p.Name); 

    return userProps.Where(p => caUserProps.Contains(p.Name)); 
} 
+0

這使我獲得了User和ClientAvailableUser接口通用的屬性,但我想從User對象創建一個僅包含那些屬性(這兩種類型通用)的新對象。謝謝 ! – 2010-01-10 20:00:53

0

我真的不知道我得到的「爲什麼」你正在試圖做你在做什麼,但你可以做一些事情:

public interface IClientAvailableUser 
{ 
    string Username { get; set; } 
    string Password { get; set; } 
} 

internal class ConcreteClientAvailableUser : IClientAvailableUser 
{ 
    public string UserName{get;set;} 
    public string Password{get;set;} 
} 

public class UserExtensions 
{ 
    public IClientAvailableUser AsClientAvailableUser(this User user) 
    { 
     return new ConcreteClientAvailableUser { UserName = user.UserName, Password = user.Password}; 
    }   
} 

然後,你可以這樣做:

IClientAvailableUser ica = myUser.AsClientAvailableUser(); 

但我不明白爲什麼你的用戶類不能只是直接實現你的接口,然後你可以做:

IClientAvailableUser ica = myUser; 

是的,這不是一個新對象,但是你需要什麼新對象?

+0

我想這樣做是因爲我的用戶對象包含許多成員(如Id,EntityKey,EntityState),我不想將它們發送到我的Json結果中。你的解決方案可以正常工作,除非我在很多地方都有我的成員,而我只是想使用ClientAvailableUser接口過濾用戶。謝謝 ! – 2010-01-10 20:32:15

+0

那麼你的解決方案有什麼問題?返回Json(new {myUser。用戶名 //});這正是要做到這一點的方式。除了在新的{..}中指定的屬性之外,您不會發送任何內容。在引擎蓋下,當你做新的{..}一個新的對象正在爲你創建。 – BFree 2010-01-10 20:53:01

+0

實際上這個解決方案有問題。如果我有幾十種方法返回用戶新的{...},並且我想添加一個新的成員給應該通過JSON發送的用戶?比我必須去每種方法,並更改新的{...}語句,以適應新的成員。這不是生產力。所以這不是一個解決方案... – 2010-01-10 21:15:20