2010-01-10 98 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