C#:过滤对象的成员

时间:2010-01-10 19:40:58

标签: c# .net json casting filtering

我有一个模特

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; }
}

并使用它来了解返回客户端的内容

如何使用界面ClientAvailableUser从User创建一个新对象,该对象只有用户的成员也出现在界面中?

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

3 个答案:

答案 0 :(得分:3)

另一个选择(以及我个人的参考)只是简单地将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; }
}

并在这样的控制器方法中调用它:

User someUser = GetSomeUser();
UserResult result = ProxyInstantiator.CreateProxy<UserResult>(someUser);

关于此问题的提醒:

会考虑索引属性,如果存在任何属性,则会失败。它没有考虑“深度复制” - 如果你的源类包含引用类型,它只会复制引用 - 也许这就是你想要的,也许它不是。

就个人而言,我采用前一种方法,只是在没有通用代理的情况下构建单独的代理类,因为如果我犯了错误,我宁愿在运行时错误上发生编译时错误。但是你问,所以你去了!

答案 1 :(得分: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 ) );
}

答案 2 :(得分: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;

是的,这不是一个新对象,但无论如何你需要一个新的对象?