Saturday, May 12, 2012

Copy specific object properties using reflection and inferface

Building view models for use within your MVC solution is great to combine objects for a strongly typed view. And I have been all about them. I just encountered a situation where I needed to update values for specific models contained within the view model. Getting the view model back from the view was the easy part thanks to MVC.

        [Authorize]
        public ActionResult Index()
        {
            var context = new Context();
            var repo = new UserRepository(context);

            var user = repo.GetUserByEmail(User.Identity.Name);

            var account = new Account { User = user };

            return View(account);
        }

In a perfect world I would have loved to just have done this

        [Authorize]
        [HttpPost]
        public ActionResult Index(Account account)
        {
            UpdateModel(account.User);
            repo.Save();

            return RedirectToAction("Index");
        }

This immediately didn't told me it wouldn't work. Update what model? and in what context??? The solution I am making rebuilds the context each get/post. Is there a better way, probably, but this is what I do for now. After searching for some .net built in solutions, I started creating an update function that would copy values from the modified object into a context object, allow Entity Framework to detect the change and commit. First round looked like this.

        public void Update(User copyToUser, User copyFromUser)
        {
            var type = typeof(User);
            foreach (var pi in type.GetProperties())
            {
                var copyValue = copyFromUser.GetType().GetProperty(pi.Name).GetValue(copyFromUser, null);

                copyToUser.GetType().GetProperty(pi.Name).SetValue(copyToUser, copyValue,null);
            }

        }

This was working great, except it was modifying values related to identity and references! And then creating new objects! ACK! While I could have hard coded specific property names to either be skipped or evaluated, I wanted a more concrete and testable way.

I created a specific Interface under my current IUser and named it IUserDetailsCompare. Then instead of using the User type during my reflection loop I told the loop to evaluate the Interface. Done!

    public interface IUser
    {
        long Id { get; set; }
        string Email { get; set; }
        string NameFirst { get; set; }
        string NameLast { get; set; }
        DateTime DateJoined { get; set; }
        Guid Ref { get; set; }
        bool Active { get; set; }
        bool Disclaimer { get; set; }
    }

    public interface IUserDetailsCompare
    {
        string NameFirst { get; set; }
        string NameLast { get; set; }
    }

        public void Update(User copyToUser, User copyFromUser)
        {
            var type = typeof(IUserDetailsCompare);
            foreach (var pi in type.GetProperties())
            {
                var copyValue = copyFromUser.GetType().GetProperty(pi.Name).GetValue(copyFromUser, null);

                copyToUser.GetType().GetProperty(pi.Name).SetValue(copyToUser, copyValue,null);
            }

        }
I consider this a huge win in my strive to follow a more TDD approach.

Monday, February 27, 2012

MVC and Facebook for real

Believe it or not. I am about to release my first professional MVC 3 application for a client. I have done a few personal projects to learn and play with. But this one is for real and will get high traffic. This site also has a facebook integration on creating and managing events.

Since this isn't my first MVC 3 application, I really wanted to get a better grasp on partial views and how to implement them successfully. What I knew about partial views before going into this project is they are the ideal substitute of custom controls in a webform app. My first ever MVC 3 project coming from webforms I was expecting it to be a self contained "drop in" piece of functionality. And the first code example I saw was calling HTML.RenderPartial("_partial.cshtml"). Well I didn't really get how it was self contained, the partial views depended on objects in the viewbag without going back to the server. In order to different object it felt as if I had to much .net code going on in the partial view for it even to be considered a 'view'.

Even though I have heard and seen Controller Actions return a partial view I couldn't figure out why a controller action would just return a partial when I want a full user experience. Looking back this is mainly new syntax learning with razor vs traditional asp code. I finally ran into HTML.RenderAction() What a game changer it was. I could now drop in functionality independent of location and it would render proper html.