Archive for December, 2009

12/18/09
Fahad Zia

The content type “x” is part of an application feature


I had to deal with this issue recently when trying to delete content type i provisioned using feature. I wasted alot of hours on to resolve this. Let me start with how i started with this feature. I developed this feature to provision site columns to the site collection with following xml (I am not showing all the attributes, only the ones that matter for this post):

<Feature  Id="85801D0D-C686-4f85-91D3-200703281A64" />
  <ElementManifests>
    <ElementManifest Location="sitecolumns.xml" />
    <ElementManifest Location="contenttypes.xml" />
    <ElementManifest Location="workflow.xml" />    
  </ElementManifests>
</Feature>

So now my site columns and content types were working as expected. Then the requirements change and i had to install site columns and content types on “Web” scope instead of “Site”. First of all i found out that you CANNOT deploy site columns and site content types using CAML using feature(annoying!). To deploy on “Web” scope i had to add them using SharePoint object model. So to do this i added the code and commented out contenttypes.xml and sitecolumns.xml from my feature. Here’s how:

<Feature  Id="85801D0D-C686-4f85-91D3-200703281A64" />
  <ElementManifests>
    <!--<ElementManifest Location="sitecolumns.xml" />-->
    <!--<ElementManifest Location="contenttypes.xml" />-->
    <ElementManifest Location="workflow.xml" />    
  </ElementManifests>
</Feature>

Next thing i did was i upgraded my solution WITHOUT deactivating the feature. So now i wanted to delete the existing site columns and site content types as I was getting error of duplicate names when adding them using feature activation code. I thought simple let me delete it from the UI and then activate my feature. It wasn’t as simple as I thought. When I tried to delete the content types i would get “The content type “x” is part of an application feature”. I deleted all the items/libraries where I added this content type but SharePoint wouldnt stop complaining whenever I tried to delete the content types.

Googling for this error pointed me to some good posts like: http://blog.thekid.me.uk/archive/2008/11/03/a-tip-when-looking-at-the-sharepoint-content-db.aspx and http://www.codeplex.com/spcontenttypeexplore, it did help me to find out that there was NO library using the content type i was trying to delete and pointed to site columns that were contained in the content type.

More google and I came across this post: http://stackoverflow.com/questions/1150070/deleting-a-content-type-in-sharepoint. I followed what it said:

“stsadm.exe -o deactivatefeature
stsadm.exe -o uninstallfeature -force
stsadm.exe -o retractsolution -immediate
(execute the jobs created from retracting the solution)stsadm.exe -o execadmsvcjob
sstsadm.exe -o deletesolution”

This dint work either but it made me think in the right direction. So eventually what worked was I decommented by feature lines for sitecolumns.xml and contenttypes.xml basically taking it to the orginal settings:

<Feature  Id="85801D0D-C686-4f85-91D3-200703281A64" />
  <ElementManifests>
    <ElementManifest Location="sitecolumns.xml" />
    <ElementManifest Location="contenttypes.xml" />
    <ElementManifest Location="workflow.xml" />    
  </ElementManifests>
</Feature>

upgraded solution, activated feature and then deactivated feature and the content types i was trying to delete went away!!
Lesson learned: You cannot delete content type that is provisioned from feature using GUI. Commenting out the contenttypes.xml in my feature prevented SharePoint to delete these content types when I deactivated feature hundreds of times in desperation.

12/17/09
Fahad Zia
tags:   ,

Download BLOB in SQL Server using SSRS and ASP.NET


I came across a requirement recently where I have to let the user download files from the links displayed in SSRS report. Here’s how I approached it:
1. Provide an ASP.NET page(requestfile.aspx) that would query the BLOB column in the database and return the file by reading required column values from querystring.
2. Provide link from SSRS report to the ASP.NET page and pass it the querystring values it needs

Here is the ASP.NET page (requestfile.aspx) code that reads information from querystring and returns the BLOB as file:

/// <summary>
    /// Query the database to download BLOB column as file
    /// </summary>    
    protected void Page_Load(object sender, EventArgs e)
    {
        string strTableName = "";
        string strColumnName = "";
        string strPKColumnName = "";
        string strPKColumnValue = "";
        string strFileName = "";
        string strConnectionString = "Data Source=.;Initial Catalog=xx;Trusted_Connection=True;";
        byte[] data=null;

        strTableName = Request.QueryString["tbl"].ToString();
        strColumnName = Request.QueryString["colName"].ToString();
        strPKColumnName = Request.QueryString["pkColName"].ToString();
        strPKColumnValue = Request.QueryString["pkColValue"].ToString();
        strFileName = Request.QueryString["filename"].ToString();

        string strSQL = string.Format("SELECT {0} FROM {1} WHERE {2}={3};",
                                        strColumnName,
                                        strTableName,
                                        strPKColumnName,
                                        strPKColumnValue);
        using (SqlConnection conn = new SqlConnection(strConnectionString))
        {
            conn.Open();
            SqlCommand cmd = new SqlCommand(strSQL,conn);
            SqlDataReader rdr = cmd.ExecuteReader();

            while (rdr.Read())
            {
                data = (byte[])rdr[strColumnName];
                break;
            }
            if (data.Length > 0)
            {
                Response.Buffer = true;
                Response.ContentType = string.Format("application/{0}",Path.GetExtension(strFileName));
                Response.AddHeader("content-disposition",string.Format("attachment;filename={0}",strFileName));
                Response.BinaryWrite(data);
                Response.Flush();
            }
            conn.Close();
        }
    }

Now in SSRS i created an embedded function called WriteJavascriptString:

Function WriteJavascriptString (FileName As String, _
			TblName As String, _
			ColName As String, _
			PKColName As String, _
			PKColValue As String) As String
	Return "javascript:void window.open('http://localhost:4127/website/requestfile.aspx?filename=" & FileName & "&tbl=" & TblName & "&colName=" & ColName & "&pkcolName=" & PKColName & "&pkColValue=" & PKColValue & "','_blank')"
End Function

ColName is the BLOB column i.e. of type varbinary. TblName is the table that has this column, PKColName is the column name that unqiuely identifies the row and PKColValue is its value.

and finally this code is linked to the field text boxes on the report by right clicking the text box then “Properties”->”Navigation” tab->”Jump to URL” and writing the expression as shown below:

=IIf(IsNothing(First(Fields!Document_blob.Value, "dsDocuments")),"",Code.WriteJavascriptString(First(Fields!Document_filename.Value, "dsDocuments"),"DOCUMENTS_TBL","Document_blob","DocID",Parameters!DocID.Value))