Silverlight + SharePoint 2010 - did you just deploy customizations to SharePoint via the document upload?

Just finished my presentation earlier tonight in SDDN regarding Silverlight and SharePoint.  I had some initial reservations whether true Silverlight people want to even know about SharePoint, but I was pretty blown away by their feedback, interesting questions, and I think they found the session insightful. 

This is good :-)

 

I think I delivered my first "shock and awe" when people first saw me deploy to SharePoint.  I finished building my XAP file, and then browsed over to SharePoint, selected my Shared Documents library, clicked upload files (and for additional effect, used the drag & drop upload facility in SharePoint 2010).  Before you knew it, I had the XAP file in my document library, and I'm adding a Silverlight web part and configuring the XAP URL.

For comedy effect, I was pretending as if this is business as usual.

You guys were too good and picked it up right away - it was just too magical.  Hold on a second!  Did you just by-passed all the system admins and deployed customization code to your SharePoint server

The absolutely correct answer is, no, not really, I just deployed customizations to the SharePoint UI, an additional tool if you will, that will help you do your job easier.  Technically, it is not running on the server.  Technically, you can run a separate .NET exe tool to work against SharePoint via the same web services and it can do similar things.

Depending who you are, this might be too magical, and thus, way too dangerous.  I think the thought falls into two categories, and I'm hoping by discussing this, we can compare some thoughts on the PROs and CONs of deploying Silverlight to SharePoint.

 

PRO

  • Bypass system admins
  • Can rapidly develop and test.  Can rapidly update new version
  • Can create simple tools and install them on SharePoint quickly
  • Deploy to SharePoint online

CON

  • Unsafe code, is still unsafe
  • I can deploy a Silverlight webpart that will take my boss' permissions and copy sensitive data to a public location

 

I suggest a compromised workaround for Production SharePoint

  • Block upload of *.XAP files from Central Administration | Web Applications
  • Allow sandbox solutions - which can install XAP files, via the Solutions Gallery
  • Rely only on in-house developed solutions, or solutions purchased through a trusted and verified source such as Office.com, Bamboo, or ProdUShare.

 

At the end of the day, I believe that yes - tools can be used for evil, but for many many businesses, the need for tools to help them to be more efficient, and the need for a stable server that doesn't die all the time, far out-weights the risks of allowing Silverlight solutions.

  • A badly behaving Silverlight crashes one browser, affecting one user
  • A badly behaving web page customization crashes the App Pool, and affects many users

In terms of customizing SharePoint to rapidly meet business needs and still maintain high levels of server availability, you can't ignore or brush off Silverlight + SharePoint possibilities.

I hope the market will agree with me, and I think as long as you don't use your tools for evil, you can help a lot of people with what you can build.

 

I'm still so excited.

VB.NET - there are times when you have to keep an open mind

I suddenly found myself needing to write a very simple upload method in our MVC controller.  The uploaded file is in CSV format.

The question that immediately jumps to mind is, where do I find a CSV parser?

  • Do I write my own - it may sound simple, but as soon as you start to think about all the various permutations of splitting a line of text, with all the escape characters as well as error handling, actually, it is NOT simple at all
    1. How do you handle the case where the user actually wants to use comma (,) in a field
    2. Do you include or exclude header row
    3. How do you handle line numbers and parse error
    4. What do you do with extra spaces or tabs
    5. How do you handle line breaks
    6. What about lines that didn't have matching number of fields
  • Do I then, just re-use someone else's example
  • Or do I use the one that's supplied and supported by Microsoft, and already installed on your machine as part of the .NET Framework since .NET 2.0?

The answer is blindingly obvious - use Microsoft's one - move on, next problem.

  • Well, it's in Microsoft.VisualBasic.FileIO

Hmm, let me think about this again…

 

This is where I find many C# developers suffer, we would rather build our own untested, unsupported code to do CSV parsing, either with String.Split(',') or with clever Regex.  We're convinced that our own code is better somehow than the Microsoft supplied library.  We're so convinced, we'll run off and spend 30minutes building our own parser, we'll follow by spending 2 hours on testing our parser and making sure it's bulletproof.

So I took a good look at myself and stopped.  I said to myself. 

No, I'll be pragmatic.

 

The Microsoft VB.NET CSV parser is part of the TextFieldParser class.  Documented here:

http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.fileio.textfieldparser.aspx

 

Through the Common Language Runtime (CLR) we can include Microsoft.VisualBasic library easily in our C# project.  Here they are, happily (may be) next to each other, and doing work for me.

image

 

The actual code is extremely simple:

using (TextFieldParser reader = new TextFieldParser(fileUpload.InputStream))
{
    reader.TextFieldType = FieldType.Delimited;
    reader.Delimiters = new[] { "," };

    while (!reader.EndOfData)
    {
        string[] parts = reader.ReadFields();

        if (parts.Length != 5)
        {
            // problem.
            continue;
        }
        string customerCode = parts[0];
        string customerName = parts[1];
        string contact = parts[2];
        string phone = parts[3];
        string information = parts[4];

        var customer = customers.FirstOrDefault(c => c.CustomerCode == customerCode);
        if (customer == null)
        {
            // new
            customer = new Customer()
            {
                CustomerCode = customerCode,
            };
            Entities.Customers.AddObject(customer);
        }

        customer.CustomerName = customerName;
        customer.CustomerPhone = phone;
        customer.CustomerContact = contact;
        customer.Information = information;
    }
    Entities.SaveChanges();
}

TIP - Silverlight - InitParams and ApplicationLifeTimeService(s)

I have this love-hate relationship with InitParams

Like

  • Pre-download values to Silverlight, so it's available to the client before Silverlight even starts rendering
  • Don't have to worry about whether Silverlight can talk to any data source - if you can't see the webpage then the problem is elsewhere

Dislike

  • Well it's on the web page…  anyone could see it, and probably tweak it via DOM manipulation
  • If you bind to this data, you can't really "update" it if the data changes on the server.  If say the user settings has changed, you'll need a F5 refresh to force the Silverlight client to reload.  In a sense this is often treated like read-only data.

One thing we can fix

  • A really large App.Current with lots of different values sucked out from the initParams during App_Start

 

Using ApplicationLifeTimeServices

1. Write a Lifetime Service

    public class CompanyApplicationLifeTimeService : IApplicationService
    {
        static CompanyApplicationLifeTimeService _current = null;

        public static CompanyApplicationLifeTimeService Current
        {
            get
            {
                return _current;
            }
        }

        #region IApplicationService Members

        public void StartService(ApplicationServiceContext context)
        {            
            _current = this;
            var parseThisStuff = context.ApplicationInitParams;
        }

        public void StopService()
        {
            _current = null;
        }

        #endregion
    }

2. Add this to your App.Xaml

<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
             x:Class="DispatchKing.Silverlight.App"
             xmlns:common="clr-namespace:DispatchKing.Silverlight.Common"
             >
  <Application.ApplicationLifetimeObjects>
    <common:CompanyApplicationLifeTimeService x:Name="CompanyLifeTimeService" />
  </Application.ApplicationLifetimeObjects>
  <Application.Resources>
    <ResourceDictionary>
   ...

 

3. Now anywhere in your code you have access to the service Singleton

CompanyApplicationlifeTimeService.Current.GiveMeStuff

Silverlight - sharing a common class between Silverlight and .NET

Because Silverlight is compiled against a separate set of Silverlight runtime, we can not reference or share a common library between a Silverlight project and a normal .NET project.

One very common and simple workaround is then to create a common project for .NET, and a common project for Silverlight, and then add the files as existing links from the .NET project to the Silverlight project.  This ensures that the same files are shared by the two sides and we have our matching class definitions.

 

With RIA, there is a new way.

image

In the .NET project, name your extra files with xxx.shared.cs

Compile your .NET project.  This triggers the RIA toolkit to run and generate some files for us:

image

 

So you no longer need to share a file via external link.

Silverlight - the magic of Silverlight RIA Toolkit

 

I checked in some code.  Moments later, my colleague asked me:

"did you check in the service reference for this ServiceReference.DispatchKing.Web.Services.RunboardDuplexService"

I pondered, and answered:

"no need - it was generated for me"

In deed - my Silverlight project started to do something magical for me.  It was creating GeneratedWcfClientCode\ServiceReference.cs

Because this was generated like RIA services, it was not included in the project, and thus - if you didn't have the right tools installed, it appears that your colleague has forgotten to check in files!

 

image

 

Memory Lane

In the old days before RIA services, we rolled out WCF Service and added our own Service Reference via the Silverlight project.  This generated all sorts of service reference proxy classes.

The biggest downside, is that each time we updated the service, we had to regenerate the service reference, otherwise they'd become out of sync and Silverlight will crash and burn.  (or in the case where it didn't crash… we got really scared).

 

In Silverlight 3 we got RIA and the DomainService, having Silverlight project linked to a Web Project meant that Visual Studio automatically started generating all the RIA/DomainService code, as well as the data objects (or entity) required, and making sure everything's available and synchronized between the Web (service) and Silverlight (client).  There was much rejoicing.

What's this magic?

What made this case really special then, is that I'm not using only Domain Services.

image

 

Hmm it doesn't work for my colleague.  We quickly compared out installed Programs and Features, and found out why:

image

 

You can grab this via Web platform installer, or directly from:

http://www.microsoft.com/downloads/en/details.aspx?FamilyID=7b43bab5-a8ff-40ed-9c84-11abb9cda559&displaylang=en

The RIA Services Toolkit enables the following 5 features:

 

1. LinqToSql DomainService
2. Soap endpoint - This enables you to expose a soap endpoint for your DomainService
3. JSON endpoint - This enables you to expose a JSON endpoint for your DomainService
4. ASP.net DomainDataSource - This control will enable you to create an ASP.net application that can talk to your DomainService
5. WCF client proxy auto generation/updating for WCF Core Service – This enables you to get up-to-date WCF proxy and configuration each time you build your solution, when you add Silverlight-enable WCF service in your Silverlight application.

 

The magic, is number 5.