Andosi Blog - the Art of Great Design

Executing SQL Queries via SharePoint Web Services

Can you really execute native SQL Queries from SharePoint Web Services?

Microsoft SharePoint is great for building enterprise systems tying together various data sources.  If the information you are looking for is in a SharePoint List or Document Library, it is straightforward to call the built-in Web Services to query or manipulate that data.  Through custom Web Parts, you can run server-side code and easily retrieve data that lives outside SharePoint.

Read more

Capturing (and using) raw SOAP messages in WCF

WCF is great for building web services.  It's also great for interacting with existing web services.  Visual Studio makes it so easy . . . add a service reference, point to the WSDL of the service and just like that, you have a set of classes to handle the service and data contracts.
Unfortunately, sometimes web services don't live up to their contracts.  Recently, I was interacting with a web service and found that sometimes the response would be null.  I fired up Fiddler and looked at the SOAP messages.  Sure enough, an error had occurred on the remote server and the response, while valid XML, looked nothing like the promised Data Contract.  It did however provide a useful description of the error.
There was no SOAP fault . . . no exception thrown (unless I tried to use the null response without checking), just a null response object by the time WCF handed it to me.
I searched the web and found several suggestions.

  • Use Fiddler . . . tried that but it doesn't help when you are trying to get your service into production.
  • Build a SOAP Extension . . . that's great if you are building a legacy asp.net XML Web Service and just want to log or tweak the messages.  I'm doing WCF and needed to make the messages available to my service in real time.
  • Edit the Visual Studio Generated classes to wrap the response in XML and deserialize it yourself . . . that might work but you throw away so much niceness that's built for you already.
I ended up building a custom Endpoint behavior and applying it to the generated SOAP Client object.
 
First, you need a class to hold the raw Request and Response:
        public class InspectedSOAPMessages
        {
            public string Request { getset; }

            public string Response { getset; }
        }
  
 
Create an instance of the class and pass it into the new Endpoint Behavior:
 
        InspectedSOAPMessages soapMessages = new InspectedSOAPMessages();
        batchClient.Endpoint.Behaviors.Add(new CapturingEndpointBehavior(soapMessages));
        BatchService.batchResponseType response = batchClient.BatchOperation(batchRequest);
        batchClient.Close();
           //response will be null if the contract was violated . . . 
        if (response == null)
        {
           results.Message = soapMessages.Response;    } else { //process normal response
  
Now, all we need is the set of classes to handle the Endpoint Behavior:
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Description;
    using System.ServiceModel.Dispatcher;

    namespace MBSGuru
    {
    ///<summary>
        /// Allows capturing of raw SOAP Messages
        ///</summary>
        public class CapturingEndpointBehavior : IEndpointBehavior
        {
            ///<summary>
            /// Holds the messages
            ///</summary>
            public InspectedSOAPMessages SoapMessages { getset; }

            public CapturingEndpointBehavior(InspectedSOAPMessages soapMessages)
            {
                this.SoapMessages = soapMessages;
            }

            ///<summary>
            /// Required by IEndpointBehavior
            ///</summary>
            ///<param name="endpoint"></param>
            ///<param name="bindingParameters"></param>
            public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { return; }

            ///<summary>
            /// Required by IEndpointBehavior
            ///</summary>
            ///<param name="endpoint"></param>
            ///<param name="clientRuntime"></param>
            public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
            {
                clientRuntime.MessageInspectors.Add(new CapturingMessageInspector(this.SoapMessages));
            }

            ///<summary>
            /// Required by IEndpointBehavior
            ///</summary>
            ///<param name="endpoint"></param>
            ///<param name="endpointDispatcher"></param>
            public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { return; }

            ///<summary>
            /// Required by IEndpointBehavior
            ///</summary>
            ///<param name="endpoint"></param>
            public void Validate(ServiceEndpoint endpoint) { return; }
        }

        ///<summary>
        /// Actual inspector that captures the messages
        ///</summary>
        public class CapturingMessageInspector : IClientMessageInspector
        {
            ///<summary>
            /// Holds the messages
            ///</summary>
            public InspectedSOAPMessages SoapMessages { getset; }

            public CapturingMessageInspector(InspectedSOAPMessages soapMessages)
            {
                this.SoapMessages = soapMessages;
            }

            ///<summary>
            /// Called after the web service call completes.  Allows capturing of raw response.
            ///</summary>
            ///<param name="reply"></param>
            ///<param name="correlationState"></param>
            public void AfterReceiveReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
            {
                this.SoapMessages.Response = reply.ToString();
            }

            ///<summary>
            /// Called before the web service is invoked.  Allows capturing of raw request.
            ///</summary>
            ///<param name="request"></param>
            ///<param name="channel"></param>
            ///<returns></returns>
            public object BeforeSendRequest(ref System.ServiceModel.Channels.Message request, IClientChannel channel)
            {
                this.SoapMessages.Request = request.ToString();
                return null;
            }
        }
    }
  
And just like that, you have a copy of the request and response.  How you handle the violation of the contract is up to you.  At least now you can report something more informative than "Error Occurred"
 
This blog has been relocated from http://mbsguru.blogspot.com/ with authorization.

CSS Consolidator jiu-jitsu

CSS Consolidator

Sometimes you inherit a huge pile of CSS and want to make a few changes. Sometimes there are huge groups of duplicated rules. Sometimes, you just want to see everywhere Comic Sans is used in your stylesheets. (Hopefully, nowhere.)
Paste in your Source CSS below and click Consolidate CSS.  Rules and selectors will be consolidated in the next textbox.  All processing is done client-side in JavaScript.
Below that, you will see a breakdown of your styles, arranged by Selector, Attribute and Values.
Why did I do this?  Why not?

Source CSS (No @media tags please)

Consolidated CSS

SelectorsAttributesValues

This blog has been relocated from http://mbsguru.blogspot.com/ with authorization.

Resolving "Error occurred in deployment step 'Activate Features': Invalid file name"

The other day I was working on a SharePoint project that required the deployment of a Content Type, a List Template, a couple of List Instances and a couple of Feature Receivers. Things were coming along well until I started to reorganize the project. I dragged the List Instances into the List Template folder and renamed several folders to better represent their purposes. When I went to deploy, I got the error: "Error occurred in deployment step 'Activate Features': Invalid file name".

 
I looked around the .spdata files, checked the Feature file and double-checked them all again. Everything looked right. The ULS Logger wasn't much help but it did give me the actual exception: "Exception: Microsoft.SharePoint.SPException: Invalid file name. The file name you specified could not be used. It may be the name of an existing file or directory, or you may not have permission to access the file"
 
Still not much help. I removed all the items from the feature and was able to deploy successfully. Unfortunately, a feature that doesn't do anything isn't much good so I started adding items and deploying one at a time. Feature Receivers: Check. Content Type: Check. List Template: Check. List Instance: Failed.
 
After looking at the List Instance for a while and not seeing the problem, I deleted the Instance from the project and recreated it from scratch. I added it to the feature, deployed and it failed again.
 
I went through the List Template again and saw the familiar warning:
<?xmlversion="1.0"encoding="utf-8"?>
<Elementsxmlns="http://schemas.microsoft.com/sharepoint/">
<!-- Do not change the value of the Name attribute below. 
If it does not match the folder name of the List Definition project item, 
an error will occur when the project is run. -->
<ListTemplate
        Name="OldFolderName"
        Type="10001"
I had renamed the List Template folder from within Visual Studio but it did not update the ListTemplate Name element.
 
After correcting the Name to match the new folder name everything worked and the world was right again.
 
Add Solution:
Found 1 deployment conflict(s). Resolving conflicts ...
Deleted list instance 'Lists/SomeAwesomeList' from server.
Adding solution 'SomeAwesomeSolution.wsp'...
Deploying solution 'SomeAwesomeSolution.wsp'...
Activate Features:
Activating feature 'SomeAwesomeFeature' ...
Run Post-Deployment Command:
Skipping deployment step because a post-deployment command is not specified.
========== Build: 1 succeeded or up-to-date, 0 failed, 0 skipped ==========
========== Deploy: 1 succeeded, 0 failed, 0 skipped ==========
 
This blog has been relocated from http://mbsguru.blogspot.com/ with authorization.

Love, Hate and the ViewState

I was recently tasked with creating a SharePoint interface to Microsoft Dynamics GP Item Maintenance. As the client's business had grown, inconsistencies in theirItem Master became apparent. When new items were needed, a similar existing item was copied and the details updated to match the new item. If there were no similar items, a new item was created.
The problem was, many of the existing items were not properly categorized. There were people in the organization who knew bits of information about items, but nobody had all the information to correctly set up an item. Setting up an item properly required a combination of phone calls, emails and a little bit of luck. It could sometimes take weeks to get the item set up. This held up BOMs, Routings and pretty much everything else dependent on the new item.
As usual for a new project, we started with a discovery phase. We tried to identify the groups that knew the necessary bits of information about items and proceeded to schedule interviews. During the interviews we gathered a lot of information to help us get started. We also found several points that were unclear: i.e. Accounting said Billing provides this. Billing said it was Sales, Sales thought it was Purchasing, Purchasing pointed to Engineering and Engineering said it was Accounting. After a few round trips, we were able to pin most things down but in the end, there were still a few bits of information that nobody understood.
It was clear the Workflow would have to be very flexible. Adding to the complexity, some Items could bypass entire groups. For example, there's no need to set a price on an item if you don’t sell it. And why bother Purchasing if you are making this item in your own shop?
To solve this business problem, we needed to deliver a product with:

  • Flexible Workflow rules that could be adapted as the business changes (and as the users work with the system and discover steps that had been overlooked.)
  • Field-Level security to ensure each group can only edit their section.
  • The ability to assign some fields to multiple groups and users.
  • The ability to add new fields and groups as new requirements surface.
  • Audit Logging for accountability.
  • An intuitive User Interface.
  • The ability to open, edit and copy existing GP Items.
  • The ability to push the approved changes back to GP.

This was not a simple SharePoint WebPart with an ASP.NET Form you can throw on a Page and start using. The form had to be generated dynamically, with different sections editable by different users. Lookup Fields had to be populated dynamically. Some based on values stored in GP like Class Code, others with a Hard-Coded list of choices, like Item Type. Oh, and new lookup fields could be added at any time.
What's more, the fields and their rules were not known at design time. All the metadata describing the rules had to be parsed during form generation and postback.
While building this application I ran into a few challenges. One in particular had to do with updating posted values based on business rules and then rendering the fields, not as the user had posted, but as the rules dictated. Now that you know the background, how do we make this happen?
The ViewState is great! It makes your life as a web developer so much easier. Some action causes a postback and all the fields are repopulated with their values. You can handle events for a control without having to worry about the rest of the form. Controls get their IDs set automatically. What's not to love? Plenty.

  1. The ViewState is transmitted and parsed with every page post/load cycle using bandwidth, memory and CPU cycles for the server and the client.
  2. The ViewState, while encoded, is not encrypted. I've seen this argument and I don’t think it's really all that relevant. After all, you are sending the same information back and forth through the form fields. And if you need to keep a secret, use HTTPS.
  3. The ViewState is persistent insistent. If I take the users' submitted data and do some processing on the backend, I just might want to change some values on the form when it is sent back. Suppose they tried to order 100 widgets but you just sold some and you only have 75. Send the form back to the user with quantity set to 75 and display a nice alert telling them they are lucky to get that many. Thanks to ViewState, the quantity field gets set back to 100 automatically. I know, JavaScript could validate the page before the user submits. But what if the user is running without JavaScript? I know I do unless I'm on a trusted page. Add-ons like NoScript offer significant protection while surfing and after a whitelisting your common sites, they pretty much stay out of the way. But even with JavaScript, if I'm dealing with Dynamic Data, (what other kind is there?) the validation rules may have changed since the page was loaded. I suppose I could build some AJAXy validation JavaScript, but again, you can't count on JavaScript being there and you should never trust anything a user submits, even if you think your JavaScript has sanitized it.

So, lets say we want to prevent ViewState from running. Easy enough, just set the EnableViewState property of the control to false:

         TextBox t = new TextBox();
         t.EnableViewState = false;
         t.Text = this.ToString();
         return t;

And what if you want to disable ViewState on the whole page?

      private void Page_Init(object sender, System.EventArgs e)
      {
         this.EnableViewState = false;
         //do some other interesting stuff 

      }

So, you’ve defeated the ViewState on a couple of projects and now it's time to build a SharePoint Web Part. Create the Web Part, add some controls, disable ViewState, deploy the Web Part and life is good! And we still have time to make happy hour (the first one!)
Wait a minute. Didn't you disable ViewState? Why aren't your backend changes sticking? Because SharePoint Web Parts love ViewState so much, they insist on using it. Disable it on the control? Doesn't matter. It happens anyway. Control.ClearChildViewState() doesn't even help. How about firing up SharePoint Designer and disabling it for the entire Page that hosts the Web Part? Congratulations, you've done it, and you’ve disabled pretty much all the SharePoint functionality too. No, there has to be way around this.
Actually, there are two:

  1. Create a LiteralControl and build it's Text property with the HTML you need to create your form field. Now you can render the LiteralControl and IIS will never even see the field. Remember, you will have to inspect the Post Variables yourself to find out how many widgets you just sold. Unfortunately, I tend to make the occasional mistake dynamically building HTML from within a C Sharp app. Leave off a quote or miss a closing tag and your form starts acting really strange. That brings us to the other option.
  2. Create your controls like normal and add them to parent controls if you like. No need to worry about the HTML, IIS will get it right. But instead of adding the controls to the page (or a page element) render them into the Text property of our friend the LiteralControl. Using a StringBuilder, a StringWriter and an HTMLTextWriter, it all falls into place
             //...Build your textbox as you like. 
             //Don't forget a unique ID. 
             TextBox tbQuantity = new TextBox();
             LiteralControl LControl = new LiteralControl();
             LControl.Text = this.RenderControlToString(tbQuantity);
             this.Controls.Add(LControl);
    
          public string RenderControlToString(WebControl Control)
          {
             StringBuilder sb = new StringBuilder();
             using (StringWriter sw = new StringWriter(sb))
             {
                using (HtmlTextWriter textWriter = new HtmlTextWriter(sw))
                {
                   Control.RenderControl(textWriter);
                }
             } return sb.ToString();
          }

Using the HtmlTextWriter, we have well-formed HTML without having to worry about ViewState moving our cheese. Even in a SharePoint Web Part. And as a bonus, the ID you assigned to the Control is the ID that will be returned. No prepending all the parent control IDs by IIS. This, too helps to reduce the page size and load time.

This blog has been relocated from http://mbsguru.blogspot.com/ with authorization.

  • This email address is being protected from spambots. You need JavaScript enabled to view it.
  • (813) 792-1939
  • Privacy Policy