2016-01-21 39 views
3

問題僅用於教育目的。繼承與ViewData類的屬性參數和插入組合

我有3個Web旅程的MVC應用程序,它們以許多方式相互鏡像 - 這可以方便地引入可以處理常見部件的Base類。

我也使用大量類的屬性正在投入viewData 這使得它方便的重用旅程之間的意見時,我可以,而不需要在繼承這一數據每程插入不同的電話號碼(或其他位)每個viewModel,因爲它是每旅程相同(不能放置到母版頁,因爲它顯示在許多不同的意見)。這些常量在生成電子郵件等時也用於代碼中。

[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來繼續,並且繼續我的生活。

我的選擇:

  1. 更改方法簽名,這些常量傳遞到方法 - 我不喜歡這一點,因爲其將數據傳遞給基礎指示的執行應在類本身。

  2. 引入abstract獲取方法,然後返回用於基類的常量例如。

    protected override string GetContactNumber() 
    { 
        return ContactNumber; 
    } 
    

    我不喜歡,有沒有辦法強制歸還這些特定的常量和感覺有點哈克,但恕我直言,爲努力最少的最佳解決方案。

  3. 擺脫常量和屬性,並引入BaseViewModel類,該類將具有抽象屬性和每個旅程中會設置值的一種機制。或者可以是已經設置了這些屬性的3個基類。 - 不喜歡它,很多替代品。

你能想出一些其他的解決辦法,是將解決我的情況最少的工作和最低黑客。

回答

2

我認爲你在這裏的主要問題是你正在使用屬性的目的,他們不是爲了。屬性用於將元數據添加到代碼構造中。但是您正在使用它們附加數據,通常應該將其作爲實際對象定義的一部分(作爲屬性獲取器)。

此外,我不認爲你必須訴諸於類繼承來提出一個適用於這種情況的設計。但是如果你需要,這個解決方案仍然可以工作。

獨立接口

如果你打破每一塊數據到自己的接口,你可以採取的事實,即一個類可以實現多個接口,以覆蓋默認值根據需要。

public interface IContactNumber 
{ 
    string ContactNumber { get; } 
} 

單獨行動Fitlers

而不是繼承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(); 
    } 
} 

請注意,您可以也加入ContactNumberViewData前添加了一種通過其存在的屬性和測試,以打開此功能開啓或關閉,如果你真的需要一個ON/OFF開關。

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] 
public class ContactNumberAttribute : Attribute 
{ 
} 
+0

我愛你的解決方案。我真的不喜歡每個控制器頂部的一堆常量,尤其是沒有被接口強制使用,但是讓每個VM都繼承自一些BaseVM,只是爲了進入一些模糊的地方,一些數據看起來更糟。這個解決方案更加優雅 - 簡單。 –

0

聽起來像你想要使用模板模式。

如果我接近這種模式我會用一個接口來定義合同提供「常量」:

public interface IJourneyConstants 
{ 
    string ContactNumber {get;} 
    string ContactNumberForPricingPage{ get;} 
    string ProductReference {get;} 
    string LegalReference = {get;} 
} 

那麼就需要定義類(ES),將提供這樣的數據:

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看起來更清潔。

然後,你定義一個Controller

[LayoutViewData(Provider = typeof(ExampleJourneyConstantProvider)] 
public class ExampleJourneyController : Controller{ 
} 

注:沒有理由你控制器無法實現IJourneyConstants

[LayoutViewData(Provider = typeof(ExampleJourneyController)] 
public class ExampleJourneyController : Controller, IJourneyConstants{ 
} 

然後,您就ActionFilter將動態創建Provider **實例,施放它,應用任何常見的吸引引擎代碼並更新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); 
    }  
}