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

+0

Я отредактировал мой вопрос! Простите. –

+0

См. Http://stackoverflow.com/questions/1302946/asp-net-mvc-controlling-serialization-with-jsonresult – Jacob

ответ

3

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

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

Таким образом, вам не нужно определить специальный интерфейс, вы можете поместить это право метаданных в вашей модели.


Update: Видимо, это не вариант, потому что класс является производным классом и его членов из (неизменяемо) базового класса, которые должны быть скрыты.

Это является можно динамически генерировать класс, как вы хотите, либо с помощью Emit или динамический прокси-библиотеку, как замок, но это будет очень громоздким.Если вы можете, я очень рекомендую использовать простой прокси-класс, вместо этого:

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

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

Или, если вы действительно не можете иметь дело с поддержанием этого, вы можете создать «общий» прокси: cоздателем экземпляров

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

Это отличный ответ, и я пробовал это, но я забыл упомянуть, что мой класс User расширяет EntityObject, а EntityObject имеет свои собственные члены, которые я не могу фильтровать с помощью ScriptIgnoreAttribute. Спасибо ! –

+0

@Christian - вы считали, что обертывание и делегирование сущности в вашем классе, а не расширение? – TrueWill

+0

@Aaronaught - Ваш пример отлично работает, хотя я думал, что могу принять более прямой подход к этому. Большое спасибо ! Это отличная помощь! –

0

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

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 новый объект, содержащий только те свойства (общие для двух типов). Спасибо ! –

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. Спасибо ! –

+0

Итак, что случилось с вашим решением? return Json (новый {myUser.UserName //}); Это именно так. Вы не отправляете ничего, кроме свойств, которые вы указали в новом {..}. Под капотом, когда вы делаете новый {..}, для вас создается новый объект. – BFree

+0

На самом деле с этим решением что-то не так. Что делать, если у меня есть десятки методов, которые возвращают пользователей с новыми {...}, и я хочу добавить нового пользователя в User, который должен быть отправлен через JSON? Затем я должен перейти к каждому методу и изменить новый оператор {...}, чтобы он соответствовал новому члену. Это не продуктивно. Так что это не решение ... –

Смежные вопросы