当我为所有页面提供共同属性时,我一直在努力寻找一种处理Asp.net MVC网站模型的好方法.这些属性将显示在布局(母版页)中.我正在使用一个包含这些属性的"BaseModel"类,我的布局使用此BaseModel作为其模型.
每个其他模型都继承自该BaseModel,并且每个模型都具有相对于它所代表的视图的特定属性.正如您可能已经猜到的那样,我的模型实际上是视图模型,即使这与此处不太相关.
我尝试了不同的方法来初始化BaseModel值
但这些都没有真正吸引我:
当然,(几乎)所有这些解决方案都有效,但我正在寻找一种更好的方法.
在输入这个问题时,我发现可能是一条新路径,也可能是构建器模式,但实现也很快成为负担,因为我们可以拥有数十个视图和控制器.
我很乐意接受任何严肃的建议/暗示/建议/模式/建议!
更新
感谢@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;
}
}
Run Code Online (Sandbox Code Playgroud)
然后调用它非常简单:
[Model(typeof(HomeModel))]
public ActionResult Index()
{
var homeModel = TempData["model"] as HomeModel;
// Add View Specific stuff
return View(homeModel);
}
Run Code Online (Sandbox Code Playgroud)
它给了我最好的每一个世界.唯一的缺点是找到一种将模型传递回动作的正确方法.
这里使用TempData对象完成,但我也考虑更新可以在ActionParameters中找到的模型.
我仍在为此或之前的观点采取任何认真的建议/提示/建议/模式/建议.
@EBarr 让我使用动作过滤器的想法实际上是有效的,但最终感觉是错误的,因为没有干净的方法来检索模型而不通过 viewbag、httpcontext 项或类似的东西。此外,它还强制要求用模型来装饰每个动作。这也使得回发更加难以处理。我仍然相信这个解决方案有其优点,并且在某些特定场景中可能有用。
所以我回到了第一个方向,开始更多地研究这个话题。我来到了以下。首先问题有两个方面
在寻找更多想法时,我意识到我没有从正确的角度看待问题。我是从“控制器”POV 来看它的,而模型的最终客户端是视图。我还被提醒,布局/母版页不是视图,不应该有与之关联的模型。这种洞察力让我走上了适合我的道路。因为这意味着布局的每个“动态”部分都应该在其外部处理。当然,由于其灵活性,节似乎非常适合这一点。
在我制作的测试解决方案中,我(仅)有 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; }
}
Run Code Online (Sandbox Code Playgroud)
例如,我的视图模型是这样的:
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); }
}
}
Run Code Online (Sandbox Code Playgroud)
这在操作中初始化:
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);
}
Run Code Online (Sandbox Code Playgroud)
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;
}
}
Run Code Online (Sandbox Code Playgroud)
在视图(.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); } }
Run Code Online (Sandbox Code Playgroud)
实际的解决方案有更多代码,我尝试简化以了解这里的想法。它仍然有点原始,需要完善/错误处理,但这样我就可以在我的操作中定义这些部分是什么,它们将使用什么模型等等。它可以轻松测试,并且设置 DI 应该不是问题。
我仍然必须在每个视图中复制 @section 行,这看起来有点痛苦(特别是因为我们不能将这些部分放在部分视图中)。
我正在研究模板化的剃刀委托,看看它是否无法替换这些部分。
| 归档时间: |
|
| 查看次数: |
2418 次 |
| 最近记录: |