Thursday, April 28, 2011

Impact of Content Type changes on existing Lists in SharePoint 2007

In real world, we come across of many business scenarios that force us to add/edit/remove some columns to our existing Lists. Sometimes these Lists are heavily populated with large amount of data and workflows running on them. As we all know we add columns to a SharePoint List using Content Types for reusability purpose. So changing the columns of a List means changing the Content Type associated with the List. In such a situation, several questions come to one's mind.

1. Can I simply add the new field references to my existing ContentType CAML and re-activate the feature ? Will the new fields be added to the existing Lists ? Will it impact the running workflows on the Lists ?

We all know for sure that the new fields will be part of a new List that we create after the change. But what about the existing Lists. Simply adding the new field references to the existing Content Type CAML will not reflect in the existing Lists. You need to add these field references programmatically inside a feature receiver by passing true to the Update() method of the Content Type. Here is the sample code snippet.


public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
using (SPWeb web = ((SPSite)properties.Feature.Parent).OpenWeb())
{
SPContentType pcType = web.ContentTypes[new SPContentTypeId("GUID of the existing Content Type")];
SPField newField = web.Fields[new Guid("GUID of Field")];
SPFieldLink newFieldLink = new SPFieldLink(newField);
pcType.FieldLinks.Add(newFieldLink);
pcType.Update(true);
}
}


Any change in the Content Type should not impact the running workflows on the Lists.

When we remove fields from an existing Content Type and re-deploy it, the same columns would not be removed from the Lists that use the Content Type. Safety of the List Data is utmost important for SharePoint.

At the end, Don't forget to remove these field references inside feature deactivating event.

public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
using (SPWeb web = ((SPSite)properties.Feature.Parent).OpenWeb())
{
SPContentType pcType = web.ContentTypes[new SPContentTypeId("GUID of the existing Content Type")];
SPField newField = web.Fields[new Guid("GUID of Field")];
if (pcType.Fields.ContainsField(newField .Title))
pcType.FieldLinks.Delete(new Guid(GUID of Field));
pcType.Update(true);
}
}


Some facts related to Content Type -
If you delete the content type associated to a List, then it won't delete the columns in the List. They would be still exist as local columns. if you want to remove them from your List, then you have to delete them manually or programmatically.

What's New in C# 4.0

1) Optional Parameters


public class Order
{
//Old way
public int Purchase(Product product, Customer customer, bool isResale, string discountCode)
{

}
// New way
public int Purchase(Product product, Customer customer, bool isResale = false, string discountCode = string.Empty)
{
}
}
public class Program
{
static void Main(string[] args)
{
Order newOrder = new Order();
Product item = new Product { ItemCode = "A8901", Price = 50, Qty = 5};
Customer customer = new Customer();
customer.UserName = "R887663";
customer.PreferredCustomer = true;
newOrder.Purchase(item, customer);
}
}


2. Named Parameters


public class Program
{
static void Main(string[] args)
{
Order newOrder = new Order();
Product item = new Product { ItemCode = "A8901", Price = 50, Qty = 5};
Customer customer = new Customer();
customer.UserName = "R887663";
customer.PreferredCustomer = true;
newOrder.Purchase(item, customer, discountCode: 76883987);
}
}

3. Use of dynamic keyword


static class DynamicDemo
{
public static void Execute()
{
var person = new Person { Name = "Alex" };
var company = new Company { Name = "Microsoft" };

DoSomething(person);
DoSomething(company);
}

static void DoSomething(dynamic item)
{
Console.WriteLine(item.Name);
}
}

class Person
{
public string Name { get; set; }
}

class Company
{
public string Name { get; set; }
}



4. Use of DynamicObject


class DynamicObjectSimpleDemo : DynamicObject
{
private Dictionary members = new Dictionary();

public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return this.members.TryGetValue(binder.Name, out result);
}

public override bool TrySetMember(SetMemberBinder binder, object value)
{
this.members[binder.Name] = value;
return true;
}
}

public class DynamicObjectXDemo : DynamicObject
{
private XElement element;

public DynamicObjectXDemo (XElement element)
{
this.element = element;
}

public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = this.element.Attribute(binder.Name).Value;
return true;
}

public override bool TrySetMember(SetMemberBinder binder, object value)
{
this.element.SetAttributeValue(binder.Name, value);
return true;
}
}

class program
{
static void main(string[] args)
{
dynamic sample = new DynamicObjectSimpleDemo();
sample.Fname = "Alex";
sample.LName = "Palle";

var xml = XElement.Load("Clients.xml").Elements().Select(x => new DynamicObjectXDemo(x));

foreach (dynamic item in xml)
{
Console.WriteLine(item.FName+ " " + item.LName);
}
}
}

//Clients.xml









What's New in ASP.NET 4.0

1. In .NET Framework 4.0, the major configuration elements are moved to machine.config and applications now inherits these settings.
Sample of ASP.NET 4 Web.Config file









2. Output caching in ASP.NET 4 enables you to configure one or more custom output- cache providers. Output-cache providers can use any storage mechanism i.e. local or remote disks, cloud, distributed cache engine etc. to persist HTML content.
Example of specifying a different output-cahce provider for web page or web user control.


<%@ OutputCache Duration="60" VaryByParam="None"
providerName="DiskCache" %>


3. Some ASP.NET applications need to load large amount of data during initilization of the application before serving the first request. Prior to Asp.NET 4.0, we achieve the compilation of the application before the first request using the command Aspnet_compiler -v /MyWebSite. In Asp.NET 4.0, auto-start feature is available when ASP.NET 4 runs on IIS 7.5 on Windows Server 2008 R2.

You need to set following configuration in the ApplicationHost.config file.







If your application pool containing multiple applications, you can auto-start individual applications by setting the following configuration in applicationHost.config file.






The auto-start feature enables you to

  1. Start up the Application pool.
  2. Initialization of the Application
  3. It temporarily stops processing any http request till the initilization process is over.


4. The ASP.NET Redirect method results an extra round trip while doing a temporary redirect to an URL. ASP.NET 4.0 adds a new Helper method RedirectPermanent() that saves an unnecessary round trip.
While the Redirect method returns HTTP 302(Found) response, the RedirectPermanent() method issues HTTP 301(Permanent) response.

5. In Asp.net, The out of process Session State involves serialized session date sent to remote storage. It becomes a performance bottleneck if the data involved is large. The ASP.NET 4.0 Session State Compression feature helps to reduce the size of the data by compressing the Session State using GZipStream class. The Applications that have spare CPU cycles on the Web Server can achieve substantial performance improvement by using this feature.

You can set this option using the new compressionEnabled attribute of the sessionState element in the configuration file.

6. ASP.NET 4.0 allows you to configure the application URL path, query string length and invalid characters that are not allowed in the application url. You can configure them as follows.





7. The ASP.NET 4.0 System.Runtime.Caching API provides an uniform caching technique for all .NET applications (Web, Windows Form). It provides abstract types for custom cache implementation and a concrete in-memory object cache class called MemoryCache.

8. In ASP.NET 4, you can create custom encoding routines for the following common text-encoding tasks:

  • HTML encoding.
  • URL encoding.
  • HTML attribute encoding.
  • Encoding outbound HTTP headers.


Configure the custom encoding routines as follows:




9. CDN base Ajax and Jquery loading to ASP.NET 4.0 Applications.






[assembly: WebResource("Example.js", "application/x-javascript", CdnPath ="http://contoso.com/app/site/Example.js")]



10. 2 new properties are added to the Page class i.e. MetaKeywords, MetaDescription that helps set meta tags for search engine submission. The html rendered on the browser by these 2 properties is as follows:


<html>
<head>
<meta name="keywords" content="keyword1, keyword2"/>
<meta name="description" content="description"/>
</head>
<html>


11. A New property ViewStateMode is added to Control class. Setting ViewStateMode=false of the Control class disables viewstate for all the controls on the page. You can use ViewStateMode property of indiviual control to enable view state explicitly.

12. ASP.NET MVC way of routing in ASP.NET 4 Web Forms.

13. We use a Control's Client ID property to access that control in client script. Prior to ASP.NET 4.0, the Client ID is generated automatically by ASP.NET engine and sometimes it is difficult to predict the Client ID of a control to use in client script. In ASP.NET 4.0, you can specify how it can be generated by using ClientIDMode property at Page level or control level. It has following values:

Auto - It provides the same behaviour ASP.NET was providing prior to ASP.NET 4.0

Static - Instead of automatically generating Client ID, it sets the ID of the control as Client ID.

Predictable - For a Data Bound control, You can control how the ID of each instance of the control would be created by using ClientIDRowSuffix property. The value of this property will be appended to the Client ID property of each instance of the control.

Inherit - Inherit the ClientIDMode property of the container control to generate the Client ID of the control. If the control doesn't have a container control then it uses the ClientIDMode property of the Page.

The default mode for a site or page is - Predictable
The default mode for a control is - inherits

The following example shows how to set the default ClientIDMode for a site in the Web.config file.





The following example shows how to set the default ClientIDMode for a page in the @ Page directive.


<%@ Page Language="C#" CodeFile="Default.aspx.cs"

Inherits="_Default" ClientIDMode="Static" %>


14. Persisting row selection in Data Controls - Rows selected in Pages will be persisted across pages. When you select a row 3 on Page1 and move to Page8. If you go back to Page1, you will see row3 as selected. To achieve this, use PersistedSelection property of the Data Control.






15. Filtering date with the QueryExtender control. The Query Extender control can be added to EntityDataSource or LinqDataSource to filter the data. The QueryExtender has following filter options-


ContextTypeName="AdventureWorksDataContext" EntityTypeName=""
TableName="Products" >



SearchExpression -
RangeExpression-
PropertyExpression-
OrberByExpression-
CustomExpression-


<html>
<head runat="server">
<title>Filter Demo</title>
</head>
<body>
<form id="form1" runat="server">
Search:<asp:TextBox ID="SearchTextBox" runat="server" />
<p>

Make More:<asp:CheckBox ID="MakeCheckBox" runat="server" />
<p>
From:<asp:TextBox ID="FromTextBox" runat="server" ></asp:TextBox>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
To:<asp:TextBox ID="ToTextBox" runat="server" ></asp:TextBox>
</p>
<p>
<asp:Button ID="Button1" runat="server" Text="Search" />
</p>
<asp:LinqDataSource ID="LinqDataSource1"
ContextTypeName="FilterDemo.AdventureWorksDataContext"
TableName="Products" runat="server">
</asp:LinqDataSource>
<asp:QueryExtender runat="server" TargetControlID="LinqDataSource1">

<asp:SearchExpression SearchType="StartsWith" DataFields="Name" >
<asp:ControlParameter ControlID="SearchTextBox" />
</asp:SearchExpression>

<asp:RangeExpression DataField="ReorderPoint" MinType="Inclusive" MaxType="Inclusive">
<asp:ControlParameter ControlID="FromTextBox" />
<asp:ControlParameter ControlID="ToTextBox" />
</asp:RangeExpression>

<asp:PropertyExpression>
<asp:ControlParameter ControlID="MakeCheckBox" Name="MakeFlag" />
</asp:PropertyExpression>
</asp:QueryExtender>

<asp:GridView ID="GridView1" runat="server"
DataSourceID="LinqDataSource1" AllowPaging="True"
DataKeyNames="ProductID>
</asp:GridView>
</form>

</body>
</head>
</html>

Tuesday, April 19, 2011

Using WebGrid in ASP.NET MVC3


@model IEnumerable

@{
ViewBag.Title = "Employee Grid";
}

@{
var grid = new WebGrid(source: Model, defaultSort:"Name", rowsPerPage:3);
}

Employee Grid



@grid.GetHtml(columns: grid.Columns(grid.Column("Id"), grid.Column("Name"), grid.Column("JoiningDate"), grid.Column("PAN")))

MVC Application Execution Process (C#)


  1. When Receive first request for the application-
    In the Global.asax file, Route objects are added to the RouteTable object
  2. Perform routing -
    The UrlRoutingModule
    module uses the first matching Route object in the RouteTable
    collection to create the RouteData object, which it then uses
    to create a RequestContext (IHttpContext) object.
  3. Create MVC request handler -
    The MvcRouteHandler object creates an instance of the MvcHandler class and passes it the RequestContext instance.
  4. Create controller -
    The MvcHandler object uses the RequestContext instance to identify the IControllerFactory object (typically an instance of the DefaultControllerFactory class) to create the controller instance with.
  5. Execute controller -
    The MvcHandler instance
    calls the controller s Execute method.
  6. Invoke action -
    Most controllers inherit from the Controller base class. For controllers that do so, the ControllerActionInvoker object that is associated with the controller determines which action method of the controller class to call, and
    then calls that method.
  7. Execute result -
    A typical action method might receive user input, prepare the appropriate response data, and then execute the result by returning a result type. The built-in result types that can be executed include the following: ViewResult (which
    renders a view and is the most-often used result type), RedirectToRouteResult,
    RedirectResult
    , ContentResult, JsonResult, and
    EmptyResult
    .

Friday, April 15, 2011

Extract WSP from SharePoint Solution Store


using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

static void Main(string[] args)
{
const string extractingTo = "e:\\Solutions\\";
SPSolutionCollection solutions = SPFarm.Local.Solutions;
foreach (SPSolution solution in solutions)
{
Console.WriteLine("Extracting " + solution.Name);
SPPersistedFile wspFile = solution.SolutionFile;
wspFile.SaveAs(extractingTo + solution.Name);
}
}

System.Data.SqlClient.SqlException while installing SPJob during Feature activation

Recently we came across following exception while installing SPJob during Feature installation. System.Data.SqlClient.SqlException: The EXECUTE permission was denied on the object 'proc_putObject', database 'SharePoint_Config', schema 'dbo'.

Reason: The scope of the feature was "Web". In fact the same exception will occure if you define the scope of the feature as "Site".

Solution: Define the scope of the feature as "Web Application".

Monday, April 11, 2011

Edit Header/Footer of Word Document programmatically using Office Open XML

Scenario- My Client wants to publish policy document(Word Document) to multiple departments with department specific header and footer.

Design-
System.IO.Packing API provides strongly typed classes to manipulate office documents that adhere to office open XML file format specification. An open XML document like Word Document is stored as a package. The package has multiple parts like main document, header, footer etc. with relationship between them. The header part is included in the document by header reference tag. Changing the header information of a Word Document is to delete the existing header part and add a new header part.

Solution-

public SPFile ProcessHeaderFooter(string targetFilePath, string department)
{
string headerInfo = GetHeaderInfo(department);
string footerInfo = GetFooterInfo(department);

Stream headerStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(headerContent));
Stream footerStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes (footerContent));

SPWeb web = SpContext.Current.Web;
SPFile targetFile = web.GetFile(targetFilePath);
MemoryStream fileStream = new MemoryStream();
fileStream.Write(targetFile.OpenBinary(), 0, (int)targetFile.TotalLength);
AddHeader(headerStream, fileStream);
AddFooter(footerStream, fileStream);
targetFile.SaveBinary(fileStream);
fileStream.Close();
return targetFile;
}

public void AddHeader(Stream headerContent, Stream fileContent)
{
const string documentRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
const string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
const string headerContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml";
const string headerRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/header";
const string relationshipNamespace = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
PackagePart documentPart = null;
using (Package wdPackage = Package.Open(fileContent, FileMode.Open, FileAccess.ReadWrite))
{
// Get the main document part (document.xml).
foreach (System.IO.Packaging.PackageRelationship relationship in wdPackage.GetRelationshipsByType(documentRelationshipType))
{
Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), relationship.TargetUri);
documentPart = wdPackage.GetPart(documentUri);
// There is only one officeDocument.
break;
}

Uri uriHeader = new Uri("/word/header1.xml", UriKind.Relative);

if (wdPackage.PartExists(uriHeader))
{
wdPackage.DeletePart(uriHeader);
}

// Create the header part.
PackagePart headerPart = wdPackage.CreatePart(uriHeader, headerContentType);
XmlDocument headerDoc = new XmlDocument();
headerContent.Position = 0;
headerDoc.Load(headerContent);

// Write the header out to its part.
headerDoc.Save(headerPart.GetStream());

// Create the document's relationship to the new part.
PackageRelationship rel = documentPart.CreateRelationship(uriHeader, TargetMode.Internal, headerRelationshipType);
string relID = rel.Id;

// Manage namespaces to perform Xml XPath queries.
NameTable nt = new NameTable();
XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);
nsManager.AddNamespace("w", wordmlNamespace);

// Get the document part from the package.
// Load the XML in the part into an XmlDocument instance.
XmlDocument xdoc = new XmlDocument(nt);
xdoc.Load(documentPart.GetStream());

// Find the node containing the document layout.
XmlNode targetNode = xdoc.SelectSingleNode("//w:sectPr", nsManager);
if (targetNode != null)
{
// Delete any existing references to headers.
XmlNodeList headerNodes = targetNode.SelectNodes("./w:headerReference", nsManager);
foreach (System.Xml.XmlNode headerNode in headerNodes)
{
targetNode.RemoveChild(headerNode);
}

// Create the new header reference node.
XmlElement node = xdoc.CreateElement("w:headerReference", wordmlNamespace);
XmlAttribute attr = node.Attributes.Append(xdoc.CreateAttribute("r:id", relationshipNamespace));
attr.Value = relID;
node.Attributes.Append(attr);
targetNode.InsertBefore(node, targetNode.FirstChild);
}

// Save the document XML back to its part.
xdoc.Save(documentPart.GetStream(FileMode.Create, FileAccess.Write));
}
}


public void AddFooter(Stream footerContent, Stream fileContent)
{
const string documentRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument";
const string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
const string footerContentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml";
const string footerRelationshipType = "http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer";
const string relationshipNamespace = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
PackagePart documentPart = null;
using (Package wdPackage = Package.Open(fileContent, FileMode.Open, FileAccess.ReadWrite))
{
// Get the main document part (document.xml).
foreach (System.IO.Packaging.PackageRelationship relationship in wdPackage.GetRelationshipsByType(documentRelationshipType))
{
Uri documentUri = PackUriHelper.ResolvePartUri(new Uri("/", UriKind.Relative), relationship.TargetUri);
documentPart = wdPackage.GetPart(documentUri);
// There is only one officeDocument.
break;
}

Uri uriFooter = new Uri("/word/footer1.xml", UriKind.Relative);
if (wdPackage.PartExists(uriFooter))
{
// Although you can delete the relationship
// to the existing node, the next time you save
// the document after making changes, Word
// will delete the relationship.
wdPackage.DeletePart(uriFooter);
}
// Create the footer part.
PackagePart footerPart = wdPackage.CreatePart(uriFooter, footerContentType);

XmlDocument footerDoc = new XmlDocument();
footerContent.Position = 0;
footerDoc.Load(footerContent);
// Write the footer out to its part.
footerDoc.Save(footerPart.GetStream());
// Create the document's relationship to the new part.
PackageRelationship rel = documentPart.CreateRelationship(uriFooter, TargetMode.Internal, footerRelationshipType);
string relID = rel.Id;

// Manage namespaces to perform Xml XPath queries.
NameTable nt = new NameTable();
XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);
nsManager.AddNamespace("w", wordmlNamespace);

// Get the document part from the package.
// Load the XML in the part into an XmlDocument instance.
XmlDocument xdoc = new XmlDocument(nt);
xdoc.Load(documentPart.GetStream());

// Find the node containing the document layout.
XmlNode targetNode = xdoc.SelectSingleNode("//w:sectPr", nsManager);
if (targetNode != null)
{
// Delete any existing references to footers.
XmlNodeList footerNodes = targetNode.SelectNodes("./w:footerReference", nsManager);
foreach (System.Xml.XmlNode footerNode in footerNodes)
{
targetNode.RemoveChild(footerNode);
}

// Create the new footer reference node.
XmlElement node = xdoc.CreateElement("w:footerReference", wordmlNamespace);
XmlAttribute attr = node.Attributes.Append(xdoc.CreateAttribute("r:id", relationshipNamespace));
attr.Value = relID;
node.Attributes.Append(attr);
targetNode.InsertBefore(node, targetNode.FirstChild);
}

// Save the document XML back to its part.
xdoc.Save(documentPart.GetStream(FileMode.Create, FileAccess.Write));
}
}


References
http://msdn.microsoft.com/en-us/library/bb491088(v=office.12).aspx

Tuesday, April 5, 2011

Impersonating using SPUserToken object


using (SPSite site = new SPSite(siteURL))
{
using (SPSite siteAsOtherUser = new SPSite(siteURL, site.RootWeb.AllUsers["WS2003\\Administrator"].UserToken))
{
SPWeb web = siteAsOtherUser.OpenWeb();
//Write code with elevated privilege.
}
}

Type of users in SharePoint

Users: Users explicitly added to the SPWeb.
AllUsers: Users explicitly added to the SPWeb + Users got access implicitly thorough groups of SPWeb.
SiteUsers: Aggregation of all Users at the SiteCollection level.