Home | Blog | Screencasts | Projects
# Monday, November 23, 2009

One of the more interesting features of SharePoint 2010 is the activity feed, it’s kind of like the news feed in Facebook a whole range of system wide events are collated in a users my site, these can include events generated by colleagues of a user. I really think this is cool and we will see lots of applications make use of this feature. Even in a business environment the concept of a status feed is useful, SharePoint 2010 provides what seems like a nice enterprise wide service for collecting these events.

 

I thought it might be fun to have a play with some of the activity feed API’s to create something like a Twitter web part for SharePoint 2010, just to be upfront, this code isn’t really useable in a real sense, its just me playing around.

First I’ve created a visual web part for the user to enter a tweet:

 

image

 

Now if your a colleague, you can visit your ‘MySite’ and view the aggregated activities of your colleagues:

 

image

 

Here you can see that the user ‘administrator’ (this person’s colleague) has ‘tweeted’ something.

 

The SharePoint team have made this whole status/activity feed quite extensible, the first step is to define your own activity application

Setup:

	private ActivityApplication actApplication = null;
	private ActivityManager actMgr = null;
	private UserProfile currentUserProfile = null;
	private ActivityType tweetActivityType = null;
	private ActivityTemplate tweetActTemplate = null;

	private void SetupActivities()
         {
            Microsoft.Office.Server.UserProfiles.UserProfileManager pm = new UserProfileManager(SPServiceContext.Current);
            currentUserProfile = pm.GetUserProfile(Page.User.Identity.Name);
            actMgr = new ActivityManager(currentUserProfile);
            tweetActivityType = null;

            if (actMgr.PrepareToAllowSchemaChanges())
            {
                if (actMgr.ActivityApplications["SP2010Twitter"] == null)
                {

                    actApplication = actMgr.ActivityApplications.Create("SP2010Twitter");
                    actApplication.Commit();
                }
                else
                {
                    actApplication = actMgr.ActivityApplications["SP2010Twitter"];
                }

                tweetActivityType = actApplication.ActivityTypes["SP2010Tweeting"];
                if (tweetActivityType == null)
                {
                    tweetActivityType = actApplication.ActivityTypes.Create("SP2010Tweeting");
                    tweetActivityType.ActivityTypeNameLocStringName = "ActivityName";
                    tweetActivityType.ActivityTypeNameLocStringResourceFile = "SP2010Twitter";
                    tweetActivityType.IsPublished = true;
                    tweetActivityType.IsConsolidated = true;
                    tweetActivityType.AllowRollup = true;
                    tweetActivityType.Commit();
                }

                tweetActTemplate = tweetActivityType.ActivityTemplates[ActivityTemplatesCollection.CreateKey(false)];
                if (tweetActTemplate == null)
                {
                    tweetActTemplate = tweetActivityType.ActivityTemplates.Create(false);
                    tweetActTemplate.TitleFormatLocStringResourceFile = "SP2010Twitter";
                    tweetActTemplate.TitleFormatLocStringName = "Activity_Created";
                    tweetActTemplate.Commit();
                }
            }
            else
            {
                //not a profile admin

                var activities = actMgr.GetActivitiesForMe();
                foreach (var act in activities)
                {
                    if (act.Name == "SP2010Twitter")
                    {
                        actID = act.ActivityTypeId;
                    }
                }
            }
            
            

        }

The code above needs to be run once, to setup the application and to define the templates etc.

Creating an event:

	public ActivityEvent GenerateActivityEvent(string tweet, long activityId)
	{
            Entity owner = new MinimalPerson(CurrentUserProfile).CreateEntity(ActManager);
            Entity publisher = new MinimalPerson(CurrentUserProfile).CreateEntity(ActManager);
            
            ActivityEvent activityEvent = ActivityEvent.CreateActivityEvent(ActManager, activityId, owner, publisher);
            activityEvent.Name = "SP2010Twitter";
            activityEvent.ItemPrivacy = (int)Privacy.Public;
            activityEvent.Owner = owner;
            activityEvent.Publisher = publisher;            
            
            activityEvent.Value = tweet;
            activityEvent.Commit();

            
            return activityEvent;
        }

Publishing the event to the user's colleagues:

 

	public void MulticastPublishedEvents()
	{
            if (activityEvents.Count == 0)
                return;

            List publishers = new List();
            foreach (ActivityEvent activityEvent in activityEvents)
            {
                if (!publishers.Contains(activityEvent.Owner.Id))
                    publishers.Add(activityEvent.Owner.Id);
            }

            Dictionary owners;
            Dictionary> colleaguesOfOwners;
            ActivityFeedGatherer.GetUsersColleaguesAndRights(ActManager, publishers, out owners, out colleaguesOfOwners);

            Dictionary> eventsPerOwner;
            ActivityFeedGatherer.MulticastActivityEvents(ActManager, activityEvents, colleaguesOfOwners, out eventsPerOwner);

            List eventsToMulticast;
            ActivityFeedGatherer.CollectActivityEventsToConsolidate(eventsPerOwner, out eventsToMulticast);
            WriteEvents(eventsToMulticast);
	}

	private void WriteEvents(List events)
	{
            int startIndex = 0;
            while (startIndex + ActManager.MaxEventsPerBatch < events.Count)
            {
                ActivityFeedGatherer.BatchWriteActivityEvents(events, startIndex, ActManager.MaxEventsPerBatch);
                startIndex += ActManager.MaxEventsPerBatch;
            }
            ActivityFeedGatherer.BatchWriteActivityEvents(events, startIndex, events.Count - startIndex);
        }
            

Publishing an event is a little more involved and this code was drawn heavily from the SDK sample found here.

The main classes that get used here are:

 

ActivityApplication

An activity application is simply the name of the application, the ‘NewsFeed Settings’ on the my site gives the end user the ability to participate with your application:

 

 image

 

ActivityManager

Simple manager class that gets initialised with the current user profile, this class gets passed around to the other classes that create events.

 

ActivityType

This class provides us with the ability to actually define the activity, things like the time to live, is rolled up? and the template to use. This class also requires the developer to define the resources file that will be used to display the activity name.

 

ActivityTemplate

The display of the activity event gets managed by this class, you need to specify a resources file and key that contains the elements to create the display text. This class and associated resources file controls the UI presented to the user.

 

ActivityEvent

This is the actual event, here we define the publisher and other things like the item privacy level, it’s possible to create public and private events.

 

ActivityFeedGatherer

The gatherer is probably the least intuitive class, it provides the ability to batch write the events, this is handy because often we will want to publish the events to all of the colleagues of the current user.

 

Resources File:

Like I mentioned above, all of the templates and UI elements must be in a resx file. This file must be deployed to:

 

{SharePointRoot}\Resources\SP2010Twitter.resx

 

And will contain something like (note how the ActivityName and Activity_Created are used in the code above):

 

<data name="ActivityName" xml:space="preserve">
    <value>SP2010Twitter</value>
</data>
<data name="Activity_Created" xml:space="preserve">
    <value>{Publisher} tweeted: {Value}</value>
</data>

 

Some of the Default Template Variables:

{Link}

{Value}

{Publisher}

{Size}

 

So above I made use of the {Value} variable and populated the value property of the event object with the tweet value. I could change the resx definition which would change the way the event is shown to the user.

 

Problems:

 

I’ve come across a few little problems, firstly it seems like the user creating the event needs to be a profile admin, even a call to ActivityEvent.CreateActivityEvent seems to require the profile admin property. Unless I’m missing something, I hope this changes in the RTM environment.

 

You can download the sample code here

Monday, November 23, 2009 4:24:00 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [1] - Trackback
code | SharePoint 2010
# Sunday, November 22, 2009

I’ve been playing around with the new document set feature of SharePoint 2010, the idea is that you can group a bunch of documents or digital assets and control the metadata for all of them in one location.

When I first saw the demo around the document sets it reminded me of a piece of work I did for a client, they had a legacy system which would group a number of digital assets into a single entity and the search would return this one single entity if the search keyword was found in any of the documents. At the time we came to the conclusion that MOSS wasn’t a suitable replacement because of the way the search subsystem worked. Even with a custom protocol handler applying the correct metadata to each document/asset, at the point you start trying to invoke the various IFilters (doc, pdf, ppt etc) to index the contents of the documents you needed hand off a discrete url with the file extension of the document (the IFilters are invoked based on the  url), what would result was a disjointed search experience because the documents would be displayed outside of the set.

 

A document set is displayed in the document library as a single entity:

 

DocSet

 

When you select the document set, the ribbon changes so you can manage it or you can select the edit properties to manage the common meta-data:

 

docset2

 

The next thing to look at is the search experience, performing a search for a term that is in one of the documents of the set returns:

 

image

 

In this case I searched for a keyword that was specific to a document, the search results returned a just the single document, no reference to a document set.

 

Searching for a keyword that is contained in both the document set and document returns:

 

image

 

In this case we get the document set as a result and the document, rather than just a document set, the user has no idea that the document (Internet SharePoint Governance Plan) was apart of a document set.

 

I’m not saying the document set feature is useless, I think it has some valid scenario’s, I just would have liked it to work a little differently, but I guess the SharePoint team hasn’t changed the search engine under the covers and are constrained in the same way I was. I think the FAST pipeline offers some other alternatives, but I haven’t had a chance to look at that yet.

Sunday, November 22, 2009 8:02:00 PM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
SharePoint 2010
# Wednesday, November 18, 2009

Ever since the sneak peak developer videos were released months ago I’ve been wondering about the implementation of SharePoint 2010’s visual web parts. If your not sure what I mean, with SP 2010 and Visual Studio 2010 you can now create a web part with a design time experience, so you can drag and drop controls etc:

 

VS2010Designer

 

Now that the beta is upon us I can finally take a look under the covers at a visual web part, the default project structure looks like:

VS2010WebPart

 

The project contains a number of new items: Features and Package both relate to the deployment features of Visual Studio 2010 in that you can create SharePoint solutions (aka the Package) and features which can be activated, visual studio will automatically deploy and activate your web parts using this solution and features.

Next we move on to the VisualWebPart1.cs file which contains the secret sauce:

 

    public class VisualWebPart1 : WebPart
    {
        

        // Visual Studio might automatically update this path when you change the Visual Web Part project item.
        private const string _ascxPath = @"~/_CONTROLTEMPLATES/TestVisualWebPart/VisualWebPart1/VisualWebPart1UserControl.ascx";

        public VisualWebPart1()
        {
        }

        protected override void CreateChildControls()
        {
            Control control = this.Page.LoadControl(_ascxPath);
            Controls.Add(control);
            base.CreateChildControls();
        }

        protected override void RenderContents(HtmlTextWriter writer)
        {
            base.RenderContents(writer);
        }

 

As you can see, the web part still derives from WebPart, no special VisualWebPart base class, nothing special going on here.

In fact we are using the same techniques and approach that would have worked in Visual Studio 2008, the only difference now is that Visual Studio 2010 has better tooling support for SharePoint 2010 and will deploy the ascx file automatically for us to the _CONTROLTEMPLATES directory as part of the solution. 

 

There are still a few things a web part developer should know, lets look at the case where we want to expose some custom properties on a web part that we want a user to configure via the web interface:

 

      [System.Web.UI.WebControls.WebParts.WebBrowsable(true),
       System.Web.UI.WebControls.WebParts.WebDisplayName("Custom Prop"),
       System.Web.UI.WebControls.WebParts.WebDescription(""),
       System.Web.UI.WebControls.WebParts.Personalizable(
       System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared),
       System.ComponentModel.Category("Settings"),
       System.ComponentModel.DefaultValue("")
       ]        
        public string CustomProp
        {
            get { return customProp; }
            set { customProp = value; }
        }

 

Now if we put this property and attributes on the VisualWebPart1UserControl (in VisualWebPart1UserControl.ascx.cs) we will find that the custom property builder won’t appear (the web interface that lets us set a value to this property).

 

We have to add the custom property on the VisualWebPart1 class (in VisualWebPart1.cs) :

 

    public class VisualWebPart1 : WebPart
    {      

        // Visual Studio might automatically update this path when you change the Visual Web Part project item.
        private const string _ascxPath = @"~/_CONTROLTEMPLATES/TestVisualWebPart/VisualWebPart1/VisualWebPart1UserControl.ascx";

        protected override void CreateChildControls()
        {
            Control control = this.Page.LoadControl(_ascxPath);
            Controls.Add(control);
            base.CreateChildControls();
        }

        protected override void RenderContents(HtmlTextWriter writer)
        {
            base.RenderContents(writer);
        }

        [System.Web.UI.WebControls.WebParts.WebBrowsable(true),
       System.Web.UI.WebControls.WebParts.WebDisplayName("Custom Prop"),
       System.Web.UI.WebControls.WebParts.WebDescription(""),
       System.Web.UI.WebControls.WebParts.Personalizable(
       System.Web.UI.WebControls.WebParts.PersonalizationScope.Shared),
       System.ComponentModel.Category("Settings"),
       System.ComponentModel.DefaultValue("")
       ]
        public string CustomProp
        {
            get { return customProp; }
            set { customProp = value; }
        }
    }

Now we get our custom property builder:

WebPartSettings

 

Lets assume that we want to pass the user entered value to the Visual component (the usercontrol) we now need to change the visual studio generated code to cast the user control to our visual user control class, rather than the more generic base Control:

       protected override void CreateChildControls()
        {
            //user control is of type VisualWebPart1UserControl and defined with private scope
            userControl = (VisualWebPart1UserControl)this.Page.LoadControl(_ascxPath);
            Controls.Add(control);
            base.CreateChildControls();
        }

 

From here we can set properties on the userControl variable as normal.

 

The same principles apply to web part connections, so the connection points need to be defined on the web part class (not the usercontrol). Visual Studio will take care of deploying the ascx file which is still a big win.

 

I doubt an experienced web part developer would have any issues, but I wonder how many new web part developers will not know that they can make there web parts configurable and connectable given that they will likely only use the Visual Studio lie presented to them?

Wednesday, November 18, 2009 10:30:00 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | SharePoint 2010
# Sunday, November 01, 2009

I’ve been playing around with VS 2010 beta 2, I’ve got it installed in one of my development VM’s which is also running the SharePoint application that I’ve been working on for the past few months. This SharePoint application is using the 3.5 SP1 version of the .NET framework and we haven’t yet migrated the solution and projects to VS 2010, they are all in the 2008 format. I bring this point up, because the profiling tools in VS 2010 don’t need a solution to be open in order to profile an application.

 

If your not familiar with the profiling features in VS 2010 then you should take a read of both the Profiler team blog post and also the post from John Robbins (debugging and profiling god).

 

The first step to be able to profile any .net application is:

 

1. Open the visual studio 2010 command prompt

2. Run the following command:  vsperfclrenv /globalsampleon

3. Restart IIS  

4. Now from VS you can select the Profiler –> Attach/Detach option from the Analyze menu option

 

image

 

5. Now you can run through your application and the profiling information will be captured.

6. Once your done you’ll be taken to the main overview screen:

 

ProfileOverview

 

7. Now you can drill into the hot paths etc … The blog posts I’ve listed above can help you drill into this in more detail.

8. To turn off the profiling switch just run vsperfclrenv /globaloff and restart IIS

 

The best bit is, if you have debugging symbols all the features will work, that is you can right click on the hot paths and the offending line of code will be shown and highlighted. So you don’t need to convert your project to VS 2010 to get this great profiling feature, it works with previous versions of the .NET framework.

Sunday, November 01, 2009 1:39:00 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [1] - Trackback
VS 2010
# Tuesday, October 20, 2009

Nice message after installing Visual Studio 2010 Beta 2:

 

BetaErrorMsg

 

Everything is OK? … must be an error :)

Tuesday, October 20, 2009 12:34:00 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
VS 2010
# Saturday, October 17, 2009

I came across a rather interesting problem the other day (interesting enough to blog anyway).

We have a development server that we are building some SharePoint web parts on, all of the developers have local admin rights to the box. We asked a normal user to have a look at some functionality that should be available to them, but when they browsed to the site they got a 403 forbidden error message. However when the developer requested that very same page it rendered fine. The weird thing was that if the original user (not the dev) then requested the page once again, it all rendered fine.

We had a look in the SharePoint Logs and found that the assembly in the bin folder was denying access to the non developer, but once a dev hit the box the assembly was loaded fine and stayed in memory until the app pool was recycled.

So the solution was to grant all users access to the bin directory. This wouldn’t be an issue in production because we will eventually GAC deploy these assemblies.

But I did think it was a fun little exercise to track down the root cause.

Saturday, October 17, 2009 4:30:00 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
Tip | Work
# Tuesday, September 22, 2009

Windows 7 contains a really nice tool called the Problem Steps Recorder:

 

It can be found via the Windows 7 start menu by typing ‘problem step’

image

 

Once it’s running you get the following UI:

 

 image

 

From here you can record all the clicks and keystrokes that your undertaking, you can even add comments to parts of your display. It finally saves the file as a zip file, which contains a .mht file that can be viewed in your browser. This output file will list things like version numbers of applications running as well as a range of screenshots of each major activity recorded.

 

Now this is all well and good for sorting your mum’s computer problems, but as an IT pro who has had to document some awfully boring processes, I really think this tool will help me the next time I need to document the install of some software, or some mundane configuration change.

Tuesday, September 22, 2009 2:25:00 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
Tip | Windows 7

I’ve been working on a project that involves implementing a blogging system inside SharePoint, the out of the box SharePoint blogging system was unsuitable and the Community Kit for SharePoint didn’t solve our issues.

So the system we developed was lightly based on the open source .NET Blog Engine which includes comprehensive support for the MetaWebBlog API. This API allows a interaction with a blogging system so that blog posts can be added, deleted and updated, but it also provides provisions for managing things like categories and comments.

The main motivation for supporting the MetaWebBlog API is Windows Live Writer (WLW) this great tool really makes publishing to a blog a simple task. So I happily added the MetaWebBlog API and quickly found that windows live writer doesn’t support windows authentication. WLW supports a nice feature called Really Simple Discovery (RSD) where it can just be pointed to a blog’s home page and it can get all the configuration needs. By not supporting windows authentication we lost the auto discovery features.

 

So I could still enable anonymous access to the MetaWebBlog.axd and wlwriter.xml (a file that tells WLW what capabilities your blog has), so now with some manual steps in WLW I could select the MetaWebBlog API and give it the url to use. My next problem was that my blogging system was all based around windows authentication. Every user has a blog that is based around their network credentials, I needed my metawebblog API implementation to validate the user to the windows network:

 

With the following P/Invoke definitions:

 

[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
 int dwLogonType, int dwLogonProvider, ref IntPtr phToken);                

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);

[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public extern static bool DuplicateToken(IntPtr ExistingTokenHandle,
 int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle); 
 

Now in all of my metawebblog API calls I could use a method like:

 

 

                string username = string.Empty;
                string password = input.Password;
                string domain = string.Empty;

                if (input.UserName.Contains(\\))
                {
                    string[] usernameParts = input.UserName.Split('\\');
                    if (usernameParts.Length > 1)
                    {
                        domain = usernameParts[0];
                        username = usernameParts[1];
                    }
                }
                else
                {
                    username = input.UserName;
                }

                IntPtr tokenHandle = IntPtr.Zero;

                bool returnValue = LogonUser(username, domain, password,3, 0, ref tokenHandle);

                if (returnValue == false)
                {
                    //error not a valid user .. return
                    throw new MetaWeblogException("11", "User authentication failed");
                }
                
                    if (tokenHandle != IntPtr.Zero)
                        CloseHandle(tokenHandle);

This code will return an invalid authentication message to WLW if the user doesn’t provide the correct domain, username and password (you could easily remove the need for a domain, I needed to keep it).

I’d like to say that its all good but really end users won’t go to the effort of following steps to configure WLW, the RSD is a killer feature in making web blogs accessible to end users. Fingers crossed that WLW will support windows authentication in a future version.

Tuesday, September 22, 2009 1:32:00 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | Tip

We’ve all probably worked on projects in the last 5 years or so that have involved reworking applications that were once built in MS access, in my case it was an access system that stored important safety testing information that was captured in an engineering workshop. It was originally designed and built by a mechanical engineer, who didn’t know much about database design.

The system was redeveloped for a number of reasons including:

  • The data couldn’t be shared, it was locked away on a PC in the workshop, no analysis of the data could be performed.
  • It didn’t scale well, only one user at a time could access it.

There are lots of other reasons why MS access shouldn’t be used for this type of information, but my point was that Access is a pretty poor tool for critical business information because it was difficult for the business to access this information. I think most people would share this view.

Lets compare a couple of scenarios with SharePoint as the tool, so all the information would be stored in a list:

  • The data can be shared by webservices and RSS, with effort.
  • It can scale, multiple users can access it at the same time.

But is the business data in a SharePoint list really easier to work with?

Can it:

  • Be used in SQL Server analysis cube?
  • Easily used in Reporting Services?
  • Joined with other business data to see correlations?
  • Perform complex real world queries?
  • Do you really want to model your business data based on the limitations of SharePoint?

The answer is no.

 

I still think that business data needs to live in a system designed for business data, i.e. A database: SQL Server.

From here it can be queried, joined and more importantly shared, whether that be back into SharePoint or any other tool that supports a database (Reporting Services, Performance Point, Analysis Services etc).

 

So now that brings me back to my comparison with MS Access, we are now doing lots of work moving systems away from MS Access, will we be doing the same thing in 5 years time, moving our SharePoint lists away from SharePoint?

 

I think SharePoint lists have their place, no question, but not for line of business data. Keep the SP lists for trivial data that is not important to the overall operation of your business.

Tuesday, September 22, 2009 12:43:00 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
Sharepoint
# Monday, September 21, 2009

With the upcoming SharePoint conference releasing information about SharePoint 2010, it won’t be too long before everyone will be looking at the product. Interestingly SharePoint 2010 is 64 bit only, this will have an impact on developers using Virtual PC 2007, which only supports 32 bit guest OS’s.

However windows 7 and Server 2008 R2 supports boot to VHD, this provides the ability to create a single VHD file that can booted. Once you’ve booted up the VHD your computer has access to all of it’s CPU cores, which is a major win. I’ve read that the performance hit of virtualising the file system to write to the VHD is around 5%, so it’s barely noticeable. Most importantly you can boot into a 64 bit OS where SharePoint 2010 can be installed.

To recap, the advantages of boot to VHD:

  • Can run 64 bit machines
  • Access to all CPU cores
  • Still keep portability by way of VHD files

Disadvantages

  • Can’t multitask with the primary OS, the VHD OS is the primary OS
  • Need to upgrade to windows 7 or server 2008 R2 (Not really a disadvantage, but might be an issue in corporate environments)
Monday, September 21, 2009 10:49:00 PM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
SP 2010 | Windows 7
# Saturday, June 13, 2009

By default SharePoint will use impersonation, so the web.config file will have the setting:

<identity impersonate="true" />

 

This means that if you try and connect to a SQL server database from say a custom web part, the connection will appear to SQL server as the user that requested the page. This is very useful if you care about the actual user, so for example if the database has permissions set based on this assumption.

However it is often useful to have just the application pool account connect to the SQL Server database or you may wish to give the application pool account permissions to connect to active directory but not every user.

It this case you will need to impersonate the application pool account. There is a bit of code floating around the web that uses P/Invoke to call ReverToSelf() from the advapi32.dll. It turns out it’s simpler than that:

 

using (HostingEnvironment.Impersonate()) 
{
 
    // access external resource as app pool account
 
}
 

 

The HostingEnvironment.Impersonate() will do the same thing that as a call to RevertToSelf().

 

If you like me and organise your data access calls into nice methods that perform a discrete action such as:

public static void DeleteUser(int userID)
{
 
//do some database access
 
}

 

You might be really loathed to wrap all these methods with the same code like:

public static void DeleteUser(int userID)
{
 
   using(HostingEnvironment.Impersonate())
   {
      //do some database access
   }
}

 

Instead you might like to take a look at what a project such as PostSharp can offer:

 

"You can make your own custom attributes that will really add new behaviors to your code! This is sometimes called aspect-oriented programming (AOP) or policy injection."

 

This means that you can define your own attribute that will have code that will run on any invocation of your code:

public class ImpersonateAttribute : OnMethodInvocationAspect
{
    public override void OnInvocation(MethodInvocationEventArgs eventArgs)
    {
        using(HostingEnvironment.Impersonate())
        {
            eventArgs.Proceed();
        }
    }
}

 

So our code then looks like this:

[ImpersonateAttribute]
public static void DeleteUser(int userID)
{
   //do some database access    
}

 

Much easier to maintain. Now every bit of code with this attribute will run as the application pool account.

Saturday, June 13, 2009 11:16:09 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | Sharepoint

I came across an interesting problem recently deploying an ASP.NET application to the ISV directory in CRM 4, the ASP.NET application was working fine using the Cassini web server inside of Visual Studio, but when it was deployed to the ISV directory on the CRM server it failed to run.

Basically any postback caused the web forms to loose all it’s current data, so things like dropdown lists would end up blank.

It was pretty obvious that this was caused by ViewState being turned off.

The default web.config file of CRM looks like:

 

<pages buffer="true" enableSessionState="false" enableViewState="false" validateRequest="false"/>

 

Since the ISV directory inherits from this web.config file, all the applications in the ISV directory will also have viewstate disabled.

 

So just add the following line to your web.config file in the ISV directory:

 

<pages enableViewState="true"/>

 

Personally I don’t really see the need to enable viewstate, in most cases it isn’t needed if the programmer understands it’s purpose, but sometimes your just trying to deploy code that someone else wrote.

Saturday, June 13, 2009 10:46:28 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
CRM | setup
# Saturday, June 06, 2009

A lot of functionality can be added to CRM with JavaScript, if your like me and do the bulk of your normal web development JavaScript with a toolkit like JQuery, you will quickly miss the power and ease of doing things without it. The good news is that it is pretty easy to load JQuery into the page:

 

image

 

var script = document.createElement('script');
script.type = 'text/javascript';
script.src = '/ISV/jquery.js';
script.onreadystatechange = function()
{
if(this.readyState == "complete" || this.readyState == "loaded")
   setupPage();
};
 
var head = document.getElementsByTagName('head')[0];
 
head.appendChild(script);

 

Now you can use the setupPage() function with all your familiar JQuery goodness.

Saturday, June 06, 2009 9:12:47 AM (E. Australia Standard Time, UTC+10:00)  #    Comments [0] - Trackback
code | CRM | JQuery
Statistics
Total Posts: 191
This Year: 0
This Month: 0
This Week: 0
Comments: 41