Wednesday, September 8, 2010

Fluent NHibernate Spring.NET Session Factory

Fluent NHibernate requires a Spring.NET session factory other than the usual LocalSessionFactoryObject. The following implementation makes a few assumptions:

  1. We’re using MS SQL Server 2008
  2. All configuration files are fluent
  3. Connection string information will be available in the connectionStrings section of the configuration file (web.config or app.config).

Obviously you could adjust or generalize these, but this is sufficient for me now.

using System;
using System.Configuration;
using System.Reflection;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using Spring.Data.NHibernate;

namespace jsj.Spring.NHibernate
{
public class FluentNHibernateLocalSessionFactoryObject
: LocalSessionFactoryObject
{
public string[] FluentNhibernateMappingAssemblies { get; set; }

public string ConnectionStringName { get; set; }

protected override void PostProcessConfiguration(
global::NHibernate.Cfg.Configuration config )
{
base.PostProcessConfiguration( config );

FluentConfiguration fluentConfig = Fluently.Configure( config )
.Database( MsSqlConfiguration.MsSql2008.ConnectionString(
c => c.FromConnectionStringWithKey(
ConnectionStringName ) ) );

Array.ForEach( FluentNhibernateMappingAssemblies,
assembly => fluentConfig.Mappings(
m =>
m.FluentMappings.AddFromAssembly(
Assembly.Load(assembly))) );

fluentConfig.BuildSessionFactory();
}
}
}








It can be configured along the lines of







<object id="NHibernateSessionFactory" 
type
="jsj.Spring.NHibernate.FluentNHibernateLocalSessionFactoryObject, jsj.Spring">
<property name="DbProvider" ref="DbProvider"/>
<property name="ConnectionStringName" value="LocalSqlServer"/>
<property name="ExposeTransactionAwareSessionFactory" value="true" />
<property name="FluentNhibernateMappingAssemblies">
<list>
<value>jsj.Data</value>
</list>
</property>
<property name="HibernateProperties">
<dictionary>
<entry key="connection.provider"
value
="NHibernate.Connection.DriverConnectionProvider"/>
<entry key="dialect" value="NHibernate.Dialect.MsSql2008Dialect"/>
<entry key="connection.driver_class" value="NHibernate.Driver.SqlClientDriver"/>
<entry key="cache.use_second_level_cache" value="true" />
<entry key="cache.provider_class"
value
="NHibernate.Cache.HashtableCacheProvider,NHibernate" />
<entry key="max_fetch_depth" value="0" />
</dictionary>
</property>
</object>










and it will use the connection string identified as LocalSqlServer from the configuration file.

Wednesday, October 28, 2009

Stupid SharePoint Error Message

Anyone who has done any development for SharePoint runs across cryptic error messages from the browser sooner rather than later. Especially helpful is "An unexpected error has occurred" without error logging anywhere around.

This is readily addressed in web.config setting, and was most helpfully capture a year or so ago by Evren Ayan. I'm just repeating it here to provide myself with an easy-(for-me)-to-find reference for the next time I forget.

As with any .NET development, turn off custom errors in :

<customErrors mode="Off" />

Additionally, again in , set the compilation debug flag to true:

<compilation debug="true" ... >

Finally, in the section, set the CallStack value in SafeMode to true:

<SafeMode CallStack="true" ... />

Of course, the output is user hostile, but that's what we want in development anyway.

Monday, April 28, 2008

HelloSpring -- A Spring.NET Web Part

So, the other day I got asked to try embedding some existing functionality in a web part to use in SharePoint.  The existing functionality was based on Spring.NET and NHibernate and provided services for the misty, mystical world of ORM database access.

Turns out, there is nothing mystical about doing this -- although it took a while to iron out all the wrinkles -- but I thought I'd give a few helpful hints on getting things set up ok.

The quick and dirty cheat-sheet:

  1. Sign all DLLs that will be used by your web part, including Spring.NET DLLs if they aren't already.
  2. Copy the DLLs used by your web part to the correct /bin directory for the SharePoint web application.
  3. Locate the appropriate Spring.NET configuration resources (file based or included in a DLL).
  4. Let the SharePoint web application know about Spring.NET by editing its web.config to point to the configurations it should be interested in.
  5. Set the SharePoint web application trust level to Full.
  6. Write your web part using the same DLLs from step 1 & 2.  Include code to retrieve the Spring.NET ApplicationContext and use to to find the objects you need.
  7. Deploy your web part.

John Hancocks Everywhere

Repeat after me:  everything must be signed.  Everything must be signed.

SharePoint web parts are typically deployed to the GAC and as such must have strong naming in operation.  One of the requirements of a signed assembly is that all the assemblies it reference must also be signed. 

In particular, the Spring.NET assemblies need to be signed.  With Spring.NET 1.1.1, the release assemblies are signed but the debug assemblies aren't.  The source code download does not include the signing settings in the projects.  So, you can either use the release libraries provided in the default /bin directory of the distribution, or you can go into Visual Studio and add signing to all the projects and rebuild (in which case the new DLLs will be in the /build directory).

Nothing is special about Spring.NET here -- if you are using any other DLL, it, too, will need signing.  So, if instead of using Spring.NET directly, you want to use a DLL that in its turn uses Spring.NET, that DLL will need to be signed as well.  If you are using libraries that were built for a straight-forward ASP.NET application, they probably won't be signed right off the bat either.

Sign'em and move on.  See How to: Sign an Assembly if you need a boost on this.

A Stupid Web Part Described

To demonstrate the integration, we're going to create a web part that will write a greeting on a SharePoint page base on a web part property, State.  Different greetings will be issued for Wisconsin, Minnesota, any other non-blank state, and a blank state property.

Wisconsin:
image

Minnesota:
image

Iowa:
image

Empty
image

A Stupid Spring.NET DLL

To implement this, I create a dead-simple Sprint.NET cloud of classes and configuration wrapped up in a DLL.  There is one interface and two classes:

using System;

namespace HelloSpringSpringPart
{
public interface IHelloSpring
{
String Message { get; set; }
}

public class HelloWisconsin : IHelloSpring
{
public String Message { get; set; }
public HelloWisconsin()
{
}
}

public class HelloMinnesota : IHelloSpring
{
public String Message { get; set; }
public HelloMinnesota()
{
}
}
}


Both of these classes are really identical (I said it was stupid), but if you need, you can pretend in your mind they do something more fun -- maybe adding an ena, eh, or uff-da to the message in the property setter.



The configuration file, HelloSpring.xml, is equally simple:






<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net">
<object id="Wisconsin"
type="HelloSpringSpringPart.HelloWisconsin, HelloSpringSpringPart">
<property name="Message" value="Guten Tag Wisconsin!"/>
</object>
<object id="Minnesota"
type="HelloSpringSpringPart.HelloMinnesota, HelloSpringSpringPart">
<property name="Message" value="Hello Minnesota!"/>
</object>
</objects>



Notice we are inserting the value for Message as part of the object definition.  Make sure the project is signed and build the DLL.



A Stupid Web Part Implemented



In the web part code itself, we need to accomplish two things:




  1. Provide the State property.


  2. Do the right thing when it comes time to put the web part on the page.



Since we're going to use the stupid DLL and Spring.NET, we add references to HelloSpringSpringPart (the above DLL) and Spring.Core.



The property is the normal over-attributed block:



[Personalizable(PersonalizationScope.Shared),
WebBrowsable(true),
WebDisplayName("State Name"),
WebDescription("State to greet"),
Category("Configuration")]
public string State { get; set; }



CreateChildControls gets the Spring.NET context.  This particular version of GetContext pulls its information from the information specified starting in web.config.  Se the next section for that setup.  It also checks the value of the State property and adds a Literal control with appropriate text.  The only mildly interesting section is where we check to see if a Spring.NET object exists in the context before retrieving it and getting its message:



protected override void CreateChildControls()
{
IApplicationContext applicationContext =
Spring.Context.Support.ContextRegistry.GetContext();

Literal stuff = new Literal();
if (string.IsNullOrEmpty(State))
{
stuff.Text = "State is empty";
}
else if (applicationContext.ContainsObject(State))
{
IHelloSpring helloSpring =
(IHelloSpring)applicationContext.GetObject(State);
stuff.Text = helloSpring.Message + "<br/>";
}
else
{
stuff.Text = "No special greeting for " + State;
}

Controls.Add(stuff);
}






Deploy, but do not yet activate the web part.



Wiring it up



Finally, we need to hook all the pieces together.  We do this editing the web.config file for the SharePoint web application we are going to place the web part on.  For the default application, it would be something along the lines of




C:\Inetpub\wwwroot\wss\VirtualDirectories\80\web.config




First we have to tell the web app that in addition to SharePoint and all those other goodies, we're going to include a spring configuration block.




<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
</sectionGroup>
<sectionGroup name="SharePoint">
<section name="SafeControls" ... />
< ... >
</sectionGroup>
<sectionGroup name="System.Workflow.ComponentModel.WorkflowCompiler" ...>
< ... >
</sectionGroup>
</configSections>
<spring>
<context>
<resource
uri="assembly://HelloSpringSpringPart/HelloSpringSpringPart/HelloSpring.xml" />
</context>
</spring>



In the spring configuration section, we are simply telling Spring.NET to pull its default context from the HelloSpring.xml resource in HelloSpringSpringPart.dll.



Further down the config file, we make sure that the web application is set to full trust.




<trust level="Full" originUrl="" />



I have not found a way to maintain a trust level of WSS_Minimal and still get this to work.  Spring.Core uses reflection extensively and that requires the elevated trust level.  I would be happy to learn of a way to get around this.



Next, make sure that all DLLs other than the web part DLL get copied to the /bin directory for the web application.



Finally, activate the web part for the site collection.  Once this is done you should be able to add the part to a web parts page and fiddle with the State property to get the different messages.



Musings



The question, thus, is not "Can I use Spring.NET in a web part" but "Should I use Spring.NET in a web part."  I don't have an opinion yet on that.  Having to set the trust level to Full isn't optimal -- but there are certainly lots of sites that do that when component changes are restricted to trusted staff. 



The loading of the context information could be handle external to the web.config file for SharePoint in order to not be changing it by hand, although the trust level would still need to be done there.



Finally, it would probably be worth tracking down what the behavior of Spring.NET is when there multiple web parts using it on a page.



And that, folks, is how this social butterfly spent his Saturday night.

Wednesday, April 2, 2008

Keeping Namespace Names

The other day, I needed to use a bit of external code to check and possibly update values in an InfoPath form stored in a MOSS 2007 document library. Specifically, I wanted to update a field in the InfoPath form from within a WF Code Activity.

In taking the direct path to success, I broke stuff. Isn't that always the way?

The key take away is that if you de-serialize and then re-serialize a document using XmlSerializer, you will get a valid document along with the namespaces that were a part of the original...but, the names may be changed. If anything were to be looking for an element using the tag of the namespace (cough, cough, SharePoint promoted properties) instead of matching the namespace URI, such a change will break the connection.

Quick hit

For those of you simply looking for an answer to "how do I maintain my namespace tags so they are the same on Serialize as they were before Deserialize?", here's a quick cheat-sheet.

At the top level of the class you are deserializing to, perhaps created by using xsd.exe, include the following two lines:

public partial class Sample {
[System.Xml.Serialization.XmlNamespaceDeclarations]
public System.Xml.Serialization.XmlSerializerNamespaces xmlns;
...blah blah blah

The xmlns field will used to save "as is" namespace definitions on deserialization and reapply the identical definition on serialization. You do not need to do any further manipulations in the calling code. (Although you will find xsi and xsd namespace declarations also added if they weren't there before. To avoid then, skip to the bottom of the post.)

Note that the field must be public. Either protected or private will not capture the information on deserialization and the process will end up working as if the field weren't there.

Ok, those in for a quickie may leave now. The rest of the tale continues...

Ignorance is bliss

So, there I was, wanting to modify the XML of an InfoPath from from within a workflow running in SharePoint. I started on my merry way, extracting myschema.xsd from the InfoPath form, using xsd.exe to create a class and creating code to use an XmlSerializer for my de-serialization and serialization.

I did need help to find a way to make sure that correct XML processing instructions for InfoPath were produced, but otherwise it went fine. I checked for an empty field and updated it to "the right thing" if it needed updating, re-serialized and *bam* had an updated InfoPath document. It opened in InfoPath, it opened through the forms server and I was happy.

Until my workflow blew up because the promoted properties were not available anymore.

What's in a name?

A namespace in XML is a pairing of a tag and a URI that allows an XML document to disambiguate elements, thus allowing different pieces of schema to cohabit happily in one document without arguments about names. A conceptually valid piece of XML may have multiple ways of expressing the same thing with different names. That is:

<?xml version="1.0" encoding="utf-8" ?>
<a:Sample xmlns:a="http://example.com/namespace/test">
<a:child attribute="My Attribute">My Contents</a:child>
</a:Sample>

and
<?xml version="1.0" encoding="utf-8" ?>
<b:Sample xmlns:b="http://example.com/namespace/test">
<b:child attribute="My Attribute">My Contents</b:child>
</b:Sample>

and
<?xml version="1.0" encoding="utf-8" ?>
<Sample xmlns="http://example.com/namespace/test">
<child attribute="My Attribute">My Contents</child>
</Sample>

are essentially the same. The fact that in the first a is used as a label, in the second b, and in the third the default empty tag makes no difference, since a, b, and the empty tag are using the same URI, http://example.com/namespace/test.A well behaved program with treat them both the same. Unfortunately for me, properties promoted from InfoPath to SharePoint are identified by the equivalent of a:child instead of a:child and namespace info xmlns:a = http://example.com/namespace/test.The latter would allow a different document with b:child and namespace info xmlns:b = http://example.com/namespace/test to correctly match the URI and identify b:child as a promoted property.

XMLSerializer, Deserialize, and Serialize

Once upon a time in the land of DOM, you would simply load an XML string into an XML document and play funny access games with XPath. This is not nearly as much excitement as it sounds once the document progress beyond the complexity of those above. So instead, we create classes with convenient access to represent the elements of the document as properties in the class and deserialize the document into this. Documents can be saved by setting the properties and then using the same XMLSerializer to serialize them.Let's take an example. First, I used Visual Studio to create an XML Schema file, Sample.xsd from the first XML sample above. It created:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:a="http://example.com/namespace/a" attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://example.com/namespace/a" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Sample">
<xs:complexType>
<xs:sequence>
<xs:element name="child">
<xs:complexType>
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="attribute" type="xs:string" use="required" />
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>

Running xsd.exe to generate a serializable class representing the schema gives a class Sample, starts with something like:
using System.Xml.Serialization;

[System.SerializableAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://example.com/namespace/test")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://example.com/namespace/test", IsNullable=false)]
public partial class Sample {
private SampleChild childField;

public SampleChild child {
get {
return this.childField;
}
set {
this.childField = value;
}
}
}
...more definitions for SampleChild....

Finally, assuming that a text box txtSource held some XML and a text box txtOutput is being used to display the re-serialized results, a sample bit of code might be:
XmlSerializer serializer = new XmlSerializer(typeof(Sample));
StringReader reader = new StringReader(txtSource.Text);
Sample what = (Sample)serializer.Deserialize(reader);

using (MemoryStream memoryStream = new MemoryStream() )
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.Encoding = new UTF8Encoding();

using (XmlWriter writer = XmlWriter.Create(memoryStream, settings))
{
serializer.Serialize(writer, what);
writer.Close();
txtOutput.Text = Encoding.UTF8.GetString(memoryStream.ToArray());
}
}

We should (and do) end up with a compatible document. For example,
<?xml version="1.0" encoding="utf-8" ?>
<a:Sample xmlns:a="http://example.com/namespace/test">
<a:child attribute="My Attribute">My Contents</a:child>
</a:Sample>

ends up yielding
<?xml version="1.0" encoding="utf-8"?>
<Sample xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"

xmlns="http://example.com/namespace/test"
>
<child attribute="My Attribute">My Contents</child>
</Sample>

Note that the structure is the same, but the a tag for the namespace URI http://example.com/namespace/test has morphed into the default blank namespace. As mentioned above, this is fine for well behaved XML document manipulation programs. But for anything that is referring to a:child, it will be a problem. The process also added two unused namespaces, xsi and xsd, that don't affect things in this case. Still, the rest of the solution will remove them, too, to make sure that namespace-in becomes namespace-out and that's it.

Maintaining the status quo

At this point I started fiddling around with things to make sure I ended up in the same namespace. Dan Miser pointed me to a post of his explaining how to remove namespace information using the XmlSerializerNamespaces class. Using it in the way Bill G intended, I could add explicit namespace declarations or, of course, I could return to the old reliable "make it an XMLDocument and go from there." The former required hard-coding the namespace information and the latter was a lot more code (or would be if I actually wanted to DO something with this document.)

Each way seemed way too inelegant, so I kept looking and ran across the XmlNamespaceDeclarations attribute. Looking at samples, I decided to try adding
public partial class Sample {

[System.Xml.Serialization.XmlNamespaceDeclarations]
public System.Xml.Serialization.XmlSerializerNamespaces xmlns;


...blah blah blah...
}

to my generated class. Indeed, just that small change solved my problem. The xmlns element (which must be public to be correctly initialized by XmlSerializer.Deserialize) retained the tag/URI matching from my original document and the results of XmlSerializer.Serialize gave
<?xml version="1.0" encoding="utf-8"?>
<a:Sample xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:a="http://example.com/namespace/test">
<a:child attribute="My Attribute">My Contents</a:child>
</a:Sample>

Now I was almost there. Doing this in the InfoPath world was perfectly acceptable -- the my: tag for the namespace was retained and the references for the promoted SharePoint properties were just dandy.

Being a completist
The last niggle is the addition of the xsi and xsd namespace declarations. Since they are unused, they are unlikely to cause problems. But to make sure we get identical output, we can alter the calling code to grab XmlSerializerNamespaces xmlns field and feed it back into the Serialize

method:

using (XmlWriter writer = XmlWriter.Create(memoryStream, settings))
{
serializer.Serialize(writer, what, what.xmlns );
writer.Close();
txtOutput.Text = Encoding.UTF8.GetString(memoryStream.ToArray());
}

We end up with a final, matching document.
<?xml version="1.0" encoding="utf-8"?>
<a:Sample xmlns:a="http://example.com/namespace/test">
<a:child attribute="My Attribute">My Contents</a:child>
</a:Sample>

Now that's probably too much typing to justify adding two lines of code and modifying one, but so it goes.

Tuesday, April 1, 2008

You gotta be kidding me

So two trivial bits of knowledge I learned today made me slap my forehead and start this blog. Each was a how could I not know that experience.

In defense of goto...

When displaying a PowerPoint presentation I can type the page number (and an enter) while presenting and immediately jump to that slide. Nothing shows up on the display saying I entered 47 and skipped those extra marketing slides (that would be slides 2-46 in a common presentation deck) and moved immediately to the meat of the presentation.

This, for me, was a *duh* moment. It makes perfect sense, but I never picked it up and would click, click, click my way to skip slides.

The downside is, of course, that I have to prepare enough to know the slide numbers instead of "somewhere down there". At least I'd want to have a quick list of likely jump-to slides. I can certainly see where knowing this would have been really useful back when I was doing training classes and did have well organized decks.

Hidden in plain sight...

So in the process of faking mouse-over pop-ups on PowerPoint, I had the need to introduce a number of almost duplicate slides. (Yes, I could have used internal code, but this is a deck for a client and I don't want to count on the environment it will be run in.)

However, I only want to see these slides when using the mouse over--not when just clicking through the presentation in the normal order.

The *duh*scovery was that simply by doing a right-click hide-slide on the popped-up version of the slide, I would miss them in the normal order. I had foolishly been thinking that if I hid the slide it would be hidden for all purposes. Not so--it can still be the target of a hyperlink, and that allows it to join in the collusion presenting the apparent pop-up and still stay out of the way.

Another solution would be to cluster all the "special slides" at the beginning (or end) of the presentation and use "Set Up Show" under the Slide Show menu to start the presentation somewhere other than slide one. The only drawback I see here is that the variant slides are then separated in the deck from the initial slides, so it might be easier to miss related slides at update time.

Way too much to know

Y'know, I learn something just about every day. There is just way too much stuff to know, and that's in a work-oriented, tech environment. Actually being a reasonable person on planet Earth requires more.

Since I won't be solving the world's problems at large, I'll just try to capture bits and pieces of the tech things I learn. Some will be trivial, no doubt--things that lots of people already know but I just found out--and hopefully some will be not-so-trivial. I guess that will be up to youse guys to decide...