views:

157

answers:

3

I have a model

public class User : EntityObject {
    public int Id { get; set; }

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

and I would like to return to the client a JSON result containing only the Username and Password members of the User.

I am currently doing this like so

return Json(new { MyUser.Username, MyUser.Password });

But I would like to be able to have an interface

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

and use it to know what to return to the client

How can I use interface ClientAvailableUser to create a new object from User that has only the members from the User also present in the interface ?

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
A: 

Something like this will give you a list of properties in object with the same name as a propety in the interface (haven't compiled this yet, but it should be close). This should get you started.

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 ) );
}
BioBuckyBall
That gets me the properties that are common to the User and the ClientAvailableUser interface, but I would like to create from an User object a new object containing only those properties (common to the two types). Thank you !
Christian Toma
+1  A: 

Another option (and my personal pereference) would be to simply put System.Web.Script.Serialization.ScriptIgnoreAttribute on the members of the model class that you don't want serialized (or create an implicitly-convertible DTO class to do the same thing).

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

That way you don't need to define a special interface, you can put this metadata right in your model.


Update: Apparently that's not an option because the class is a derived class and it's members from the (unmodifiable) base class that should be hidden.

It is possible to dynamically generate a class the way you want, either using Emit or a dynamic proxy library like Castle, but it's going to be very cumbersome. If you can, I would really recommend to use a simple proxy class instead:

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

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

Or, if you really can't deal with maintaining this, you can build a "generic" proxy instantiator:

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

Then you can write a simple proxy class (not interface):

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

And invoke it in a controller method like this:

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

A word of caution about this:

This does not take into account indexed properties and will fail if there are any of those. It does not take into account "deep copying" - if your source class contains reference types, it will only copy the references - maybe that's what you want, maybe it isn't.

Personally, I'd take the former approach and just build individual proxy classes without the generic proxy, because if I make a mistake, I'd prefer a compile-time error over a runtime error. But you asked, so there you go!

Aaronaught
This is a great answer and I tried this, but I forgot to mention that my User class extends an EntityObject and the EntityObject has his own members which I cannot filter using the ScriptIgnoreAttribute. Thank you !
Christian Toma
@Christian - have you considered wrapping and delegating to the entity in your class rather than extending it?
TrueWill
@Aaronaught - Your example works great, though I thought I could take a more direct approach to this. Thank you very much ! This is great help !
Christian Toma
For the sake of completeness I feel like I should also mention AutoMapper: http://www.codeplex.com/AutoMapper (This might be the "more direct" approach you're looking for)
Aaronaught
A: 

I'm really not sure I get the "why" you're trying to do what you're doing, but you can do a number of things:

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

Then you can just do this:

IClientAvailableUser ica = myUser.AsClientAvailableUser();

But I don't understand why your user class can't just implement your interface directly, and then you could do:

IClientAvailableUser ica = myUser;

Yes, this isn't a NEW object, but what do you need a new one for anyway?

BFree
I want to do this because my User object contains a number of members (like Id, EntityKey, EntityState) that I don't want to send along my Json result. Your solution works fine, except I'll have my members in many places and I just want to filter the User using the interface ClientAvailableUser. Thank you !
Christian Toma
So then what's wrong with your solution? return Json(new {myUser.UserName //}); That's exactly the way to do it. You're not sending anything other than the properties you specify in the new {..}. Under the hood, when you do new {..} a new object is being created for you.
BFree
In fact there is something wrong with that solution. What if I have dozens of methods that return users with new { ... }, and I want to add a new member to User that should be sent trough JSON ? Than I must go to each method and change the new { ... } statement to fit the new member. It's not productive. So it's not a solution ...
Christian Toma