我有一个模特
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
答案 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;
是的,这不是一个新对象,但无论如何你需要一个新的对象?