May 2008 - Posts
Famous SharePoint features developing company Bamboo Solutions has created installer for Windows SharePoint Services 3.0 SP1 that install WSS3.0 on Windows Vista. The solution allows developers to create SharePoint solutions on their own machines. The need for virtual machine is still there, so don't hope you can avoid them.
The Bamboo Team Blog calls developers to try out their solution. To get things up and running there is entry titled as How to install Windows SharePoint Services 3.0 SP1 on Vista x64/x86 in their blog. The installer is free to use for everyone.
So feel free to try out SharePoint on Vista!
One cool feature of C# 3.0 is support of anonymous types. Let's suppose we have to create some data structure and we need this structure in one place in one method. This far we had to create a new private class or structure. With anonymous types we don't have to define new type - we can create it on the run.
Let's create a simple anonymous type for rectangle. The code is as follows and let's suppose it makes some calculations using dimensions of object.
... var rectangle = new { Width=x, Height=y }; ...
return density;
As we can see there is no type specified for rectangle. Instead type we have plain definition of object. This definition is also supported by IntelliSense.
If we look at previous picture we can see that our new variable has all the properties that objects have. It is inherited from object class. Let's see now what compiler produced us.
As we can see there is new type called <>f__AnonymousType0`2 defined. This is our rectangle. The other classes are not able to inherit from our rectangle and also it is not possible to recreate this type in code again. This new type is created after compilation and it doesn't exist until compilation.
Anonymous types may be very useful if we need some temporary structures to organize our data better. Anonymous types must be used very carefully because otherwise they may mess up your code and make readability worst. Therefore it is good idea to use anonymous types carefully.
C# 3.0 introduced us new language feature: type inference using keyword var. Some guys think that var in C# is same thing as var in PHP. That's wrong - var keyword in C# is totally different thing and it doesn't mean that C# has now support for type changing class attributes. Let's prove it.
I will use very simple example code here but it makes the point perfectly clear later. So here's the code.
class Program
{
static void Main(string[] args)
{
string name = "Compilers";
var name2 = "Compilers";
}
}
Now let's see what happen if we compile this code. If var doesn't mean type inference and it is therefore something different then compiler doesn't produce two analogous code blocks for variables we declared. Compiler creates the following IL code.
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 14 (0xe)
.maxstack 1
.locals init ([0] string name,
[1] string name2)
IL_0000: nop
IL_0001: ldstr "Compiler"
IL_0006: stloc.0
IL_0007: ldstr "Compiler"
IL_000c: stloc.1
IL_000d: ret
} // end of method Program::Main
If we look at this code we will see that there are two local variables declared as string: name and name2. These are the same variabled we declared before. So their types are correct. If we look at the code where assignments are made we can see that these assignments are very similiar, so there is no additional activities made in the case of variable declared with keyword var.
If we try to compile something like this
void SomeMethod()
{
var x = null;
var y;
}
we will get errors because on first line we are using null and null has no type. Second line gives error because there is no type specified and therefore there is nothing to infere.
So, I think now you believe me if I say that kayword var in C# is not same thing as var in PHP :)
"What will happen behind compiler?" is my favourite questsion when dealing with technical design and coding. There are many cool and powerful things we can use to be more productive, faster and better. We have automatic properties, extension methods, partial classes, generics and so on. If we don't know what they are and how these things are handled by compiler we may face serious troubles later.
When I plan to use some new features of language, in my case mostly C#, I will read about those features and then I will check out what happens when I use them in my code. I try to find out what these features really are - are they something really new or are they something that doesn't exist after compiling. After that I can be sure how and in which context to use these features.
One of my favorite examples is n-tier architecture using partial classes. Suppose you have a project and you have to implement BLL and DAL. Some other guy is building presentation layer. So you sit down and write partial class - one part for BLL and the other for DAL. And this class works in context of one business object and it's operations. After two weeks you have many classes like this.
Now, guess what - your application doesn't have 3-tier architecture. Why? Because after compiling you have two times less classes than you can see in IDE. There is no such a thing as partial class behind compiler. If your BLL and DLL soup had Presentation layer same way with them then after compiling you will have legacy style fat client.
There are some very valuable tools you can use to check out the results of compiling. My favourite ones are ILDasm and Lutz Roeder's Reflector. If you are not sure what one or another feature is after compiling then use these utilities to find out what compiler built.
I strongly believe that knowing your language and knowing your compiler the best you can is one of the golden keys to your success as developer.
There may be situations when we need to use parent page properties from user control. I know, this situations is a warning sign - there's something wrong with UI structure if user controls should know their parents. So, how to get correct reference to parent page so we can use also custom properties defined there?
Let's suppose we have property called MyId in parent page of our user control and we want to call this property from user control.
If we use this code in our user control
protected void MyMethod()
{
Page myParent = this.Page;
...
// here we need to call MyID property
}
we will get just a Page - the base class for all Pages. This is because reference of Page is kept in base class of user controls. Whenever you create a user control it gets also the Page property. But every page we create is custom page that inherits the Page class and we may define our own custom properties.
If we have page called MyPage and it has property MyID then how can we refer to this ID? Solution is simple: we have to use casting.
After casting parent page to correct type we are able to use it's properties.
This is the first posting of Beginners section of this blog. I will put here code samples that are not advanced enough to put them elsewhere in this blog. First example shows how to filter one dropdown list based on value selected from another.
Let's create example web form and let's name it as DropDownFiltering.aspx. We will add two dropdown lists to it and name it as ddlMain and ddlSub. ddlMain is main dropdown that provides filter value to ddlSub. The code of form follows.
<%@ Page
Language="C#" AutoEventWireup="true"
CodeBehind="DropDownFiltering.aspx.cs"
Inherits="MyApplication1.DropDownFiltering" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>My Page</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Main selection:
<asp:DropDownList runat="server" ID="ddlMain" DataTextField="Title"
DataValueField="Id" AutoPostBack="true"
onselectedindexchanged="ddlMain_SelectedIndexChanged">
</asp:DropDownList>
Sub selection
<asp:DropDownList runat="server" ID="ddlSub" DataTextField="Title"
DataValueField="Id">
</asp:DropDownList>
</div>
</form>
</body>
</html>
As you can see I defined OnSelectedIndexChanged event for ddlMain, also it has AutoPostBacks turned on. If user selects some value from ddlMain then web page will be sent to server and OnSelectedIndexChanged event will be fired.
Let's see now the code behind this page. There are two methods for binding dropdown lists with data. These methods are separate and not part of some events because we need to call them in more than one place in our code. When Page is loaded we will check if it is post back. If it is not post back then there is no data in dropdown lists and we have to bind them.
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
namespace MyApplication1
{
public partial class DropDownFiltering : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
return;
BindMain();
BindSub();
}
protected void BindMain()
{
ddlMain.DataSource = GetMainData();
ddlMain.DataBind();
}
protected void BindSub()
{
DataTable table = GetSubData();
table.DefaultView.RowFilter = "ForeignID=" + ddlMain.SelectedValue;
ddlSub.DataSource = table;
ddlSub.DataBind();
}
protected DataTable GetMainData()
{
DataTable table = new DataTable();
table.Columns.Add("ID");
table.Columns.Add("Title");
DataRow dr;
dr = table.NewRow();
dr["ID"] = 1;
dr["Title"] = "First";
table.Rows.Add(dr);
dr = table.NewRow();
dr["ID"] = 2;
dr["Title"] = "Second";
table.Rows.Add(dr);
dr = table.NewRow();
dr["ID"] = 3;
dr["Title"] = "Third";
table.Rows.Add(dr);
return table;
}
protected DataTable GetSubData()
{
DataTable table = new DataTable();
table.Columns.Add("ID");
table.Columns.Add("ForeignID");
table.Columns.Add("Title");
DataRow dr;
dr = table.NewRow();
dr["ID"] = 1;
dr["ForeignID"] = 1;
dr["Title"] = "1:First";
table.Rows.Add(dr);
dr = table.NewRow();
dr["ID"] = 2;
dr["ForeignID"] = 1;
dr["Title"] = "1:Second";
table.Rows.Add(dr);
dr = table.NewRow();
dr["ID"] = 3;
dr["ForeignID"] = 2;
dr["Title"] = "2:First";
table.Rows.Add(dr);
dr = table.NewRow();
dr["ID"] = 4;
dr["ForeignID"] = 2;
dr["Title"] = "2:Second";
table.Rows.Add(dr);
return table;
}
protected void ddlMain_SelectedIndexChanged(object sender, EventArgs e)
{
BindSub();
}
}
}
The last thing is SelectedIndexChanged event handler that is attached to ddlMain. This event will be fired when the value of ddlMain is changed on client side. When ddlMain has changed then ddlSub is binded again to show the values that correspond to ddlMain value.
Methods GetData() and GetSubData() are for example purposes only and in real applications you should replace them with your own methods that provide real data to dropdowns.
Once I wrote a class to make paging calculations. I had some data bound user controls that had no paging support. So I had to improvise. As it is was pointless to duplicate pager code to every user control where I needed paging I wrote a class to make my life easier.
/// <summary>
/// Class for data pager calculations.
/// </summary>
public class Pager
{
/// <summary>
/// Number of current page.
/// </summary>
public int CurrentPage = 1;
/// <summary>
/// Number of previous page.
/// </summary>
public int PreviousPage = 1;
/// <summary>
/// Number of next page.
/// </summary>
public int NextPage = 1;
/// <summary>
/// Count of pages.
/// </summary>
public int PageCount = 1;
/// <summary>
/// Page's first row index in collection.
/// </summary>
public int StartRow = 0;
/// <summary>
/// Pages last row index in collection plus one.
/// It is meant to use in for loop.
/// </summary>
public int StopBeforeRow = 0;
/// <summary>
/// Returns pager object with values based on given parameters.
/// </summary>
/// <param name="pageNo">Number of current page.</param>
/// <param name="pageSize">Page size.</param>
/// <param name="collectionSize">Size of rows or items collection.</param>
/// <returns></returns>
public static Pager GetPager(int pageNo, int pageSize, int collectionSize)
{
Pager pg = new Pager();
pg.CurrentPage = pageNo;
pg.PageCount = (int)Math.Ceiling((double)collectionSize / pageSize);
if (pg.CurrentPage > pg.PageCount)
pg.CurrentPage = pg.PageCount;
if(pageNo > 1)
pg.PreviousPage = pg.CurrentPage-1;
else
pg.PreviousPage = 1;
if(pg.CurrentPage>=pg.PageCount)
pg.NextPage = pg.PageCount;
else
pg.NextPage = pg.CurrentPage+1;
pg.StartRow = (pg.CurrentPage-1)*pageSize;
pg.StopBeforeRow = pg.CurrentPage*pageSize;
if(pg.StopBeforeRow > collectionSize)
pg.StopBeforeRow = collectionSize;
return pg;
}
}
As you can see this class takes also care of inconsistent parameters and handles them so your code doesn't stop working.
Getting distinct values from arrays is not a unique problem. Here will I show some options how to do it. We are will use array of integer in examples here. This blog entry shows you somehow the mighty evolution of .Net Framework.
Let's say we have array of integers like this:
We can see that this array contains duplicate elements. Let's try now to get distinct values of out if. The result we expect is the following array:
1. .NET Framework 1.0/1.1
The first method we will see works okay on all .Net Framework versions. Also the older versions are supported.
public int[] GetDistinctValues(int[] array)
{
ArrayList list = new ArrayList();
for (int i = 0; i < array.Length; i++)
{
if (list.Contains(array[i]))
continue;
list.Add(array[i]);
}
return (int[])list.ToArray(typeof(int));
}
This method works on all versions of .NET Framework. It has some bad side effects on large arrays. Because we are using ArrayList that is built to hold objects it makes some bad overhead when using value types with it. Value types will be boxed when they are assigned to objects. When casting back from objects to value types then unboxing happens.
2. .NET Framework 2.0
.NET Framework 2.0 helps us to avoid overhead of boxing and unboxing because we can use generics. The following method is more powerful because it has no side effects of boxing and unboxing.
public int[] GetDistinctValues(int[] array)
{
List<int> list = new List<int>();
for (int i = 0; i < array.Length; i++)
{
if (list.Contains(array[i]))
continue;
list.Add(array[i]);
}
return list.ToArray();
}
This method doesn't use all the power of generic. So let's make this method usable on all types we want to use.
public T[] GetDistinctValues<T>(T[] array)
{
List<T> tmp = new List<T>();
for (int i = 0; i < array.Length; i++)
{
if (tmp.Contains(array[i]))
continue;
tmp.Add(array[i]);
}
return tmp.ToArray();
}
Now you can use this method also with strings, doubles and objects. Example with array shown in the beginning of this blog entry:
nrs = GetDistinctValues<int>(nrs);
Now we have used all the power of .Net Framework 2.0 and as we can see we made our method very general.
3. .NET Framework 3.5
The last thing is the shortest one and wyou don't have to expect here more than one line of code.
nrs = nrs.Distinct().ToArray();
This is possible due to extension methods for arrays and lists that .NET Framework 3.5 offers to us. So when you are using Visual Studio 2008 you can solve problems like this easily.
SharePoint list items have a tricky way how to assign values to DateTime fields in your code. It is possible to assign value of DateTime type to this field but it also possible to assign a string. This string is a little bit mysterious. You may get errors with one dates but some dates will work perfect. So what's the trick?
If you want to assign date as a string to SharePoint's DateTime type field then you must format this string as an invariant date. The following line of code explains better what I mean.
item["MyDate"] = DateTime.Now.ToString(DateTimeFormatInfo.InvariantInfo);
If you guarantee that dates are in invariant format then you have no problems when assigning values to DateTime fields. Remember that invariant format is:
MM/dd/yyyy HH:mm:ss
ASP.NET Futures introduced support for search engine sitemaps. This is content detection protocol introduced originally by Google and later accepted also by Microsoft and Yahoo! ASP.NET makes sitemaps generating easy for us and as a bonus their model is perfect for more complex web applications like e-commerce and e-services sites. Let's see now what we can do.
Couple of word about search sitemaps
If you have small site with low number of links then managing sitemaps manually is not a problem. But if your site has many links and content that changes often you may want to automate the task of generating sitemap.
Sitemap is important thing for each site because it tells to search engine spiders where to find information from your site. If you give this information there is something you will get back - Google gives you very rich set of information about your site through their Webmasters service.
ASP.NET 2.0 introduced us the new concept - sitemaps. Besides new site navigation definition format we got also providers that were used by navigation controls. ASP.NET search sitemaps technology extends this model and makes our site navigation understandable for search engine spiders.
ASP.NET Search Sitemaps
Let's take a closer look at search sitemaps now. Suppose we have a web application that uses search sitemaps. As a first thing we will look at sitemap file that defines static navigation of our application.
<?xml version="1.0" encoding="utf-8" ?>
<sitemap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0">
<sitemapnode url="/" title="Home" description="Home page">
<sitemapnode url="about-us.aspx" title="About us" description="">
<sitemapnode url="corporate.aspx" title="Corporate information" description="" />
<sitemapnode url="values.aspx" title="Corporate values" description="" />
</sitemapnode>
</sitemapnode>
</sitemap>
Now let's see section from application's web.config where dynamic part of site's navigation is defined.
<searchSiteMap enabled="true">
<providers>
<add name="Navigation"type="Microsoft.Web.Preview.Search.AspNetSiteMapSearchSiteMapProvider, Microsoft.Web.Preview"/>
<add name="Product"
type="ASPNETFuturesEnabledWebApplication1.MySitemapProvider, ASPNETFuturesEnabledWebApplication1"
targetUrl="Products.aspx"
targetUrlseparator="?"
pathInfoFormat="false"
queryStringDataFields="ProductId"
queryStringDataFormatString="ProductId={0}"
/>
</providers>
</searchSiteMap>
As we can see there are two providers given that provide the sitemap system with dynamic data about navigation. First provider is the default one that generates search sitemap based on site's static navigation.
The second provider was added by me. This provider returns information about my products section navigation. When asked, it will return all links that products section contains. The code is as follows.
public class ProductEntry
{
public string ProductId;
public string ProductName; public String SiteMapLastModified;
public String SiteMapChangeFrequency;
public String SiteMapPriority;
}
public class ProductSitemapProvider : DynamicDataSearchSiteMapProvider
{
public override IEnumerable DataQuery()
{
List<ProductEntry> products = new List<ProductEntry>();
ProductEntry entry;
foreach (Product product in DAL.GetProducts())
{
entry = new ProductEntry();
entry.ProductId = product.Id.ToString();
entry.ProductName = product.Name;
entry.SiteMapLastModified = product.Modified.ToString("yyyy-MM-ddThh:mm:ss.fffZ");
products.Add(entry);
}
return products;
}
}
The class called ProductSitemapEntry defines search sitemap entry that is used with products. Second class, ProductSitemapProvider, is the one used by search sitemaps engine to get information about products. You can handle ProductSitemapEntry as one of the DTO classes for Products.
Search sitemaps structure
Search sitemaps protocol allows also sitemaps hierarchy to be defined. It is specially useful for large sites because we can say on higher level sitemaps which subsitemaps are changed meanwhile. This way we can lower our spidering traffic also.
Search sitemaps HttpHandler that we can find from web.config generates hierarchical search sitemaps. Let's see what happens when we make request to this handler.
<?xml version="1.0" encoding="utf-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>http://localhost:49781/SearchSiteMaps.axd?sitemap=Navigation</loc>
<lastmod>2008-04-20T01:58:41.694Z</lastmod>
</sitemap>
<sitemap>
<loc>http://localhost:49781/SearchSiteMaps.axd?sitemap=Product</loc>
<lastmod>2008-04-20T01:58:41.694Z</lastmod>
</sitemap>
</sitemapindex>
This is pure hierarchical search sitemap that refers to two subsitemaps. These subsitemaps are asked through same handler and name of search sitemap is given by query parameter. When you look at search sitemaps configuration section above you may notice that these query parameters have same values as search sitemaps providers names.
Let's see now what happens when we ask first sitemap. Remember that the first one corresponds to site's static navigation shown before. Just copy and paste URL from above XML to your browser.
<?xml version="1.0" encoding="utf-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://localhost:49781/</loc>
</url>
<url>
<loc>http://localhost:49781/about-us.aspx</loc>
</url>
<url>
<loc>http://localhost:49781/corporate.aspx</loc>
</url>
<url>
<loc>http://localhost:49781/values.aspx</loc>
</url>
</urlset>
Now let's see what is returned by products search sitemap provider.
<?xml version="1.0" encoding="utf-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://localhost:49781/Products.aspx?ProductId=100</loc>
<lastmod>2008-04-20T02:02:02.544Z</lastmod>
<priority>0.4</priority>
</url>
<url>
<loc>http://localhost:49781/Products.aspx?ProductId=102</loc>
<lastmod>2008-04-20T02:02:02.544Z</lastmod>
<priority>0.4</priority>
</url>
</urlset>
As we can see there are two products (oh, lazy me!) introduced to search engines spiders.
Conclusion
As we can see the search sitemaps technology is very powerful feature that makes our sites SEO much simpler than it was before. We can add as many search sitemaps providers to our application's configuration and let them generate search sitemaps. Besides static navigation we may have many different sections in our site. By example, we may have blog, product vatalog, support forum, FAQ pages and so on.
ASP.NET search sitemaps will provide us with common and flexible ways to make our applications support search sitemaps protocol.
More Posts
Next page »