Asp.net MVC model for viewing and layout

I am trying to find a good way to deal with our MVC Asp.net website models by having common properties for all pages. These properties should be displayed in the layout (main page). I use the "BaseModel" class that contains these properties, and my layout uses this BaseModel as its model.

Each other model inherits from this BaseModel, and each of them has certain properties relative to the view it represents. As you might have guessed, my models are actually model models, even if this is not entirely appropriate.

I tried different ways to initialize BaseModel values

  • At hand in all views
  • Having a base controller that has a virtual Initialize method for this (so that a specific controller can implement a specific general behavior for example)
  • The presence of a basic control that overrides OnActionExecuting to call the Initialize method
  • Using a helper class to do this outside the controller
  • Using the Factory Model

But not one of those who really addresses me:

  • It seems obvious to me, but DRY is one of the reasons sufficient to justify this (in fact, I never tried this solution at all, I just put it in order to be able to focus on this point at the last point).
  • , , , , , BaseController Initialize, , , .
  • .
  • 3. , .
  • , , . . , , , , ( - )

, () , .

, , , , , , .

////!

@EBarr , ActionFilterAttribute ( , 5 ):

public class ModelAttribute : ActionFilterAttribute
{
    public Type ModelType { get; private set; }

    public ModelAttribute(string typeName) : this(Type.GetType(typeName)) { }

    public ModelAttribute(Type modelType)
    {
        if(modelType == null) { throw new ArgumentNullException("modelType"); }

        ModelType = modelType;
        if (!typeof(BaseModel).IsAssignableFrom(ModelType))
        {
            throw new ArgumentException("model type should inherit BaseModel");
        }
    }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var model = ModelFactory.GetModel(ModelType);

        var foo = filterContext.RequestContext.HttpContext.Session["foo"] as Foo;

        model.Foo = foo;
        model.Bar = somevalue;

        filterContext.Controller.TempData["model"] = model;
    } 
}

: :

[Model(typeof(HomeModel))]
public ActionResult Index()
{
    var homeModel = TempData["model"] as HomeModel;

    // Add View Specific stuff

    return View(homeModel);
}

. - .

TempData, , ActionParameters.

- //// .

+5
2

, @EBarr , , , viewbag httpcontext - . , . . - , .

, . . .

, , . "" POV, . , Layout/Master . , . , "" . , , - .

, , () 4 , , - . , , /. , :

public interface IViewModel
{
    KeyValuePair<string, PartialViewData>[] Sections { get; }
}

public class PartialViewData
{
    public string PartialViewName { get; set; }
    public object PartialViewModel { get; set; }
    public ViewDataDictionary ViewData { get; set; }
}   

, :

public class HomeViewModel : IViewModel
{
    public Article[] Articles { get; set; }             // Article is just a dummy class 
    public string QuickContactMessage { get; set; }     // just here to try things

    public HomeViewModel() { Articles = new Article[0]; }

    private Dictionary<string, PartialViewData> _Sections = new Dictionary<string, PartialViewData>();
    public KeyValuePair<string, PartialViewData>[] Sections
    {
        get { return _Sections.ToArray(); }
        set { _Sections = value.ToDictionary(item => item.Key, item => item.Value); }
    }
}

:

public ActionResult Index()
{
    var hvm = ModelFactory.Get<HomeViewModel>(); // Does not much, basicaly a new HomeViewModel();

    hvm.Sections = LayoutHelper.GetCommonSections().ToArray(); // more on this just after
    hvm.Articles = ArticlesProvider.GetArticles(); // ArticlesProvider could support DI

    return View(hvm);
}

LayoutHelper - ( DI ):

public class DefaultLayoutHelper
{
    private Controller Controller;
    public DefaultLayoutHelper(Controller controller) { Controller = controller; }

    public Dictionary<string, PartialViewData> GetCommonSections(QuickContactModel quickContactModel = null)
    {
        var sections = new Dictionary<string, PartialViewData>();
        // those calls were made in methods in the solution, I removed it to reduce the length of the answer
        sections.Add("header",
                     Controller.UserLoggedIn() // simple extension that check if there is a user logged in
                     ? new PartialViewData { PartialViewName = "HeaderLoggedIn", PartialViewModel = new HeaderLoggedInViewModel { Username = "Bishop" } } 
                     : new PartialViewData { PartialViewName = "HeaderNotLoggedIn", PartialViewModel = new HeaderLoggedOutViewModel() });
        sections.Add("quotes", new PartialViewData { PartialViewName = "Quotes" });
        sections.Add("quickcontact", new PartialViewData { PartialViewName = "QuickContactForm", PartialViewModel = model ?? new QuickContactModel() });
        return sections;
    }
}

(.cshtml):

@section       quotes { @{ Html.RenderPartial(Model.Sections.FirstOrDefault(s => s.Key == "quotes").Value); } }
@section        login { @{ Html.RenderPartial(Model.Sections.FirstOrDefault(s => s.Key == "header").Value); } }
@section       footer { @{ Html.RenderPartial(Model.Sections.FirstOrDefault(s => s.Key == "footer").Value); } }

, , . / , , , . , DI .

@section , ( , ).

, , .

+1

, MVC. , .

. , . . , /, ActionExecuting OnActionExecuted.

, :

if (filterContext.ActionParameters.ContainsKey("model")) {
   var tempModel = (System.Object)filterContext.ActionParameters["model"];

   if (typeof(BaseModel_SuperLight).IsAssignableFrom(tempModel.GetType())) {
       //do stuff required by light weight model
   }

   if (typeof(BaseModel_RegularWeight).IsAssignableFrom(tempModel.GetType())) {
      //do more costly stuff for regular weight model here
   }
}

, . , . - , . , .

+2

All Articles