Saturday, April 19, 2008

Use MVP on Windows Forms

In my previous post, I suggested a MVP pattern for ASP.Net controls. This post intend to implement MVP for .Net Windows Forms. Note that since Windows Forms data are in memory objects, it is much easier to do two-way databinding.

(1) Model ---Reference Data
Data Entry windows almost always need reference data to guide user input. A dropdown list filled with 50 US States is a perfect example. Naturally, we would want a simple type structure class and a complex type storage class to hold 50 states for databinding purposes:

public struct State
{
public State(int? id, string name)
{
_StateID = id;
_StateName = name;
}

private int? _StateID;
public int? StateID
{
get { return _StateID;}
set { _StateID = value; }
}

private string _StateName;
public string StateName
{
get { return _StateName; }
set { _StateName = value; }
}
}

public class States
{
private static List _StateList = new List();
static States()
{
_StateList.Add(new State(0, "MA"));
....

_StateList.Add(new State(null, "(empty)")); // add (empty) for user to de-select to
}
public static List StateList
{
get { return _StateList; }
}
}

(2) Model --- Entity Data
Suppose UI is designed to present User an address for View/Edit/Create, we then need an entity to represent Address:

public class Address
{
private int? _StateID;
public int? StateID
{
get { return _StateID; }
set { _StateID = value; }
}
}

Note Database normalization requires only saving StateID to database and avoid duplicating StateName.


(3) Presenter --- one way databinding to View
Upon View calling its single entry point, a Presenter will initialize View, Load Data from Model and Execute Business logic (e.g. show, update, add):

public interface IAddressView
{
void ShowReferenceData();
void ShowCurrentAddress();
}

class AddressPresenter
{
private IAddressView _view;
public AddressPresenter(IAddressView view)
{
_view = view;
}
public void Render(int? UserID)
{
LoadEntityData(UserID);
_view.ShowReferenceData();
_view.ShowCurrentAddress();
}

private ASC.ReferenceData.State _State;
public ASC.ReferenceData.State State
{
get { return _State;}
set { _State=value;}
}

private ASC.Entity.Address _Address;
private void LoadEntityData(int? UserID)
{
// simulate retrival of Address from Database
_Address = new ASC.Entity.Address();
if (UserID >0) _Address.StateID = 3;
// All UserID <=0 will have empty selection of state in its Address Entity;

foreach (ASC.ReferenceData.State s in ASC.ReferenceData.States.StateList)
{
if (s.StateID == _Address.StateID)
{
_State = new ASC.ReferenceData.State(_Address.StateID, s.StateName);
break;
}
}

}
public void Update()
{
// Save Entity to Database
}
public void Add()
{
// Insert Entity to Database
}
}
Note that two properties in Address Entity Data are represented as a single property of type ASC.ReferenceData.State. This will enable loading into a ComboBox for ease of databinding.


(4) View --- Implement IView and Reverse DataBind
As before, View need to implement IView with consideration of reference data
public partial class AddressChangeForm : Form, IAddressView
{
public void ShowReferenceData()
{
this.cbState.DataSource = ASC.ReferenceData.States.StateList;
this.cbState.DisplayMember = "StateName";
}

public void ShowCurrentAddress()
{
cbState.DataBindings.Add("SelectedItem", _Presenter, "State",false,DataSourceUpdateMode.OnPropertyChanged, new ASC.ReferenceData.State(null,"(empty)"));
}
Note that Reference data are push to View using one-way databinding, while Presenter data are push to View with change probagated backwards through OnPropertyChanged.
Presenter entry point will be called upon user action:
private void btnGetCurrent_Click(object sender, EventArgs e)
{
cbState.DataBindings.Clear();
_Presenter = new AddressPresenter(this);
_Presenter.Render(1078);
}

And a new presenter need to be created upon User Add New action:
private void btnAdd_Click(object sender, EventArgs e)
{
.....
cbState.DataBindings.Clear();
_Presenter = new AddressPresenter(this);
_Presenter.Render(-1);
......
_Presenter.Add();
cbState.DataBindings.Clear();
.....
}

Friday, April 11, 2008

How to serialize XSD class

Unlike DataSet, there are no build in method to serialize a XSD class. Here is the helper function to do that:

Public Class XsdClassHelper

Public Shared Function Serialize(ByVal t As Type, ByVal obj As Object) As String
Dim ser As New XmlSerializer(t)
Dim sw As New StringWriter
ser.Serialize(sw, obj)
Return HttpUtility.HtmlEncode(sw.ToString())
End Function

Public Shared Function Deserialize(ByVal t As Type, ByVal data As String) As Object
Dim ser As New XmlSerializer(t)
Dim sr As New StringReader(HttpUtility.HtmlDecode(data))
Dim xmlr As New System.Xml.XmlTextReader(sr)
Return ser.Deserialize(xmlr)
End Function
End Class

Thursday, April 10, 2008

Unit Testing ASP.Net MVP Design Pattern using MS test Framework

For GMWBPresenter supporting GMWB User Control, MS Test framework can be used to enable access and test view components such as label, textbox, etc.

(1) We will need a Base Accessor from MS Test Framework in order to access private
and protected member on user controls, namely PrivateObject:

Friend Class BaseAccessor

Protected m_privateObject As PrivateObject

Protected Sub New(ByVal target As Object, ByVal type As PrivateType)
MyBase.New()
m_privateObject = New PrivateObject(target, type)
End Sub

Protected Sub New(ByVal type As PrivateType)
Me.New(Nothing, type)
End Sub

Friend Overridable ReadOnly Property Target() As Object
Get
Return m_privateObject.Target
End Get
End Property

Public Overrides Function ToString() As String
Return Me.Target.ToString
End Function

Public Overloads Overrides Function Equals(ByVal obj As Object) As Boolean
If GetType(BaseAccessor).IsInstanceOfType(obj) Then
obj = CType(obj, BaseAccessor).Target
End If
Return Me.Target.Equals(obj)
End Function

Public Overrides Function GetHashCode() As Integer
Return Me.Target.GetHashCode
End Function

Public ReadOnly Property PrivateObject() As PrivateObject
Get
Return Me.m_privateObject
End Get
End Property
End Class

(2) For specific control access we need extend base:

Friend Class GMWBRiderControlAccessor
Inherits BaseAccessor
Protected Shared m_privateType As PrivateType = New PrivateType(GetType(GmwbRiderControl))

Friend Sub New(ByVal target As GmwbRiderControl)
MyBase.New(target, m_privateType)
End Sub

End Class

(3) To utilize MS Test framework without loading ASP.Net Host, all view elements
have to be initialized using its contructor:

_
Public Class GMWBPresenterTester
Inherits PresenterTesterBase

Private _gmwbUCA As New GMWBRiderControlAccessor(New GmwbRiderControl())

_
Public Sub OnlyIndicatorBSeeGMWBMessage()
Dim manulifePrincipal As JHAPrincipal
manulifePrincipal = AuthenticationFacade.Instance.LogonUser _
( _
"....", _
"..." _
)
Thread.CurrentPrincipal = manulifePrincipal
Dim cif As New ContractInquiryFacade
Dim rider As New ContractRiderList

rider = cif.GetContractRiders(New ContractSearch("NAS", "789065"))
_gmwbUCA.PrivateObject.SetFieldOrProperty("dsRiders", rider)
_gmwbUCA.PrivateObject.SetFieldOrProperty("lnkHelp", New Global.System.Web.UI.WebControls.HyperLink())
_gmwbUCA.PrivateObject.SetFieldOrProperty("pnlGMWBMessage", New Global.System.Web.UI.WebControls.Panel())
_gmwbUCA.PrivateObject.SetFieldOrProperty("lblGMWBMessage", New Global.System.Web.UI.WebControls.Label())
_gmwbUCA.PrivateObject.SetFieldOrProperty("dvGMWB", New Global.System.Web.UI.HtmlControls.HtmlGenericControl())
_gmwbUCA.PrivateObject.SetFieldOrProperty("spnGMWB", New Global.System.Web.UI.HtmlControls.HtmlGenericControl())
_gmwbUCA.PrivateObject.SetFieldOrProperty("lblGMWBValue", New Global.System.Web.UI.WebControls.Label())
_gmwbUCA.PrivateObject.SetFieldOrProperty("dvWithdrwalAmount", New Global.System.Web.UI.HtmlControls.HtmlGenericControl())
_gmwbUCA.PrivateObject.SetFieldOrProperty("spnWithdrwalAmount", New Global.System.Web.UI.HtmlControls.HtmlGenericControl())
_gmwbUCA.PrivateObject.SetFieldOrProperty("lblWithdrawalAmountValue", New Global.System.Web.UI.WebControls.Label())
_gmwbUCA.PrivateObject.SetFieldOrProperty("dvRemainingAmount", New Global.System.Web.UI.HtmlControls.HtmlGenericControl())
_gmwbUCA.PrivateObject.SetFieldOrProperty("spnRemainingAmount", New Global.System.Web.UI.HtmlControls.HtmlGenericControl())
_gmwbUCA.PrivateObject.SetFieldOrProperty("lblRemainingAmountValue", New Global.System.Web.UI.WebControls.Label())
_gmwbUCA.PrivateObject.SetFieldOrProperty("pnlPayments", New Global.System.Web.UI.WebControls.Panel())
_gmwbUCA.PrivateObject.SetFieldOrProperty("spnPayments", New Global.System.Web.UI.HtmlControls.HtmlGenericControl())
_gmwbUCA.PrivateObject.SetFieldOrProperty("lblPaymentsValue", New Global.System.Web.UI.WebControls.Label())
_gmwbUCA.PrivateObject.SetFieldOrProperty("pnlYearsValue", New Global.System.Web.UI.WebControls.Panel())
_gmwbUCA.PrivateObject.SetFieldOrProperty("spnYears", New Global.System.Web.UI.HtmlControls.HtmlGenericControl())
_gmwbUCA.PrivateObject.SetFieldOrProperty("lblYearsValue", New Global.System.Web.UI.WebControls.Label())
_gmwbUCA.PrivateObject.SetFieldOrProperty("spnLIA", New Global.System.Web.UI.HtmlControls.HtmlGenericControl())
_gmwbUCA.PrivateObject.SetFieldOrProperty("lblLIA", New Global.System.Web.UI.WebControls.Label())
_gmwbUCA.PrivateObject.SetFieldOrProperty("spnRLIA", New Global.System.Web.UI.HtmlControls.HtmlGenericControl())
_gmwbUCA.PrivateObject.SetFieldOrProperty("lblRLIA", New Global.System.Web.UI.WebControls.Label())
_gmwbUCA.PrivateObject.SetFieldOrProperty("spnDateLastStepUp", New Global.System.Web.UI.HtmlControls.HtmlGenericControl())
_gmwbUCA.PrivateObject.SetFieldOrProperty("lblDateLastStepUp", New Global.System.Web.UI.WebControls.Label())
_gmwbUCA.PrivateObject.SetFieldOrProperty("spnNextStepUpDate", New Global.System.Web.UI.HtmlControls.HtmlGenericControl())
_gmwbUCA.PrivateObject.SetFieldOrProperty("lblNextStepUpDate", New Global.System.Web.UI.WebControls.Label())
_gmwbUCA.PrivateObject.SetFieldOrProperty("spnStepUpOverride", New Global.System.Web.UI.HtmlControls.HtmlGenericControl())
_gmwbUCA.PrivateObject.SetFieldOrProperty("lblStepUpOverride", New Global.System.Web.UI.WebControls.Label())
_gmwbUCA.PrivateObject.SetFieldOrProperty("spnPreviousGMWBRider", New Global.System.Web.UI.HtmlControls.HtmlGenericControl())
_gmwbUCA.PrivateObject.SetFieldOrProperty("lblPreviousGMWBRider", New Global.System.Web.UI.WebControls.Label())
_gmwbUCA.PrivateObject.SetFieldOrProperty("spnRiderTradeInDate", New Global.System.Web.UI.HtmlControls.HtmlGenericControl())
_gmwbUCA.PrivateObject.SetFieldOrProperty("lblRiderTradeInDate", New Global.System.Web.UI.WebControls.Label())
_gmwbUCA.PrivateObject.SetFieldOrProperty("litDisclaimer", New Global.System.Web.UI.WebControls.Literal())
_gmwbUCA.PrivateObject.SetFieldOrProperty("litDisclaimer", New Global.System.Web.UI.WebControls.Literal())
Dim r As ContractRiderList.gmwbRow = DirectCast(rider.gmwb.Select()(0), ContractRiderList.gmwbRow)
r.vantage_failure_indicator = False
_gmwbUCA.PrivateObject.Invoke("BindControls", rider)

Dim pnlGMWBMessage As Panel = DirectCast(_gmwbUCA.PrivateObject.GetFieldOrProperty("pnlGMWBMessage"), Panel)
MSU.Assert.IsTrue(pnlGMWBMessage.Visible, "Indicator=B should be able to see GMWB Message if any")

'7889078 has GMWB ind=F
rider = cif.GetContractRiders(New ContractSearch("NAS", "676767"))
r = DirectCast(rider.gmwb.Select()(0), ContractRiderList.gmwbRow)
r.vantage_failure_indicator = False
_gmwbUCA.PrivateObject.SetFieldOrProperty("dsRiders", rider)
_gmwbUCA.PrivateObject.Invoke("BindControls", rider)

pnlGMWBMessage = DirectCast(_gmwbUCA.PrivateObject.GetFieldOrProperty("pnlGMWBMessage"), System.Web.UI.WebControls.Panel)
MSU.Assert.IsTrue(Not pnlGMWBMessage.Visible, "Indicator=F should not see GMWB Message")

End Sub

End Class

Note that App.Config under MS Test project must include all the Authorization, Capability and connection information. Also all context access such as HttpContext,
Session Context are not allowed or need to turn off for this kinds of testing strategy.

Tuesday, April 1, 2008

Testing .NET Application Performance

The content summarize this link http://msdn2.microsoft.com/en-us/library/ms998581.aspx

Max Stress Level:
RPS < 100
TTFB < 8sec
cpu% < 60%


Observing Metrics:
CPU%, Queue Length, Context Switching,
RAM Available, Page/Sec
Process Private Bytes, Working Set

ASP.Net
RPS, TTFB, Request in Queue, Request Execution Time

My Comments:
Too much context Switching will kill scalability on SMP since server will be busy not doing real work; Process with large private bytes need to find way to share data such as caching;

Friday, March 7, 2008

ASP.Net Unit Testing in VS.Net 2005

Let us focus on test Presenter in MVP, including Code-Behind and explicitly declared Presenter class..


Presneter Code:


public interface IGMWBView
{
void UpdateGMWBFields();
}
public class GMWBPresenter
{
private bool _ShowNextStepUpDate;
public bool ShowNextStepUpDate
{
get { return _ShowNextStepUpDate;}
set { _ShowNextStepUpDate = value;}
}
public void UpdateGMWBView(IGMWBView view)
{
BindToDomainModel();
view.UpdateGMWBFields();
}
public void BindToDomainModel()
{
ShowNextStepUpDate = true;
}
}

Code-behind:


public partial class _Default : System.Web.UI.Page, IGMWBView
{
protected void Page_Load(object sender, EventArgs e)
{
_Presenter = new GMWBPresenter();
}
protected void Button1_Click(object sender, EventArgs e)
{
this.lbNextStepUpDate.Text = "Changed";
_Presenter.UpdateGMWBView(this);
}
public GMWBPresenter _Presenter ;
#region IGMWBView Members
public void UpdateGMWBFields()
{
}
#endregion


Unit Test Code:


[TestMethod]
[HostType("ASP.NET")]
[UrlToTest("http://localhost/TDDASPNet1.Web/Default.aspx")]
public void CSRCanSeeNextStepUpDate()
{
Page p = ctx.RequestedPage;
Label lb = (Label) p.FindControl("lbNextStepUpDate");
Assert.IsTrue(lb.Visible, "Lable hidden for CSR");
PrivateObject pObj= new PrivateObject(p);
pObj.Invoke("Page_Load", null, EventArgs.Empty);
pObj.Invoke("Button1_Click",null,EventArgs.Empty);
Button btn = (Button)p.FindControl("Button1");
Assert.IsTrue((lb.Text == "Changed"), "Button click failed " + lb.Text);
GMWBPresenter gmwb= (GMWBPresenter) pObj.GetFieldOrProperty("_Presenter");
Assert.IsTrue( gmwb.ShowNextStepUpDate ,"OOps");
}
General Pattern:

/// (1) Set Url
/// (2) Get the page Object
/// (3) using PrivateObject to Invoke: Page_Load, Click
/// this should call Presenter entry pointer.
/// (4) Get Presenter as property and test its properties.

This will allows us to do Unit Tests by calling server code

Monday, February 18, 2008

Use RelayBinding to Expose Your Service to the world through MS Service Bus

To use Microsoft implementation of ESB at http://www.biztalk.net/, We need only use RelayBinding in WCF + Biztalk Service Extension.
In fact Service Config could be as simple as the following:

<configuration>
<system.serviceModel>
<services>
<service name="JQD.HomeLabService">
<endpoint contract="JQD.IHomeLabService"
name="JQD.HomeLabService"
binding="relayBinding" />
...
and the corresponding code are also simple:

static void Main(string[] args)
{
CardSpaceTokenProvider tok = new CardSpaceTokenProvider();
string rHostName = RelayBinding.DefaultRelayHostName;
Uri rHostUri = new Uri(String.Format("sb://{0}/services/{1}/HomeLabService",rHostName,tok.GetUserName()));
ServiceHost rHost = new ServiceHost(typeof(HomeLabService), rHostUri);
rHost.Description.Endpoints[0].Behaviors.Add(tok);
rHost.Open();
Console.ReadLine();
rHost.Close();
}

Clearly, the endpoint "sb://conect.biztalk.net/services/jqd2001/HomeLabService" will be correctly relayed to your server every running code has a correct cardSpace token.

For a piece of code to reach your web services through relay, it only need to know the contract

[ServiceContract()]
public interface IHomeLabService
{
[OperationContract()]
string HeartBeat();
}

and it then need to connect to MS ESB:


static void Main(string[] args)
{
ChannelFactory f = new ChannelFactory(new RelayBinding(),
"sb://connect.biztalk.net/services/jqd2001/HomeLabService");
f.Open();
IHomeLabService ch =f.CreateChannel();
Console.WriteLine( ch.HeartBeat());
f.Close();

Noticeably, RelayBinding is the key to traverse NAT or firewall and without expose your server to the internet directly.

Friday, February 15, 2008

How to correctly use MVP Design pattern

JHA has a presenter implementation in GMWBRiderControl.ascx, which uses GMWBPresenter. This presenter implementation has two problems:
(1) View are updated by function rather than properties, which makes Presenter stateless and unable to utilize overriding, retraction technique commonly seen in OOP/OOD.
(2) Missing commonly seem MVP elements such as IView and Processing() single entry point. This deviation from standard MVP result in a lot of business logic in ASP.Net Code-behind ( such as BindControls function in GMWBRiderControl.ascx.vb) --- completely defeated the purpuse of MVP to separately logic into Presenter.
To correctly this problem, the following changes are needed:

(1) Convert all function into property. e.g


Public Function ShowLifeTimeIncomeAmount() As Boolean
'
If IsGMWBIndicatorA _
OrElse IsGMWBIndicatorH _
Then
Return False
End If
'
Return True
End Function

would be changed to

Private _ShowLifeTimeIncomeAmount As Boolean
Public Property ShowLifeTimeIncomeAmount() As Boolean
Get
Return _ShowLifeTimeIncomeAmount
End Get
Set(ByVal value As Boolean)
_ShowLifeTimeIncomeAmount = value
End Set
End Property

all logic behind those function will be move into the function gettin gdata from Domain Model

Private Sub BindDataFromDomainModelToPresenter()
If IsGMWBIndicatorA _
OrElse IsGMWBIndicatorH _
Then
ShowLifeTimeIncomeAmount = False
End If
'
ShowLifeTimeIncomeAmount = True
' TODO: Change all the other function to property and call here to set it
End Sub


(2) Define "IView" and implement on User Control

Public Interface IGMWBView
Sub UpdateGMWBFields(ByVal IsVantageDown As Boolean)
Sub UpdateWithDrawAllowanceNewLabel(ByVal IsVantageDown As Boolean)
Sub UpdateWithdrawAmountInfo(ByVal IsVantageDown As Boolean)
Sub UpdateBenefitBase(ByVal IsVantageDown As Boolean)
Sub UpdatePaymentStepInfo(ByVal IsVantageDown As Boolean)
Sub MakeAllLIAFieldVisibleForBrokerClient(ByVal IsVantageDown As Boolean)
End Interface


Partial Class GmwbRiderControl
..
Implements IGMWBView


Public Sub UpdateGMWBFields(ByVal _IsVantageDown As Boolean) Implements IGMWBView.UpdateGMWBFields

' Move some code seqment from BindControls to here
End Sub

(3) Implementing business Execution login in Presenter


Public Overridable Sub UpdateGMWBView(ByVal view As IGMWBView)
' Pump Data from DO to P
BindDataFromDomainModelToPresenter()
' Invoke processing function implemented on ASP.Net code-Behind (VIEW)
view.UpdateGMWBFields(_IsVantageDown)
// other logic to change view
End Sub

(4) Call the single entry point on presenter "UpdateGMWBView" in user control in the function "BindControls"

The above steps will result in standard implementation of MVP.

Saturday, February 9, 2008

Using WSE 3.0 X509 Search API to implement RSA Crypto

WSE 3.0 has two functions to retrieve X509 Certificate:

X509Certificate2Collection cert2s = X509Util.FindCertificateBySubjectName(SubjectName, StoreLocation, StoreName.ToString());

X509Certificate2Collection cert2s = X509Util.FindCertificateByKeyIdentifier(ThumbPrint , StoreLocation, StoreName.ToString());

This is much simpler than using COM API or its .Net Wrapper. Using these function, we can write a class encapsulate RSA Crypto with considering of Certification Basic Policy Validation ( such as revocation):


namespace JQD
{
public class X509RSAEncryptor
{
#region Encryption and Decryption
public static string[] EncryptUTF8ToBase64(string ClearTextUTF8, X509Certificate2 cert2ForEncryption, X509Certificate2 cert2ForSignning)
{
if (null == cert2ForEncryption) throw new ApplicationException("null X509 cert for Encryption");
// create Encryption RSA using Public Key only
RSAParameters rsaForEncryptionPublicParam = X509Util.GetKey(cert2ForEncryption).ExportParameters(false);
RSACryptoServiceProvider rsaForEncryption = new RSACryptoServiceProvider();
rsaForEncryption.ImportParameters(rsaForEncryptionPublicParam);
// generate encrypted Base64 string from clear Text
byte[] clearBytes = Encoding.UTF8.GetBytes(ClearTextUTF8);
byte[] cipherBytes = rsaForEncryption.Encrypt(clearBytes, true);
string cipherTextBase64 = Convert.ToBase64String(cipherBytes);
string signatureTextBase64 = "";
if (null != cert2ForSignning)
{
// create Signning RSA using Private Key
RSAParameters rsaForSignningPrivateParam = X509Util.GetKey(cert2ForSignning).ExportParameters(true);
RSACryptoServiceProvider rsaForSignning = new RSACryptoServiceProvider();
rsaForSignning.ImportParameters(rsaForSignningPrivateParam);
// compute Signature using SHA1
byte[] signature = rsaForSignning.SignData(cipherBytes, SHA1.Create());
signatureTextBase64 = Convert.ToBase64String(signature);
rsaForSignning.Clear();
}
rsaForEncryption.Clear();
// send both Data and its singature as Base64 string
return new string[2] { cipherTextBase64, signatureTextBase64 };
}
public static string DecryptBase64ToUTF8(string[] CipherBase64, X509Certificate2 cert2ForDecryption, X509Certificate2 cert2ForVerifySignning)
{
if (null == cert2ForDecryption) throw new ApplicationException("null X509 cert for decryption");
byte[] cipherBytes = Convert.FromBase64String(CipherBase64[0]);
if (null != cert2ForVerifySignning)
{
// create Verify Signning RSA using public Key
RSAParameters rsaForVerifySignningPrivateParam = X509Util.GetKey(cert2ForVerifySignning).ExportParameters(false);
RSACryptoServiceProvider rsaForVerifySignning = new RSACryptoServiceProvider();
rsaForVerifySignning.ImportParameters(rsaForVerifySignningPrivateParam);
// Verify signature
byte[] signature = Convert.FromBase64String(CipherBase64[1]);
try
{
if (!rsaForVerifySignning.VerifyData(cipherBytes, SHA1.Create(), signature)) throw new ApplicationException("Data have been tampered.");
}
finally
{
rsaForVerifySignning.Clear();
}
}
// create Decryption RSA using Private Key
RSAParameters rsaForDecryptionPrivateParam = X509Util.GetKey(cert2ForDecryption).ExportParameters(true);
RSACryptoServiceProvider rsaForDecryption = new RSACryptoServiceProvider();
rsaForDecryption.ImportParameters(rsaForDecryptionPrivateParam);
// Decrypt and convert to Clear Text
byte[] clearBytes = rsaForDecryption.Decrypt(cipherBytes, true);
rsaForDecryption.Clear();
return Encoding.UTF8.GetString(clearBytes);
}
#endregion
#region X509 finder
public static X509Certificate2 FindX509Certificate2(string SubjectName, StoreLocation StoreLocation, StoreName StoreName)
{
X509Certificate2Collection cert2s = X509Util.FindCertificateBySubjectName(SubjectName, StoreLocation, StoreName.ToString());
X509Certificate2Collection validCert2s = Validate(cert2s);
if (validCert2s.Count == 0) return null;
return validCert2s[0];
}
public static X509Certificate2 FindX509Certificate2(byte[] ThumbPrint,StoreLocation StoreLocation, StoreName StoreName)
{
X509Certificate2Collection cert2s = X509Util.FindCertificateByKeyIdentifier(ThumbPrint , StoreLocation, StoreName.ToString());
X509Certificate2Collection validCert2s=Validate(cert2s);
if (validCert2s.Count == 0) return null;
return validCert2s[0];
}
private static X509Certificate2Collection Validate(X509Certificate2Collection cert2s)
{
X509Certificate2Collection validCert2s = new X509Certificate2Collection();
foreach (X509Certificate2 cert2 in cert2s)
{
// applies the base policy to that chain, Note that on Win2k3, the basic policy
// check conformance to RFC3280, which include revocation for X509 Cert2.
bool IsConformedToBasicPolicy = cert2.Verify();
// Some other simple checking
bool IsArchived = cert2.Archived;
bool IsExpired = (DateTime.Now > cert2.NotAfter)
bool IsNotActive =( DateTime.Now < cert2.NotBefore);
if (IsConformedToBasicPolicy && !IsArchived && !IsExpired && !IsNotActive)
{
validCert2s.Add(cert2);
}
}
return validCert2s;
}
#endregion
}
}

The usage is also simple as illustrated by the following:


class Program
{
static void Main(string[] args)
{
string msg="This is a secret.";
Console.WriteLine(msg);
X509Certificate2 cert2Enc;
X509Certificate2 cert2Sig;
string subjectName1="CN=idmcertid, OU=Internet Infrastructure, O=\"Blah Blah, Inc.\", L=Boston, S=massachusetts, C=US";
byte[] thumbPrint1=new byte[] { 0xb7, 0x4a, ....., 0x9c, 0x0c };

cert2Enc = X509RSAEncryptor.FindX509Certificate2(subjectName1,StoreLocation.LocalMachine, StoreName.My);
cert2Sig = X509RSAEncryptor.FindX509Certificate2("CN=XYZ Company", StoreLocation.CurrentUser, StoreName.My);
cert2Sig = X509RSAEncryptor.FindX509Certificate2(subjectName1, StoreLocation.CurrentUser, StoreName.My);
string[] cipherBase64 = X509RSAEncryptor.EncryptUTF8ToBase64(msg, cert2Enc, cert2Sig);
Console.WriteLine(cipherBase64[0]);
Console.WriteLine();
Console.WriteLine(cipherBase64[1]);
Console.WriteLine();
X509Certificate2 cert2Dec=X509RSAEncryptor.FindX509Certificate2(subjectName1,StoreLocation.LocalMachine, StoreName.My);
Console.WriteLine(X509RSAEncryptor.DecryptBase64ToUTF8(cipherBase64, cert2Dec, cert2Sig));
Console.ReadLine();
}

Note that when install Private Key, Must check " Mark this key Exportable" to avoid " Key in invalid state" exception. Also remember RSA encryption requires Receiving party to hold private key for decryption and hold public key for verify signning. Sending party hold just the opposite. So Receiveing party only need sending party's public key, and so on. This means it is better to have two computers with two set of good X509 Cert to run the code. If the cert is really invalid as seen in MMC IDE, IsConformedToBasicPolicy will be false.
Finally, There are no need to install WSE 3.0. You only need to have a copy of Microsoft.Web.Services3.dll and add its reference to your project.

Wednesday, February 6, 2008

Using Ajax Behavior to capture Before Unload event

In my previous post
http://jqdjha.blogspot.com/2008/02/handling-onbeforeunload-event-double.html
I discussed how to use setTimeout to avoid double firing onbeforeunload event in IE6 and IE7.
If the web page support ASP.Net Ajax Library, then we can use Ajax behavior to inject our code from that previous post to window, body, frameset tag, all of which support beforeunload event in IE. Note that we can control whether to attach "NavAway Behavior" or not:

<script>
      $create(JQD.NavAwayWarning,null,null,null,window);
</script>


var globalIsThe2ndNavAway=false;
Type.registerNamespace("JQD");


JQD.NavAwayWarning= function (element) {
      JQD.NavAwayWarning.initializeBase(this,[element]);
      this._navAwayHandler = Function.createDelegate(this,this._onNavAway);
}


JQD.NavAwayWarning.prototype = {

_onNavAway : function (e) {

      if (globalIsThe2ndNavAway) return;
      window.event.returnValue="You are losing changes."
      globalIsThe2ndNavAway=true;
      setTimeout("globalIsThe2ndNavAway=false;",0) ;

},

initialize: function () {
      JQD.NavAwayWarning.callBaseMethod(this,'initialize');
      $addHandler(this.get_element(),'beforeunload',this._navAwayHandler);
},

dispose: function() {
      JQD.NavAwayWarning.callBaseMethod(this,'dispose');
      $removeHandler(this.get_element(),'beforeunload',this._navAwayHandler);
}

}
JQD.NavAwayWarning.registerClass('JQD.NavAwayWarning', Sys.UI.Behavior);

Monday, February 4, 2008

Handling OnBeforeUnload Event Double Firing

IE 6.0 and 7.0 will fire onbeforeunload event twice if there is a link button on the ASP.Net page ( or in general an anchor with href set to a postback javascript). Here is the simple markup that "double firing" when postback happens:

<body onbeforeunload="alert();">
<form id="form1" runat="server">
<asp:LinkButton ID="LinkButton1" runat="server">LinkButton
</form>
</body>

The following javascript will make sure only the 1st Navigate-away event will invoke a message


var IsThe2ndNavAway=false;
function OBUL()
{
if (!IsThe2ndNavAway)
{
event.returnValue="Are you sure you want to lose your changes."
IsThe2ndNavAway=true;
setTimeout("IsThe2ndNavAway=false;",0)
}
}
Note that the 1st Nav-Away will execute the code to show message, etc. At the same time, 2nd will be blocked at function entry point since it is on the same thread as 1st Nav-Away.
As user dismiss the Message Box, the 2nd Nav-Away can no longer get into if-code block.
At the same time setTimeout will execute "IsThe2ndNavAway=false;" on a different thread so that the next round of "1st Nav-Away, 2nd Nav-Away" can contnue.

Saturday, January 26, 2008

How to use Footnote to annotate a Web Page

JHA Fundperformance uses Genearic List to render foonotes for product title and each fund. In general render footnotes requires insert DataBinding Expression at the right location and keep counter updated.
Here are the code for simplest Footnoting a Web Page:

<%@ Page Language="C#" ... Inherits="TestFootNotes._Default" %>

Test Footnoting a web page<br />
Web Pages are structure less<%# RenderFootNoteNumber("struct") %> writting of information. Therefore, annotate certain part of it requires putting a structure at certain location<%# RenderFootNoteNumber("location") %>.
<hr />
Footnotes: <br />
<asp:Repeater ID="Repeater1" runat="server" DataSource="<%# _FootnoteList %>">
<ItemTemplate>
[<%# Eval("key") %>]. <%# Eval("value") %><br />
</ItemTemplate>
</asp:Repeater>



public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
DataBind();
}
public Dictionary _FootnoteList = new Dictionary();
public string RenderFootNoteNumber(string AnnotationID)
{
int i=_FootnoteList.Count;
_FootnoteList.Add(i + 1, "Footnote Content for Annotation of " + AnnotationID );
return "<sup>["+_FootnoteList.Count.ToString()+"]</sup>";
}
}


The key is to have _FootNoteList as a holder of footnote counter and content as we going through the Web Page.

Friday, January 25, 2008

MVP Design Pattern vs. MVC design Pattern

JHA FundPerformance uses MVP ( Model View Presenter) design Pattern to render Fund Performance Data. In fact, ASP.Net uses Code-Behind to convert user action/events to Request to data. But JHA FundPerformance add one more layer MTData as DataObject. Specifically, Object Data Source explicitly separate Select/Insert/Update actions from other User Events handler in ASP.Net code-behind and that is the reason we called it "DataObject Presenter".

As a general rule, MVP requires user requests hit View first and then decide which Presenter method to call. On the contrary, MVC (Model View Controller) as implemented in ASP.Net MVC framework CTP routes user requests to Controller first and then decide which View to render.

Thursday, January 24, 2008

Two way databinding in ASP.net

It is very easy to one-way databind from code behind to Markup:

<%@ Page CodeBehind="Default.aspx.cs" Inherits="WebApplication1._Default" %>
<%# Name %>

namespace WebApplication1
{
public partial class _Default : System.Web.UI.Page
{
public string Name
{
get { return "JHA"; }
}

The other way from markup back to code-behind is a little difficult to write but Microsoft has created "Object Data Source" to make picking up data much easier:


<Form runat="server">
<asp:textbox runat="server" id="tb1">
<asp:objectdatasource runat="server" id="ods1" runat="server"
TypeName="WebApplication1._Default" SelectMethod="Test">
<SelectParameters>
<asp:ControlParameter ControlID="tb1" Name="Email" />
</SelectParameters>
</asp:objectdatasource>
<asp:Button runat="server" />
</Form>

In essence, Databinding become two-way using ObjectDataSource Select, update, Insert methods.


protected override void OnPreRender(EventArgs e)
{
DataBind();
ods1.Select();
}
public void Test(string Email) // email parameter will be filled by text from tb1
{
}

How to build NUnitASP container using HTML source

JHA site uses nested Master Page to host content, eg. PrivateBase is inside Site.Master

<%@ Master MasterPageFile="Site.Master" Inherits="ManulifeUSA.Common.Web.UI.UserControls.PrivateBase" %>

<asp:Content ContentPlaceHolderID="mainContentPlaceHolder">

<asp:PlaceHolder ID="rightPlaceHolderContainer" runat="Server">

Note that "rightPlaceHolderContainer" is nested inside "mainContentPlaceHolder" as well.

This structure will generate the following HTML sourcece in AddressChangePage.aspx as follows:

<input type="submit" id="ctl00_ctl00_mainContentPlaceHolder_rightPlaceHolder_btnUpdateAddress" />

Note that the id is the clue for constructing containers for NUnitASP testing, namely, ctl00 referes to SiteMaster, the 2nd ctl00 refers to PrivateBase, etc.
Since I have abstracted "ctl00_ctl00_mainContentPlaceHolder" into the following Test Container:

Public Class PrivateBaseTester
Inherits UserControlTester
Sub New(ByVal SiteID As String, ByVal PrivateBaseID As String, ByVal rightPlaceHolderID As String)
MyBase.New(rightPlaceHolderID, New UserControlTester("mainContentPlaceHolder",

New UserControlTester(PrivateBaseID, New UserControlTester(SiteID))))
End Sub
End Class


You would need to construct containers for btnUpdateAddress in the following manner:

Dim master As New PrivateBaseTester("ctl00", "ctl00", "rightPlaceHolder")

Dim btnUpdateAddress As New ButtonTester("btnUpdateAddress", master)

I hope this will help developers understand the approach to create container chains.

Wednesday, January 23, 2008

Use Enum in Object Data Source

JHA Performance Page uses Object Data Source to pick up Http Form Submitted data --- the so called two way data binding. For April 08 release, we need introduce Enum Type to model 3-states for Performance Type and the following class could be used to pick up Enum as input.

Public Enum PerformanceTypeEnum
Daily
Standard
AdjNonStandard
End Enum

Public Class MTData
public Function GetData(pt as PerformanceTypeEnum) as IList
End Class

<asp:ObjectDataSource id="ods1" TypeName="MTData" SelectMethod="GetData" OnSelecting="ods1_Selecting" >
<asp:SelectParameters >

<asp:Parameter Name='pt" type="String" >

Public Sub Ods1_Selectting (sender as Object, e as ObjectDataSourceEventArgs)
e.InputParameters("pt") = PerformanceTypeEnum.Daily
End Sub


Note that the parameter defined in the markup cannot have PerformanceTypeEnum as its type