Sharepoint: publish a webservice with your solution

February 29, 2008

In this article I’ll cover how can you publish a WebService with your solution that will be accessible at every site in all the web applications where the solution is deployed. To keep thing simple, I’ve chosen a simple Helloworld webservice but with this approach we could publish any webservice or .aspx pages or a httpHandler

What we will do is to publish the service in the _vti_bin sharepoint folder of our site. To do this, we must first copy the .asmx file of the service in the Common FilesMicrosoft Sharedweb server extensions12ISAPI folder of our server.

<%@ WebService Language=”c#” Class=”MyAssembly.Services.HelloWorldService,MyAssembly.Services”%>

This was the .asmx file we want to distribute and following is the .DDF file from which we’ll generate the solution, I’d like to show that you could also publish a web.config file for the _vti_bin/MyAssembly folder, we would want to do so if, for example, were deploying an httpHandler instead of a webservice or if we need to configure some AppSettings parameters.

; Solution.DDF
FeaturesISAPIMyAssemblyweb.config ISAPIMyAssemblyweb.config
FeaturesISAPIMyAssemblyHerramientas.asmx ISAPIMyAssemblyHelloWorldService.asmx
ServicesbinDebugMyAssembly.Services.dll MyAssembly.Services.dll

Then we must define the manifest.xml file used in the solution deployment:

<RootFiles>
<RootFile Location=ISAPIMyAssemblyweb.config />
< RootFile Location=ISAPIMyAssemblyHelloWorldService.asmx />
</RootFiles>

<Assemblies><Assembly DeploymentTarget=WebApplication Location=MyAssembly.Services.dll /></Assemblies>

Remember to set the appropiate assembly permission at the CodeAccessSecurity section of the manifest.xml file if not running under Full Trust mode or not deploying the assembly in the GAC.

Finally, here is the easy code of our Helloworld service:

namespace MyAssembly.Services
{
    [WebService(Namespace = "http://MyAssembly.com/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class HelloWorldService : System.Web.Services.WebService
    {

        [WebMethod]
        public string HelloWorld()
        {
            return "Greetings from this Service !";
        }

    }
}

Now, after deploying the solution we can access the webservice adding _vti_bin/MyAssembly/HelloWorldService.asmx at our site url (for those sites we have deployed the solution to !). However, If we have deployed the solution in the GAC, the service should be able to all the sites in the server.

That’s all for now ! As always, feel free to comment anything you might find interesting !


Ajax enabled webpart not working on second postback

February 20, 2008

Trying to develope an Ajax webpart with aspx.net 2.0 I came up with this msdn example  which seemed to be exactly what I was looking for. I followed the steps suggested by the article but I ended up with some weird behaviour from the webpart when finally deployed in my Sharepoint server.

Apparently everything was working as expected and when I clicked the webpart the first time, the ajax method was correctly executed and the “Hello! XXX” text succesfully displayed. However, clicking again the button didn’t seem to do anything, also, other page actions that should submit the form and do some action (like the “Edit Page” item from the “Site Actions” menu) stopped working.

I finally found that the problem was in the EnsurePanelFix function of the example which was not working as expected

private void EnsurePanelFix()
{
   if (this.Page.Form != null)
   {
     String fixupScript = @"
     _spBodyOnLoadFunctionNames.push(""_initFormActionAjax"");
     function _initFormActionAjax()
     {
       if (_spEscapedFormAction == document.forms[0].action)
       {
         document.forms[0]._initialAction =
         document.forms[0].action;
       }
     }
     var RestoreToOriginalFormActionCore =
       RestoreToOriginalFormAction;
     RestoreToOriginalFormAction = function()
     {
       if (_spOriginalFormAction != null)
       {
         RestoreToOriginalFormActionCore();
         document.forms[0]._initialAction =
         document.forms[0].action;
       }
     }";
   ScriptManager.RegisterStartupScript(this,
     typeof(SayHelloWebPart), "UpdatePanelFixup",
     fixupScript, true);
   }
}

So what I did is to provide my own implementation of that function. What we needed is to invalidate the onSubmit wrapper that Sharepoint calls in order to ensure some callback scenarios, this can be done by setting to true the _spSuppressFormOnSubmitWrapper variable so I replaced the original function by the following implementation

private void EnsurePanelFix()
        {
              ScriptManager.RegisterStartupScript
                (this,
                 typeof(AjaxEnabledWebpart),
                 "UpdatePanelFixup",
                 "_spOriginalFormAction = document.forms[0].action; _spSuppressFormOnSubmitWrapper=true;",
                 true);
}

And that’s it ! my Ajax webpart is working properly no matter how many times we click the Hello World ! button 🙂


Exchange Server Event Sink in c#

February 15, 2008

I’ve been asked to make some minor modifications to a component I developed some time ago. Reviewing the code, I thought it would be a nice topic to talk about it.

This component is an Event Sink which is supposed to be installed in an Exchange Server that parses all incoming email messages (depending on the rules defined on it’s registration, but this is a topic I’ll leave for another post!) and check if they contain some tag in it’s subject field. If so, it does some processing and changes their subject by adding a #OK or #ERROR tag depending on the process outcome. This tag is meant to be used by the Outlook to determinate on which folder should the message be placed (defined by some Outlook rule by the client)

What we need in order to create an Exchange event sink is a class that implements an interface defined in th COM+ component cdoex.dll which we can find in the bin folder of our exchange server. In order to do that we must build the interop .net assembly that will wraps us this library in managed code. We can do that with the following call to tlbimp

sn –k sn.key 
tlbimp cdoex.dll /keyfile:sn.key /out:Interop.cdoex.dll /namespace:CDO

Then we add this reference to our assembly and creates a class that implements the ISMTPOnArrival interface. There are a lot of other interfaces in the cdoex library depending on when do we want our event sink to be executed, for this example I needed to be when a message arrived and this is why I choosed this particular interface. Here is the class declaration:

[Guid("021079E3-6FCB-491b-A78F-81BC31A1EC9D")]
[ComVisible(true)]
public class MyEventSink : ISMTPOnArrival, IEventIsCacheable

So following is the implementation of the ISMTPOnArrival interface:

void ISMTPOnArrival.OnArrival(IMessage Msg, ref CdoEventStatus EventStatus)
{
    try
    {
        //Do message processing
        //Here we can access all message's properies
        //for example the message body: Msg.TextBody

        //Change subject to OK
        Msg.Fields["urn:schemas:mailheader:subject"].Value = "#OK# " + Msg.Subject;

    }
    catch (Exception)
    {
        //Change subject to ERROR
        Msg.Fields["urn:schemas:mailheader:subject"].Value = "#ERROR# " + Msg.Subject;
    }
    finally
    {
        //Save message properties
        Msg.Fields.Update();
        Msg.DataSource.Save();
        EventStatus = CDO.CdoEventStatus.cdoRunNextSink;
    }
}

Then, all we need to do is register the .dll in the Exchange server but this is a topic I’ll cover in another post ! As always, feel free to leave any comment !


Active Directory Role Provider

February 14, 2008

Having an asp.net application with forms authentication enabled authenticate users against an Active Directory is an easy thing. All you have to do is use an ActiveDirectoryMembershipProvider and configure it’s connectionString property in the web.config file.

Retrieving user’s role information is a different thing. One would expect an ActiveDirectoryRoleProvider to connect to the Active Directory and retrieve the current user’s group information, however such object doesn’t exist.

What follows is a custom implementation of this role provider that queries an Active Directory and retrieves user’s group information.

First you have to define a class that inherits from System.Web.Security.RoleProvider in order to use it as your application role provider

public class CustomActiveDirectoryRoleProvider : System.Web.Security.RoleProvider

Next, you should retrieve the configuration information from the web.config file in the Initialize method

private string _loginProperty = "sAMAccountName";
private string _connectionString = string.Empty;
private string _applicationName = string.Empty;

public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
    _connectionString = config["connectionStringName"];
    _applicationName = config["applicationName"];
    if (!string.IsNullOrEmpty(config["attributeMapUsername"]))
        _loginProperty = config["attributeMapUsername"];
    base.Initialize(name, config);
}

At last, whe have to implement GetRolesForUser method where we will get all the groups where the current user belongs to (other methods such as IsUserInRole can be implemented later by querying the resultset from GetRolesForUser)

public override string[] GetRolesForUser(string userName)
{
    List allRoles = new List();

    DirectoryEntry root = new DirectoryEntry(WebConfigurationManager.ConnectionStrings[_connectionString].ConnectionString);
    foreach (DirectoryEntry entry in root.Children)
    {
        if (entry.SchemaClassName.ToLower() == "group")
        {
            object members = entry.Invoke("Members", null);
            foreach (object member in (IEnumerable)members)
            {
                DirectoryEntry child = new DirectoryEntry(member);

                if (_getProperty(child, _loginProperty) == userName)
                {
                    string name = _getProperty(entry, "name");
                    allRoles.Add(name != "" ? name : entry.Name);
                }
            }

        }
    }

    return allRoles.ToArray();
}

Finally I provide the _getProperty function source code

private string _getProperty(DirectoryEntry entry, string propertyName)
{
    if ((entry.Properties[propertyName] != null) &&
        (entry.Properties[propertyName].Value != null))
    {
        return entry.Properties[propertyName].Value.ToString();
    }

    return "";
}

And that’s it ! hope it’s useful and don’t forget to leave a comment if you think so 😉


Hide base class property from XmlSerialization

February 13, 2008

Some time ago I created a business logic framework for developing .net applications at my company. It implemented a Data Transfer Object pattern where objects inherited from a base Data Object class where passed through all the application tiers and where ultimately served to UI through a Web Services layer.

Yesterday, a developer came up with a new requirement. He needed to hide a public property of the base data object from serialization at the web service layer and only serialize the attributes he added in the child classes.  

One solution to this problem would be to implement the IXmlSerializable  interface and provide it’s own serialization code in ReadXml and WriteXml methods, but he didn’t want to have to implement all the serialization in all the classes just to hide a property.

Another solution would be to add the XmlIgnore attribute at the base class property, this way it wouldn’t get serialized by the XmlSerializer, the problem is this would have impact in all the other projects which where using the same library and might rely on this property being serialized.

Since I didn’t want to modify the current interface of the base class to not incur in any compatibility issue with other projects I had to find another way to solve this issue. Finally, I came up with a weird solution which I thought it would be useful to share here 🙂

Let’s suppose that we want to prevent a given property called “State” from serialization. I found out that you can create a new public boolean property called “StateSpecified” (that is, adding the sufix “Specified” to the property name) and if you set that property value to false then the “State” property won’t be serialized. And believe me, it works !

So I finally added the following code at the base class

[XmlIgnore()]
public bool StateSpecified = true;

By adding this property, the backward compatibility with any other projects using this base class was assured and the only thing they had to do to hide the property from serialization was to set it’s “XXXSpecified” property to false. The only thing that worries me is that this behaviour isn’t documented or supported by microsoft, it might change in future releases of the .net framework.