My Apps on the SharePoint Store

My Books

  • Apps
  • Azure
  • REST
  • CSOM/JSOM
  • Service Apps
  • WCF
  • REST/OData
  • CSOM/JSOM
  • SharePoint API
  • Silverlight
  • jQuery

Recent blog posts

User login

Home | Blogs | Stephane Eyskens's blog

SharePoint 2010, deploying a BCS Model using a farm property bag to have a dynamic siteurl

Hi,

As you probably noticed and as I've observed on the web, many people complain about the fact that when deploying a BCS model with Visual Studio, you have to specify a value for the feature property SiteUrl. If you don't, you might have troubles depending on your topology as described in the following blog posts http://trentacular.com/2011/05/deploying-sharepoint-bdc-models-via-features-without-specifying-a-siteurl/ and http://blogs.msdn.com/b/vssharepointtoolsblog/archive/2010/07/30/deploy-your-bdc-model-to-a-specific-bcs-service-using-visual-studio-2010.aspx

The problem with the static approach is that usually the URL varies according to the environment (dev, test, acceptance, production...) and you do not want to create several packages. The approaches described in the above posts are ok but seem a bit overkill to me.

As an alternative, it is also possible to implement your own deployment routine with a little bit of effort. By default, when you create a BCS Project with Visual Studio or with BCS Meta Man, it creates a farm feature to deploy your model. This farm features has a feature receiver pointing to a built-in class :

ReceiverAssembly="Microsoft.Office.SharePoint.ClientExtensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" ReceiverClass="Microsoft.Office.SharePoint.ClientExtensions.Deployment.ImportModelReceiver".

The idea is to make your own feature receiver calling a helper class deriving from the built-in class. Then, you can use the property SiteUrl but instead of specifying a static URL, getting its value from a Farm property bag dynamically.

Here are the steps to do that:

  • Create a BCS Meta Man Project or a Visual Studio Project


  • Click on the model and go to its properties, empty the feature receiver associated to it


  • Add the properties SiteUrl and SiteUrlProp as part of your feature properties and specify the name of the farm property bag


  • Add a reference to Microsoft.Office.SharePoint.ClientExtensions.dll


  • Add a new event receiver to your feature and implement the following code :
    public class SiteUrlBCSEventReceiver : SPFeatureReceiver
    {
        const string SiteUrl = "SiteUrl";
        const string SiteUrlProp ="SiteUrlProp";
        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            ReworkProperty(properties);
            DynamicBCSDeployment d = new DynamicBCSDeployment();               
            d.FeatureActivated(properties);
        }
        public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            ReworkProperty(properties);
            DynamicBCSDeployment d = new DynamicBCSDeployment();
            d.FeatureDeactivating(properties);
        }
    
        void ReworkProperty(SPFeatureReceiverProperties properties){
    	  SPFarm LocalFarm=((SPWebService)properties.Feature.Parent).Farm;
              if (LocalFarm.Properties[properties.Definition.Properties[SiteUrlProp].Value] == null)
                throw new ArgumentException("Missing farm property");
              
    	  properties.Definition.Properties[SiteUrl].Value = LocalFarm.Properties[properties.Definition.Properties[SiteUrlProp].Value].ToString();
              properties.Feature.Properties.Update();
    
        }
                
    }
    public class DynamicBCSDeployment : ImportModelReceiver
    {
        public DynamicBCSDeployment() { }
    }
    


Of course, you should make it a generic helper but for clarity reasons, I regroup everything in a single code block. The ReworkProperty method is also called when deactivating the feature to make sure that if you changed the value of the farm property, that change is taken into account.


Before deploying, just ensure you have created your farm property with the right value. As said earlier, each farm (dev, acceptance, prod...) will have its own property with its own value but with the same name, so your code does not need to be changed and you don't have to fiddle with dummy web applications or urls. Moreover, if you have several possible deployment URLS, just create as many farm properties as possible deployment URLS in each of your different environments and you chose the appropriate property key in the SiteUrl parameter.

If you want, you can of course work with something else than farm properties but the idea is to have something flexible enough.

Now you can just deploy and enjoy!

Happy Coding!

Comments

Query on your solution

Hi,

First of all thanks for sharing such a wonderfull article though i have some query for the same.
As you have mentioned "Add the properties SiteUrl and SiteUrlProp as part of your feature properties and specify the name of the farm property bag"

It would be great if you can explain that what should be the value we should provide for SiteUrl & SiteUrlProp and what do you mean by specify the name of the farm property bag and where should we specify that?

Thanks in advance.

Extra Info

Hi,

The whole idea behind this post is to make your BCS deployment model dynamic. A Farm Property bag is a Farm property that you can add programmatically via a feature or with PowerShell or even via Central Administration providng you install a solution such as the Property Bag Settings (http://pbs2013.codeplex.com/) the same exists for SharePoint 2010.

The idea is to define one Farm property in each environment (dev/integration/staging/prod) and define the corresponding URL for each environment. This URL will be different and that's why I suggest using a Farm property bag to hold that different value. The name of the property should of course be the same in each environment. The advantage of the Farm property is that it is accessible to all users in reading, even anonymous and you don't need to fiddle with web.config or whatever.

Once you have defined that property, you can use the technique described in the post. In the SiteUrlProp feature property, you assign the name of your Farm property. When your custom feature receiver gets activated, you go read that name and get the actual value of the target URL where you want to deploy your BCS. In that same receiver, you just assign the property bag value (the target URL) to the SiteUrl property and then, you call the FeatureActivated hook of your custom class that inherits from ImportModelReceiver. The SiteUrl value is read by the base class which will deploy the BCS Model to it.

Best Regards

Our solution

We attempted to use this model, with a configuration file to set and unset the ENV variable for different environments as it went in based on a detected url.. For some reason the SPFarm.Local.Properties didn't ever find the new ENV variable, although we could see it from powershell.

So what we did instead is listed the siteUrl of each target environment then looped through those values and attempted to deploy into each. We have 4 enviornments, so 3 always fail, but we were able to get it to deploy successfully regardless of what enviornment..

XML

Feature Receiver code...
string[] envs = new string[4] { "DEV", "TEST", "INTTEST", "PROD" };

public override void FeatureActivated(SPFeatureReceiverProperties properties) {
foreach(string env in envs)
{
try
{
ReworkProperty(properties, env);
DynamicBCSDeployment d = new DynamicBCSDeployment();
d.FeatureActivated(properties);
}
catch (ArgumentException e)
{
//ignore
}
catch (Exception ee)
{
throw ee;
}
}
}

Enumeration

Hi,

Thanks for sharing! Farm property is in theory always OK, so yo don't need to use an ENV variable or a registry entry. FARM properties are readable by any user (even anonymous) so providing you set it up correctly along with your deployment, everything should be ok.

Anyway, happy that you found your own way.

Best Regards

You're smarter than others

Your workaround is a real production scenario workaround ! thanks for this usefull tips !

Thanks :)

Thanks :)

Good Solution!

I feel that the other solutions that were linked in your blog post are not practical, especially when you dont have control over creating web apps or changing host headers in a staging and/or production environment. Which is usually the case.

I like the implementation here, it is much more elegant. Thanks for the post!

Reddy

Simpler way

You can get the site URL from the feature properties.
private void ReworkProperty(SPFeatureReceiverProperties properties)
{
string siteUrl = string.Empty;
SPWebService webService = (SPWebService)properties.Feature.Parent;
SPWebApplication webApp = webService.WebApplications.Where(
wa => wa.Sites.Count > 0 &&
wa.IsAdministrationWebApplication == false).FirstOrDefault();
if (webApp != null)
{
using (SPSite site = webApp.Sites[0])
{
siteUrl = site.Url;
}

if (properties.Definition.Properties[SiteUrl] == null)
{
properties.Definition.Properties.Add(new SPFeatureProperty(SiteUrl, siteUrl));
}
else
{
properties.Definition.Properties[SiteUrl].Value = siteUrl;
}

properties.Feature.Properties.Update();
}
}

see example at: http://zormim.wordpress.com/2012/10/01/integrating-sharepoint-and-system...

Hi, Thanks for your comment.

Hi,

Thanks for your comment. However my way is dynamic not yours. Indeed you always take the first site collection of the first webapp that has at least one site col and that is not the admin app.

With my way, you can define the URL of your choice in a property bag.

Best Regards