Wednesday, December 10, 2008

Custom RoleProvider notes

I finally completed my custom RoleProvider that uses NHibernate to retrieve data from the database. Below I have listed my notes on this topic.

Issue: My RoleProvider would not work if I overrode the default constructor.
Solution: Override the Initialize method and put your code there instead.

Issue: The RoleProvider class cannot access HttpContext.Current.Session.
Solution: There really isn't a solution. It's just a fact.


Custom MembershipProvider notes

I finally completed my custom MembershipProvider that uses NHibernate to retrieve data from the database. Below I have listed my notes on this topic.

Issue: I could not get the "Web Site Administration Tool" to create a user without giving me an error.

Solution: Make sure that the following property returns true:
public override bool RequiresQuestionAndAnswer
If it was false, I would get a generic error that made absolutely no sense:

The following message may help in diagnosing the problem: Exception has been thrown by the target of an invocation. at System.RuntimeMethodHandle._InvokeMethodFast(Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner) at System.RuntimeMethodHandle.InvokeMethodFast(Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeTypeHandle typeOwner) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Web.Administration.WebAdminMembershipProvider.CallWebAdminMembershipProviderHelperMethodOutParams(String methodName, Object[] parameters, Type[] paramTypes) at System.Web.Administration.WebAdminMembershipProvider.CreateUser(String username, String password, String email, String passwordQuestion, String passwordAnswer, Boolean isApproved, Object providerUserKey, MembershipCreateStatus& status) at System.Web.UI.WebControls.CreateUserWizard.AttemptCreateUser() at System.Web.UI.WebControls.CreateUserWizard.OnNextButtonClick(WizardNavigationEventArgs e) at System.Web.UI.WebControls.Wizard.OnBubbleEvent(Object source, EventArgs e) at System.Web.UI.WebControls.CreateUserWizard.OnBubbleEvent(Object source, EventArgs e) at System.Web.UI.WebControls.Wizard.WizardChildTable.OnBubbleEvent(Object source, EventArgs args) at System.Web.UI.Control.RaiseBubbleEvent(Object source, EventArgs args) at System.Web.UI.WebControls.Button.OnCommand(CommandEventArgs e) at System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) at System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) at System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) at System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint)

Issue: I ran into a couple of examples that verify the password by unencoding the one in the database and then comparing it to the user's entry.

Solution: I recommend that you do it the same way that Microsoft did it. That is to encode the user's entry and compare it to the encoded database entry. It eliminates the need to have an unencode method.

Other things worth noting:

If you are going to use Encryption encoding (as opposed to clear text or hash encoding), don't forget to generate your own MachineKey. Here are a few link on that topic:


Thursday, December 4, 2008

WCF & the IIS Require secure channel (ssl) setting

After turning on "Require secure channel (SSL)" on the IIS 6.0 Directory security tab (secure communications dialog), I lost several hours today trying to figure out why my WCF services stopped working and started giving me this error:
[System.ServiceModel.ServiceActivationException] = {"The requested service, '' could not be activated. See the server's diagnostic trace logs for more information."}

I tried a variety of things, but when I finally broke down and ran the diagnostic trace I got the following:
The service '/DoIt/MyService.svc' cannot be activated due to an exception during compilation. The exception message is: Could not find a base address that matches scheme http for the endpoint with binding MetadataExchangeHttpBinding. Registered base address schemes are [https]..

All my services use https to send data; however, my metadata exchange binding, which I intended to remove/comment out later, use http. When I changed all my metadata exchanged end points to use mexHttpsBinding instead of mexHttpBinding and all my behaviors to use <servicemetadata httpsgetenabled="true"> instead of <servicemetadata httpgetenabled="true">, everything started working again.

I figured that the http endpoints would just be ignored or if accessed permissioned denied because all addresses must use a secure channel (https). It appears that I was wrong and that it causes a compilation error.

Wednesday, July 30, 2008

WCF DateTime field adjusted automatically for time zone

Well, I got bit by DateTime serialization yesterday. Our server is in the U.S. Central Standard Time (CST) zone and our client is in the U.S. Eastern Standard Time (EST) zone. All data saved in the database is relative to the client and NOT the server. So when the client wants to see all data for today, only the date is specified using DateTime.Today. So when the EST client sent a date of 7/29/08 00:00:00 AM to our WCF webservice on the CST server, the date was converted to 7/28/08 11:00:00 PM. The symptom from the client's point of view was that they could never retrieve data for today.

According to Coding Best Practices Using DateTime in the .NET Framework there are several ways to work around this; however, they missed one. If you change your dates so that DateTimeKind is unspecified, the XML serializer will NOT try to convert them. For example, this code will convert a date to DateTimeKind.Unspecified:

DateTime newDate = DateTime.SpecifyKind(oldDate, DateTimeKind.Unspecified);