C #: фильтровать элементы объекта - PullRequest
0 голосов
/ 10 января 2010

У меня есть модель

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 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 ]

3 голосов
/ 10 января 2010

Другой вариант (и моя личная ссылка) заключается в том, чтобы просто поместить System.Web.Script.Serialization.ScriptIgnoreAttribute в члены класса модели, которые вы не хотите сериализовать (или создать неявно-конвертируемый класс DTO для того же). 1002 *

Ex:

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

Предупреждение об этом:

Это не учитывает индексированные свойства и завершится ошибкой, если таковые имеются. Он не учитывает «глубокое копирование» - если ваш исходный класс содержит ссылочные типы, он будет только копировать ссылки - возможно, это то, что вы хотите, а может и нет.

Лично я бы выбрал первый подход и просто создавал отдельные прокси-классы без универсального прокси, потому что, если я сделаю ошибку, я бы предпочел ошибку времени компиляции, чем ошибку времени выполнения. Но вы спросили, так что вы идете!

0 голосов
/ 10 января 2010

Я действительно не уверен, что понимаю, почему вы пытаетесь делать то, что делаете, но вы можете сделать несколько вещей:

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 голосов
/ 10 января 2010

Примерно так получится список свойств объекта с тем же именем, что и в интерфейсе интерфейса (еще не скомпилировано, но оно должно быть близко). Это должно начать вас.

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 ) );
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...