Previously I was running a number of remote tests on a new Powershell module to ensure it performed as expected when invoked via Powershell Remoting.
Labels: Powershell, Reflector
I've recently been testing a Powershell module I developed to see if it worked as expected via Powershell Remoting.
Labels: Powershell
I've been working on a lot of back-end data access code lately and I
needed a little change. So when I arrived in the office last Friday I
boldly assigned a web-developer task to myself (its been at least year
since I wrote some serious ASP.NET)
This particular (very low priority) task was based on a customer
request for an email AutoSuggest TextBox. I sensed a Friday challenge.
In our web application we have a TextBox where the user can input
additional email addresses of the people they would like notified when
the workflow status of a particular process has changed.
For example, if this were an on-line shopping application (it is not),
this field would contain email addresses of people that would be
emailed when an item is now in stock. The email addresses need to be
semicolon delimited and in full smtp format.
For example:
client1@contoso.com;client2@contoso.com
The Change Request was simple, the TextBox should be replaced with a
control that acts like the 'To' field when composing an email in Outlook
In terms of behaviour: As a name is typed, the TextBox should
'autosuggest' matching contacts and when selected should display them
in the TextBox. It should obviously be able to accept multiple
contacts, in addition to contacts that do not exist (well, could not be
looked up). In this case the source of the contacts for lookup was the
corporate Active Directory (and not just users, but email distribution
groups as well). In addition, the control should display the full name
and not the underlying email address, just as outlook does.
I was pleasantly surprised how easy this was!
Since our Web Application is ASP.NET 4.0, we already use jQuery
throughout. With Google to rescue, I download and configured Drew
Wilsons jQuery autosuggest plugin:
http://code.drewwilson.com/entry/autosuggest-jquery-plugin
The plugin handles keydown events and will query a specified URL
supplying what the user has typed so far in the query string. In
response it expects a JSON formatted array of suggestions. This sounded
like a perfect match for a WCF WebService (which also turned out to be
surprisingly simple):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Web;
using System.Text.RegularExpressions;
using System.DirectoryServices;
namespace ExampleAutosuggest
{
[ServiceContract(Namespace = "")]
[ServiceKnownType(typeof(string))]
[ServiceKnownType(typeof(string[]))]
public interface IContactSuggestionService
{
[OperationContract]
[WebGet(UriTemplate = "/Search?q={q}", BodyStyle = WebMessageBodyStyle.Bare, ResponseFormat = WebMessageFormat.Json)]
Contact[] Suggest(string q);
}
public class ContactSuggestionService : IContactSuggestionService
{
public Contact[] Suggest(string q)
{
DirectoryEntry de = new DirectoryEntry();
de.Path = "LDAP://OU=Users,DC=prod,DC=ad,DC=contoso,DC=com";
de.AuthenticationType = AuthenticationTypes.Secure;
DirectorySearcher deSearch = new DirectorySearcher();
deSearch.SearchRoot = de;
deSearch.Filter = "(&(objectClass=user) (cn=" + q + "*))";
deSearch.SizeLimit = 10;
SearchResultCollection results = deSearch.FindAll();
// Get the SMTP email address for the users
IList list = new List();
foreach (SearchResult result in results) {
try {
DirectoryEntry der = result.GetDirectoryEntry();
string email = der.Properties["mail"].Value.ToString();
string name = der.Properties["name"].Value.ToString();
list.Add(new Contact(name, email));
}
catch (Exception) {
}
}
return list.ToArray();
}
}
[DataContract]
public class Contact
{
public Contact(string Name, string Email)
{
this.DisplayName = Name;
this.Email = Email;
}
[DataMember(IsRequired = true, Name = "name")]
public string DisplayName { get; set; }
[DataMember(IsRequired = true, Name = "value")]
public string Email { get; set; }
}
}
Download Project
Finally, I needed to make some changes to Drew's code so the control's state would survive both partial and full postbacks with ASP.NET. Drew's implementation saves the list of values to a hidden input field created by the script, this field only contains the underlying values, which may be different to what was displayed to the user and is only designed to be retrieved by the server during the PostBack. Once the Post Back all state is lost. This was address by adding a hidden form field to the page and supplying the elements id to the autosuggest code when it was attached. The autosuggest's code was then modified to serialise and deserialise its state (via JSON) to the hidden field whenever a contact was added or removed. The ASP.NET framework takes care of maintaining the value of the hidden field between postback and so the state of the autosuggest is also maintained. In the end we stored the serialised state of the auto-suggest directly in the database, which meant we could render it very quickly without having to perform Active Directory lookups on each contact before displaying the page. Im sure our DBA's would'nt be happy about this, but at least its not XML (which drives them crazy).
Download Customised Autosuggest
Note:
This Javascript is not perfect, but let me know if you find any bugs
Here's a little trick to determine if the current script is being run via Remoting;
$remote = (test-path variable:PSSenderInfo) -and
(($PSSenderInfo -ne $null) -and
($PSSenderInfo.GetType().Name -eq 'PSSenderInfo'))
Labels: Powershell
There appear to be a few bugs in the way HNibernate 2.1.2GA handles
Oracle functions. You can find the official documentation here:
http://docs.jboss.org/hibernate/stable/core/reference/en/html/querysql.html#sp_query
Issue 1: Calling Functions and
Procedures in an Oracle Package
The first bug appears in the 2.1.2 implementation of
CalleableParserNHibernate.Engine.Query.CallableParser
The CalleableParser class is reponsible for parsing 'call' expressions
enclosed in '{ }' in mappings, for example:
The Parse(...) method relays on the following RexEx expression to match
the function names:
"\{[\S\s]*call[\s]+([\w]+)[^\w]"
Unfortunately this expression will not match names which exist in an
Oracle package. This is because such names require a scope, separated
by using standard '.' notation.
For example:
MyOraclePackage.MyFunction
In this scenario the above RegEx expression will match only
'MyOraclePackage', which
results in a failed call.
(take a look at the posts here https://forum.hibernate.org/viewtopic.php?f=25&t=968269&p=2434379#p2434379)
At the moment our team have worked around this by creating wrapper
functions that call the real function in the appropriate package, far
from ideal at the moment, but the project is very short on time.
Issue 2: Return Scalar from
Oracle Function
The second bug, which may be more difficult to fix arises when you try
to return a scalar value from an Oracle Function, as expressed in the
same example mapping:
As required by NHibernate the Oracle function we have
developed, called: 'get_id_from_name'
returns an Oracle REFCURSOR which contains the scalar value. The
problem arises when we try to determine the column name to put in out
nHibernate mapping file.
If the column name is incorrect NHibernate throws a 'could not execute
query exception', and if we look further into the ODP.NET Trace
File
(you have to turn this on with a Registry Editor) you'll see:
ODP error code=-1502; ODP
message=Unable to find specified column in
result set
So what's happening?
After running the function, NHibernate starts processing the resultset
from the DataReader and eventually calls into some code that attempts
to get the ordinal value (column index) for the named
column. However, since the name we made up does not exist an exception
is thrown. Certainly, there doesnt appear to be a way to specify the
column names in a REFCURSOR from Oracle.
So how do we get the column name?
Well I still haven't figured this out, but after looking at it in the
debugger I found a private member in Oracles DataReader, sadly I cant
quite recall how I found it, but in our case it was always:
:B1
So the final mapping looks like:
I spoke to our Oracle DBA's who recalled the Oracle usually
refer to the results as :B1, :B2, etc. So if your having this problem, try :B1
as the column name
For reference these issues were encountered with Oracle 10G and ODP.NET
2.112.1.0
Catching custom errors/exceptions from Oracle procedures in nHiberntate
Posted by Ian Clegg at 13:38I'm lucky (or unlucky) enough to work with a pretty smart Oracle DBA on my current project. Although the fact that he is smart doesn't change my somewhat negative view of Oracle (purely for a developer tools standpoint).
Interestingly, the decision to use Oracle for this system came down to the number of in-house Oracle DBA's and Unix Administrators who far outnumber the Windows engineers (and who appear to be much harder to outsource/offshore)
Anyway, we ran into a problem on Friday which took us an entire afternoon to figure out. Our Oracle DBA had developed a PL/SQL procedure that would perform some calculations and populate another table with the results. It was decided that this particular task was best suited to PL/SQL for performance reasons, since it touched on a large number of entities in the database, whilst also doing a number of inserts.
The procedure in question look a little like this:
FUNCTION xxxx(...) RETURN INTEGER
IS
....
-- Declare bespoke error codes
e_invalid_cmp_class EXCEPTION;
PRAGMA EXCEPTION_INIT(e_invalid_cmp_class, -20001);
...
BEGIN
IF (v_cmp_class_check = FALSE) THEN
RAISE e_invalid_cmp_class;
END IF;
END xxxx;
As you can see our DBA was diligently raising useful error messages in the custom error range when something unexpected occurs. In fact I was alarmed that DBA's were capable of such well structured and organised code.
In this solution, all data access from the application passes through a generic .NET based data-layer, which is largely built on the nHibernate (think Repository pattern).
The problem was that the custom/user exceptions raised in PL/SQL would simply vanish. Indeed, it was as if the exceptions were not even being raised. nHibernate would execute the procedure and continue on as if the call had been successful. Conversely, standard Oracle exceptions did appear to work - for example, if we called a procedure that did not exist. Of course, our DBA was happy to blame nHibernate (as all DBAs do).
After double checking everything, we put together the smallest sample code we could to reproduce the problem. We eliminated nHibernate and our entire DAL, and ended up with about 10 lines of C# using ODP.NET. Still the exceptions were not being caught, or propagated. At this point, I was happy to blame Oracle and ODP.NET - however, this didn't help find the solution.
In the end we stumbled across if by trial and error:
You MUST the RAISE_APPLICATION_ERROR procedure, and not RAISE for user exceptions
Rewriting the procedure (shown below) ensured that the exceptions propagated through our DAL
There appears to be no clear documentation explaining the difference between this procedure and the language keyword 'RAISE'. However, without the procedure, user exceptions don't seem to arrive at the client. In testing our DBA was able to see them on the server, but the ODP.NET client simply ignore's or doesn't even get to see them.
I have a love hate relationship with that online Bizarre Bazaar called eBay, sometimes you can grab a genuine bargain or a part that nobody else seems to stock...but you have to be on your guard for fakes. In fact I've stopped buying specific goods on eBay because I've had to return fakes on too many occasions (well two occasions)


