Practical Application of Convention vs Configuration

Sorry for the double, but here’s a case in point that I’d like to share and get input on. Below are two entities, coded like normal.

Category.cs

using System.Collections.Generic;
using SharpArch.Core.DomainModel;
namespace Venue.Core
{
public class Category : Entity
{
private virtual IList<Forum> forums = new List<Forum>();
public virtual int Sequence { get; set; }
public virtual string Title { get; protected set; }
public virtual string Description { get; set; }
public virtual bool Enabled { get; set; }
public virtual bool Visible { get; set; }
public virtual IEnumerable<Forum> Forums { get { return forums; } }
public void AddForum(Forum entity) { forums.Add(entity); }
public void RemoveForum(Forum entity) { forums.Remove(entity); }
public Category(string title)
{
Title = title;
}
}
}

Forum.cs

using SharpArch.Core.DomainModel;
namespace Venue.Core
{
public class Forum : Entity
{
// public virtual int CategoryId { get; set; }
public virtual int Sequence { get; set; }
public virtual string Title { get; protected set; }
public virtual string Description { get; set; }
public virtual bool ThreadsEnabled { get; set; }
public virtual bool ThreadsQueued { get; set; }
public virtual bool RepliesEnabled { get; set; }
public virtual bool RepliesQueued { get; set; }
public virtual bool Enabled { get; set; }
public virtual bool Visible { get; set; }
public virtual Category Category { get; protected set; }
public Forum(Category entity, string title)
{
Category = entity;
Title = title;
}
}
}

Now, everything works fine for inserts and deletes, but what about reparenting (moving a forum from one category to another? Simply calling c1.RemoveForum(f) and then c2.AddForum(f) doesn’t work because f.Category still points to c1. I can’t set f.Category in the body of the AddForum and RemoveForum methods as it is protected set. This has bugged me for the longest time.

Solution?

P.S. I stripped input checking and exception throwing here for clarity.

public class Category : Entity
    {
        private IList<Forum> forums = new List<Forum>();
        public virtual int Sequence { get; set; }
        public virtual string Title { get; protected set; }
        public virtual string Description { get; set; }
        public virtual bool Enabled { get; set; }
        public virtual bool Visible { get; set; }
        public virtual IEnumerable<Forum> Forums { get { return forums; } }

        protected Category()
        {
        }

        public Category(string title)
        {
            Title = title;
        }

        public virtual void AddForum(Forum entity) { forums.Add(entity); }
        public virtual void RemoveForum(Forum entity) { forums.Remove(entity); }
    }

    public class Forum : Entity
    {
        // public virtual int CategoryId { get; set; }
        public virtual int Sequence { get; set; }
        public virtual string Title { get; protected set; }
        public virtual string Description { get; set; }
        public virtual bool ThreadsEnabled { get; set; }
        public virtual bool ThreadsQueued { get; set; }
        public virtual bool RepliesEnabled { get; set; }
        public virtual bool RepliesQueued { get; set; }
        public virtual bool Enabled { get; set; }
        public virtual bool Visible { get; set; }
        public virtual Category Category { get; protected set; }

        protected Forum()
        {
        }

        public Forum(Category entity, string title)
        {
            Title = title;
            MoveCategory(entity);
        }

        public virtual void MoveCategory(Category category)
        {
            if (Category != null)
                Category.RemoveForum(this);

            Category = category;
            Category.AddForum(this);
        }
    }

Ok, hm…well it works, but here’s a question for you D. If the job of managing related forums is the job of the owner (in this case, the category) should the method to move one to another belong in the category class? This is one of those grey areas in DDD that confuse me.

Anyway, let’s say I have the following service method:

public bool ProcessForumEdit(Forum transientForum, int categoryId)
{

// get destination category
Category destinationCategory = categoryRepository.GetById(categoryId);

// get original forum
Forum originalForum = categoryRepository.GetOriginalForumById(transientForum.Id);

// update the original
originalForum.MoveTo(destinationCategory);
// … update remaining public settables except Id

// save
categoryRepository.SaveOrUpdate();

return true; // only reached if no exception was thrown

}

Does that look close to being right? I’m still not sure I like the idea of having a method that fetches a forum in the category repository though. Still, that’s the pattern, repository for root only. So am I close?

If you have the original CategoryId, then if might feel more natural to load the original Category to find the forum. If you want the parent to control the moving, then you migth want to a MoveForum method to the categry that delegates to the forum method:



public class Category
{
// snip
   public virtual void MoveForum(Forum forum)
   {
      forum.MoveTo(this);
   }
// snip
}

Ok, well I have that part working ok, but I am still having some general issues with FluentNHibernate that are simply killing me.

The biggest problem is the protected parameterless ctor and all the protected set fields. The default model binder just can’t handle that. So what am I to do? Write my own binder(s)? Or is there a provided class that I am unaware of?

This is all just mounting up to become of pile of reasons why I don’t like these things. If I dropped either a dbml or edmx in there, and extended the generated classes with a few things, I’d be done. Of course, I’d be stuck with SqlServer, but that doesn’t really bother me, as that’s all I have.

I just have to wonder if all this is really practical.

We don’t send domain entities to or from the client, we use specific View and Input models. The “parameterless ctor and all the protected set fields” are your own design decisions and have nothing to do with FluentNHibernate. If you really want your domain entities created by Mvc modelbinders, then you will either have to create your own binder or have an empty public constructor.

Well I understand about the ViewModels (I call them Presentations) but how does that help in binding on post? Take the following:

[HttpPost]
public ActionResult Edit(Forum entity)
{

// cut out active code here…

// default action if not handled above
return View(new ForumEditPresentation(entity, categories /* for selectList */ ));

}

This is what I’ve been doing. Are you saying I should completely duplicate the entity properties as a normal class like so:

public class ForumEditPresentation
{

public int Id { get; set; }
// other fields and select list here

}

and then do:

[HttpPost]
public ActionResult Edit(ForumEditPresentation presentation)
{

// cut out active code here…

// default action if not handled above
return View(presentation);

}

Yes, that’s how we do it. We also use AutoMapper (http://automapper.codeplex.com/) and some strongly defined conventions so we have very little mapping code for dealing with moving data around.

You know…I feel a bit vindicated here. More than a year ago I made a post which spawned a long thread on this very thing. In it, I suggested that what we are handling on post is a collection of form values (as indicated by the default of FormCollection) and that instead of binding to entities, we should be binding to a separate class that wraps just those fields we are capturing on the form itself. Since we somtimes send more data to a view than we get back in the form, we should also have a separate view class. Setup like this:

[HttpGet]
public ActionResult Edit (int id)
{

// disply the view using values in entity
return View(new ForumEditPage(forumService.GetForumById(id));

}

[HttpPost]
public ActionResult Edit (ForumEditForm f)
{

if (ModelState.IsValid)
if (forumService.ProcessEdit(f))
return RedirectToAction(“Manager”);

// redisply the view using values in prior form
return View(new ForumEditPage(f);

}

The funny thing is, nobody knew what the hell I was talking about back then. Everybody thought I was nuts. Granted, I used two classes (one for view and one for form) whereas here, we’re suggesting using a single class, but whatever, it’s the same mechanic.

How funny…I wonder what idiotic thing I’ve said this year that will be embraced next year?

I’ve been pushing this on you since the start, so your speaking to the converted :stuck_out_tongue:

You always did kinda get me, even when I was purely rambling. I was just pointing it out for the sake of it. Perhaps you could answer one more thing for me, something I never really followed up on.

In the CategoryController.Manager view, which presents a list of basic info on current categories (id, sequence, title) I would like to make each row have an editable sequence with a submit form at the bottom. On post, it saves the new order for each item. I know I can bind to IEnumerable<CategoryManagerForm> to pick up the changes (form elements must be indexed for this to work), but is it good practice to do so?

Also, is it ok, to then pass an IEnumerable<CategoryManagerForm> to View(), for display, or should I still be wrapping it in a single class:

View(new CategoryManagerForms(forms));

Thanks D