>问题仅用于教育目的。
我有一个 MVC 应用程序,它有 3 个 Web 旅程,它们在许多方面相互镜像 - 这使得引入处理常见部分的基类变得很方便。
我还使用了许多被放入viewData
的类属性 这使得在旅程之间重用视图时很方便,我可以在每个旅程中插入不同的电话号码(或其他位),而无需在每个视图模型中继承此数据,因为它在每个旅程中都是相同的(不能放置在母版页中,因为它显示在许多不同的视图中)。在生成电子邮件等时,这些常量也用于代码中。
[LayoutViewData(ContactNumber = ContactNumber, LegalType = LegalType, LegalReference = LegalReference)]
public class JourneyController : BaseJourneyController
{
private const string ContactNumber = "0800 161 5191";
private const string ContactNumberForPricingPage = "0800 051 3322";
private const string ProductReference = "LS0083";
private const string LegalReference = "conveyancing";
}
public class LayoutViewDataAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Controller.ViewData.Add("ContactNumber", this.ContactNumber);
}
...
}
我正在尝试解决的问题:
有些方法在旅程中几乎相同,可以移动到基数,但它们需要常量中的值。问题是只有静态可以传递到属性中,这意味着我不能简单地将它们更改为仅使用 getter 的抽象属性并继续我的生活。
我的选择:
更改方法签名以将这些常量传递到方法 - 我不喜欢这样,因为必须将数据传递给 base 表明实现应该在类本身中。
引入抽象 Get 方法,然后返回常量以在基类中使用,例如。
protected override string GetContactNumber() { return ContactNumber; }
我不喜欢没有办法强制返回这些特定的常量,并且感觉有点笨拙,但恕我直言,这是花费最少努力的最佳解决方案。
摆脱常量和属性并引入 BaseViewModel 类,该类将具有抽象属性和每个旅程的一个机制来设置值。或者可以是 3 个基类,并且已经设置了这些属性。- 不喜欢它,很多替代品。
你能想到其他一些解决方案吗,那就是最少的工作和最少的黑客来解决我的情况。
我认为您在这里的主要问题是您将属性用于它们不打算用于的目的。属性用于向代码构造添加元数据。但是,您正在使用它们来附加通常应该是实际对象定义一部分的数据(作为属性获取器)。
此外,我不认为你一定要诉诸类继承来提出一个适用于这种情况的设计。但是,如果需要,此解决方案仍然有效。
独立接口
如果将每段数据分解到其自己的接口中,则可以利用类可以实现多个接口的事实来根据需要重写默认值。
public interface IContactNumber
{
string ContactNumber { get; }
}
独立式操作钳
与其继承具有自身内置限制的ActionFilterAttribute
,不如实现IActionFilter
从等式中删除不必要的属性。
public class ContactNumberActionFilter : IActionFilter
{
private readonly string defaultContactNumber;
public ContactNumberActionFilter(string defaultContactNumber)
{
this.defaultContactNumber = defaultContactNumber;
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
// No implementation
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
var controller = filterContext.Controller;
var provider = controller as IContactNumber;
var contactNumber = (provider == null) ? this.defaultContactNumber : provider.ContactNumber;
controller.ViewData.Add("ContactNumber", contactNumber);
}
}
然后,只需全局注册过滤器即可。
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
// Register the action filter globally and provide a default number
filters.Add(new ContactNumberActionFilter("0800 161 5191"));
// TODO: Make filters for ContactNumberForPricingPage,
// ProductReference, and LegalReference and set their default
// values here.
}
}
用法
现在,如果要覆盖默认联系号码,只需在控制器中实现IContactNumber
即可。如果未实现此接口,将使用默认编号。
public class HomeController : Controller, IContactNumber
{
public string ContactNumber
{
get { return "0800 161 5192"; }
}
public ActionResult Index()
{
return View();
}
}
请注意,如果您确实需要开/关开关,您还可以添加一种方法来打开或关闭此功能,方法是使用 Attribute 并在将ContactNumber
添加到ViewData
之前测试其是否存在。
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ContactNumberAttribute : Attribute
{
}
在我看来,您正在尝试使用模板模式。
如果我接近这种模式,我会使用一个接口来定义提供"常量"的合约:
public interface IJourneyConstants
{
string ContactNumber {get;}
string ContactNumberForPricingPage{ get;}
string ProductReference {get;}
string LegalReference = {get;}
}
然后,您将定义将提供此类数据的类:
public class ExampleJourneyConstantProvider : IJourneyConstants
{
string IJourneyConstants.ContactNumber => "0800 161 5191";
string IJourneyConstants.ContactNumberForPricingPage => "0800 051 3322";
string IJourneyConstants.ProductReference => "LS0083";
string IJourneyConstants.LegalReference => "conveyancing";
}
在此示例中,我使用了显式接口实现。 这不是必需的,但是如果您将其放在Controller
中,它会使您的控制器看起来更干净一些。
然后定义一个Controller
:
[LayoutViewData(Provider = typeof(ExampleJourneyConstantProvider)]
public class ExampleJourneyController : Controller{
}
注意:您的控制器没有理由无法实现IJourneyConstants
:
[LayoutViewData(Provider = typeof(ExampleJourneyController )]
public class ExampleJourneyController : Controller, IJourneyConstants{
}
然后,您将ActionFilter
动态创建提供程序**的实例,强制转换它,应用任何常见的诱人引擎代码并更新 ViewData:
public class LayoutViewDataAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
IJourneyConstants constants;
try{
constants = (IJourneyConstants )System.Activator.CreateInstance(this.Provider);
}catch (Exception e){
throw new Exception("Failed to create instance of IJourneyConstants from " +
$"{this.Provider.Name}. Ensure it inherits from IJourneyConstants "+
"and contains a public paramaterless constructor.");
}
// perform any common calculation using your constants
bool isContactNumberInternational = constants.ContactNumber.StartsWith("+");
// update the ViewData
filterContext.Controller.ViewData.Add("ContactNumber",
constants.ContactNumber);
filterContext.Controller.ViewData.Add("IsNumberInternational",
isContactNumberInternational);
}
}