In-depth analysis and flexible application of Enterprise Library (3): If Unity, PIAB, and Exception Handling are introduced into MVP mode ......

Source: Internet
Author: User
Tags mscorlib

Recently, I am working on a Smart Client Software Factory project. Those familiar with SCSF or CAB should be familiar with the MVP design model. MVP is a variant of MVC. View and Mode focus on the UI presentation and business model respectively. View and Mode are completely separated. View accesses the business model through Presenter, presenter "indirectly" calls View to perform operations on the UI. For Exception Handling in MVP, the Exception Handling Application Block of Enterprise Library is implemented directly. Specifically, add a try/catch block to each control event in the View, and use the ExceptionPolicy in the catch to handle exceptions. This leads to the problem that the same code is repeatedly distributed across every corner of the application. So I have another idea: using Policy Injection to handle exceptions in AOP mode, when I had this idea, I thought a little more about it. Why don't we integrate Unity together. (Download Source Code)

1. MVP Introduction

In order to allow some readers who have not been familiar with MVP to understand the subsequent content, I will give a brief introduction to MVP first. As shown in: MVP is a bit similar to the MVC we are familiar with. View is responsible for implementing the interaction between the UI presentation and users. In CAB, View is generally implemented through a User Control, mode focuses on specific business models, independent of View and Presenter. View has a Presenter reference. When View needs to call Mode (for example, access Mode is required to pass in query conditions to obtain data), access Mode through Presenter. For the Presenter, it needs to operate the View (for example, after the data is obtained successfully, it is displayed in the View), but the Presenter does not directly reference the View itself, the View Interface (IView) is referenced, so the View object is an unstable object, and the Presenter only needs some fixed operations in the View, therefore, define these operations in the IView interface and convert the dependency on the View into the IView dependency, which fully reflects the principle of structure-oriented conversion.

2. Simulate simple MVP

Next, we use a simple scenario to simulate MVPs. This is an example of a commonly used calculator. The overall composition is shown in:

1. ICalculator: Calculator interface, which defines an operation for Division calculation.

Namespace Artech. UnityInMVP
{
Public interface ICalculator
{
Int Divide (int op1, int op2 );
}
}

2. Calculator: implements the ICalculator interface.

Namespace Artech. UnityInMVP
{
Public class Calculator: ICalculator
{
Public int Divide (int op1, int op2)
{
Return op1/op2;
}
}
}

3. CalculatePresenter: the calculation result is displayed in the View.

Namespace Artech. UnityInMVP
{
Public class CalculatePresenter
{
Public CalculatePresenter ()
{
This. Calculator = new Calculator ();
}

Public ICalculateView View
{Get; set ;}

Public ICalculator Calculator
{Get; set ;}

Public void Calculate (int op1, int op2)
{
Int result = this. Calculator. Divide (op1, op2 );
This. View. DisplayResult (result );
}
}
}

The CalculatePresenter has an ICalculateView attribute, which is used to display the calculation result. The reason why ICalculator is defined is to remove the dependencies on the specific Calculator, but so far, this goal has not been reached, because it is still dependent on Calculator in the constructor.

4. ICalculateView: defines an operation for displaying calculation results. This operation is called by CalculatePresenter.

Namespace Artech. UnityInMVP
{
Public interface ICalculateView
{
Void DisplayResult (int result );
}
}

5. CalculateView: In this example, it is a Form that implements ICalculateView

Namespace Artech. UnityInMVP
{
Public partial class CalculateView: ICalculateView
{

Public CalculatePresenter Presenter
{Get; set ;}

# Region ICalculateView Members

Public void DisplayResult (int result)
{
This. textBoxResult. Text = result. ToString ();
}

# Endregion

Private void buttonCalculate_Click (object sender, EventArgs e)
{
Int op1;
Int op2;
If (! Int. TryParse (this. textBoxOp1.Text. Trim (), out op1 ))
{
Return;
}

If (! Int. TryParse (this. textBoxOp2.Text. Trim (), out op2 ))
{
Return;
}

Try
{
This. Presenter. Calculate (op1, op2 );
}
Catch (Exception ex)
{
If (ExceptionPolicy. HandleException (ex, "UI Exception Policy "))
{
Throw;
}
}
}

Private void CalculateView_Load (object sender, EventArgs e)
{
This. Presenter = new CalculatePresenter ();
This. Presenter. View = this;
}
}
}

Initialize the Presenter attribute during Load and set the View object to View itself. In buttonCalculate_Click, input the user-input operand and call the Calculate method of Presenter. To handle potential exceptions, a try/catch is added and the Enterprise Library handler ton Handling Applicaion Block is called in the catch to handle exceptions. At the same time, CalculateView implements the DisplayResult method of ICalculateView and displays the calculation result in TextBox.

3. Transformation of the above program through Unity and Policy Injection

My current goal is to improve the above design to achieve the following two goals:

  • Exception Handling through AOP, frequent occurrence of the same try/catch is not a good phenomenon (in fact, in our current project, except for the handling of evil exceptions, there are other non-business logics that I have known each other. I hope all these business-independent logics are implemented through AOP ).
  • Remove the dependencies of CalculatePresenter on Calculator so that it only depends on ICalculator.

My idea is to introduce the Policy Injection Application Block to implement the Exception Handling operation; Introduce Unity to implement decoupling of CalculatePresenter and Calculator through Depedency Injection; at the same time, Policy Injection and Unity are integrated through Unity Extension (See Chapter 1 of this series ).

To this end, we will first transform CalculatePresenter.

Namespace Artech. UnityInMVP
{
[ExceptionCallHandler ("UI Exception Policy")]
Public class CalculatePresenter: MarshalByRefObject
{
Public CalculatePresenter ()
{
This. Calculator = new Calculator ();
}

Public ICalculateView View
{Get; set ;}

[Dependency]
Public ICalculator Calculator
{Get; set ;}

Public void Calculate (int op1, int op2)
{
Int result = this. Calculator. Divide (op1, op2 );
This. View. DisplayResult (result );
}
}
}

  • To enable Policy Injection to take effect, I have inherited MarshalByRefObject and applied ExceptionCallHandler in the form of Custom Attribute, and formulated the exception handling policy (in real project development, I recommend using Policy injection in configuration mode ).
  • The Property Dependency based on Unity is implemented through [dependency.

Then we will rebuild the View. Because we use [Dependency] and [ExceptionCallHandler] In CalculatePresenter, we need to create the CalculatePresenter object using the Unity Container method, for this reason, I have defined the base class of View: ViewBase.

Namespace Artech. UnityInMVP
{
Public partial class ViewBase: Form
{

Private IUnityContainer _ unityContainer;

Protected IUnityContainer UnityContainer
{
Get
{
If (this. _ unityContainer = null)
{
This. _ unityContainer = new UnityContainer ();
UnityConfigurationSection unityConfigSection = ConfigurationManager. GetSection ("unity") as UnityConfigurationSection;
UnityConfigSection. Containers. Default. Configure (this. _ unityContainer );
}
Return this. _ unityContainer;
}
}
}
}

The IUnityContainer attribute is defined in ViewBase to create a Presenter object for the View. In this way, CalculateView can be defined as follows:

Namespace Artech. UnityInMVP
{
Public partial class CalculateView: ViewBase, ICalculateView
{
Public CalculateView ()
{
InitializeComponent ();
}

Public CalculatePresenter Presenter
{Get; set ;}

# Region ICalculateView Members

Public void DisplayResult (int result)
{
This. textBoxResult. Text = result. ToString ();
}

# Endregion

Private void buttonCalculate_Click (object sender, EventArgs e)
{
Int op1;
Int op2;
If (! Int. TryParse (this. textBoxOp1.Text. Trim (), out op1 ))
{
Return;
}

If (! Int. TryParse (this. textBoxOp2.Text. Trim (), out op2 ))
{
Return;
}

This. Presenter. Calculate (op1, op2 );
}

Private void CalculateView_Load (object sender, EventArgs e)
{
This. Presenter = this. UnityContainer. Resolve <CalculatePresenter> ();
This. Presenter. View = this;
}
}
}

In buttonCalculate_Click, try/catch is not required at all. During View initialization, Presenter is directly created using the Resolve Method of UnityContainer.

Finally, let's take a look at the related Configuration:

<? Xml version = "1.0" encoding = "UTF-8"?>
<Configuration>
<ConfigSections>
<Section name = "exceptionHandling" type = "Microsoft. practices. enterpriseLibrary. exceptionHandling. configuration. exceptionHandlingSettings, Microsoft. practices. enterpriseLibrary. predictionhandling, Version = 4.0.0.0, Culture = neutral, PublicKeyToken = 31bf3856ad364e35 "/>
<Section name = "unity" type = "Microsoft. practices. unity. configuration. unityConfigurationSection, Microsoft. practices. unity. configuration, Version = 1.1.0.0, Culture = neutral, PublicKeyToken = 31bf3856ad364e35 "/>
</ConfigSections>
<ExceptionHandling>
<ExceptionPolicies>
<Add name = "UI Exception Policy">
<ExceptionTypes>
<Add type = "System. Exception, mscorlib, Version = 2.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089"
PostHandlingAction = "None" name = "Exception">
<ExceptionHandlers>
<Add type = "Artech. UnityInMVP. ExceptionHandlers. MessageBoxHandler, Artech. UnityInMVP"
Name = "Custom Handler"/>
</ExceptionHandlers>
</Add>
</ExceptionTypes>
</Add>
</ExceptionPolicies>
</ExceptionHandling>
<Unity>
<Containers>
<Container>
<Types>
<Type = "Artech. UnityInMVP. ICalculator, Artech. UnityInMVP" mapTo = "Artech. UnityInMVP. Calculator, Artech. UnityInMVP"/>
</Types>
<Extensions>
<Add type = "Artech. UnityInMVP. UnityExtensions. PolicyInjectionExtension, Artech. UnityInMVP"/>
</Extensions>
</Container>
</Containers>
</Unity>
</Configuration>

The first part is the configuration of exceptionHandling. For the sake of simplicity, I have created a custom ExceptionHandler: MessageBoxHandler to handle all exceptions. This handler only displays the error message through MessageBox, if you are interested, you can download the source code.

<ExceptionHandling>
<ExceptionPolicies>
<Add name = "UI Exception Policy">
<ExceptionTypes>
<Add type = "System. Exception, mscorlib, Version = 2.0.0.0, Culture = neutral, PublicKeyToken = b77a5c561934e089"
PostHandlingAction = "None" name = "Exception">
<ExceptionHandlers>
<Add type = "Artech. UnityInMVP. ExceptionHandlers. MessageBoxHandler, Artech. UnityInMVP"
Name = "Custom Handler"/>
</ExceptionHandlers>
</Add>
</ExceptionTypes>
</Add>
</ExceptionPolicies>
</ExceptionHandling>

 

The second part is the unity configuration. in <types>, the mapping relationship between ICalculator and Calculator is defined, and the decoupling of Presenter and Calculator is realized; the extensions configuration implements Policy Injection and Unity integration. For detailed implementation, see Chapter 1 of this series.
<Unity>
<Containers>
<Container>
<Types>
<Type = "Artech. UnityInMVP. ICalculator, Artech. UnityInMVP" mapTo = "Artech. UnityInMVP. Calculator, Artech. UnityInMVP"/>
</Types>
<Extensions>
<Add type = "Artech. UnityInMVP. UnityExtensions. PolicyInjectionExtension, Artech. UnityInMVP"/>
</Extensions>
</Container>
</Containers>
</Unity>
This makes all implementations. For example, if the second operand is set to zero, the defined MessageBoxHandler will be executed and the Message will be displayed through MessageBox, as shown below:

 

P.S.Although Policy Injection can be applied to the Presenter to handle exceptions through AOP, this requires that all logic with potential exceptions thrown on the View be implemented through Presenter, because predictionhandler is applied to the Presenter.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.