среда, 15 февраля 2012 г.

REST with WCF and Entity Framework with JSON Serialization


Problem: you have some database and use Entity Framework to get data from it. You need develop web-service which returns JSON-data based on your entities. And as a consumer of this service you need to use javascript client. Also, you should provide cross-domain access to your web-service.

First, lets define configuration for this service:
<bindings>
  <webHttpBinding>
    <binding name="crossDomain" crossDomainScriptAccessEnabled="true" />
  </webHttpBinding>
</bindings>
<services>
  <service name="[Name of your service]">
    <endpoint address="" behaviorConfiguration="restBehavior" binding="webHttpBinding" bindingConfiguration="crossDomain" contract="[Name of your contract]">
      <identity>
        <dns value="localhost" />
      </identity>
    </endpoint>
  </service>
</services>
<behaviors>
  <endpointBehaviors>
    <behavior name="restBehavior">
      <webHttp />
    </behavior>
  </endpointBehaviors>
</behaviors>

Parameter crossDomainScriptAccessEnabled="true" allows requests from another domain. If you need to use SSL, just add security tag to the binding tag with mode="Transport":
<binding name="crossDomain" crossDomainScriptAccessEnabled="true">
  <security mode="Transport" />
</binding>

Okey, now we should define service contract. To get JSON-data from your web-methods you should add WebGet(ResponseFormat = WebMessageFormat.Json)  attribute to each web-method:
[ServiceContract]
public interface IService1
{
    [
OperationContract]
    [
WebGet(ResponseFormat = WebMessageFormat.Json)]
    
string GetOrder(Int32 id);
}

Now, we can request our service something like this, I use jquery:
$.getJSON('/GetOrder?id=7&callback=?'function (data) {
...
});

Great, but when we try to return entity we get this error:
The type 'xxx' cannot be serialized to JSON because its IsReference setting is 'True'.

Entity Framework doesn't support JSON-serialization, so I found this workaround:
public string GetOrder(Int32 id)
{
    
// getting order…
    return SerializeJSON(order);
}

static string SerializeJSON<T>(T obj)
{
    JavaScriptSerializer serializer = 
new JavaScriptSerializer();
    
return serializer.Serialize(obj);
}

вторник, 14 февраля 2012 г.

Sharepoint 2010 Forms Templates Sharepoint 2010 Forms Templates


In this article I'll tell you how to change standard templates for Edit, Display and New Forms for List Items and Documents in Sharepoint 2010. You can make it with Sharepoint Designer or InfoPath for partucular lists. This is described here:

But what if you need to change all forms of templates in the farms with your custom form through wsp-package? While I was investigating this question I found many suggestions to change file 14\TEMPLATE\CONTROLTEMPLATES\DefaultTemplates.ascx:

This template is used for default list edit, display and to create form of list items:
<SharePoint:RenderingTemplate id="ListForm" runat="server">
  <Template>
    …
  </Template>
</SharePoint:RenderingTemplate>
 
And this one is used for documents:
<SharePoint:RenderingTemplate id="DocumentLibraryForm" runat="server">
  <Template>
    …
  </Template>
</SharePoint:RenderingTemplate>

Yes, if you change these templates and make iisreset you'll get what you want — a new template for all forms.  So, what if we don't want to change the system files? Okey, let's look at the control, that manages templates in CONTROLTEMPLATES folder: SPControlTemplateManager. Method GetTemplateByName(String) of this class returns the template of RenderingTemplate control by its ID. All templates are loaded to the static HASH-table after the first call of this method. That is why we should make iisreset after changing DefaultTemplates.ascx file. So let's see how it works:

private static Hashtable GetTemplateCollection()
{
    if ((s_templateTable == null) && (HttpContext.Current != null))
    {
        lock (InternalSyncObject)
        {
            if (s_templateTable == null)
            {
                Hashtable templateTable = new Hashtable();
                FileInfo[] files = new DirectoryInfo(HttpContext.Current.Server.MapPath(systemTemplateLocation)).GetFiles("*.ascx");
                string controlTemplateFile = systemTemplateLocation + defaultTemplateFile;
                string str3 = systemTemplateLocation + mobileDefaultTemplateFile;
                LoadControlTemplate(templateTable, controlTemplateFile);
                LoadControlTemplate(templateTable, str3);
                foreach (FileInfo info2 in files)
                {
                    if (!(info2.Name == defaultTemplateFile) && !(info2.Name == mobileDefaultTemplateFile))
                    {
                        controlTemplateFile = systemTemplateLocation + info2.Name;
                        LoadControlTemplate(templateTable, controlTemplateFile);
                    }
                }
                s_templateTable = templateTable;
            }
        }
    }
    return s_templateTable;
}

Variables values:
systemTemplateLocation = "/_controltemplates/";
defaultTemplateFile = "DefaultTemplates.ascx";

As you see, first, this control loads all templates from DefaultTemplates.ascx file. Then it gets all files form CONTROLTEMPLATES directory and loads all templates from them. So if it finds templates with the same name as defined in DefaultTemplates.ascx it replaces them. Okey, let’s check it. Create a new file in CONTROLTEMPLATE directory and add a template to it:
<SharePoint:RenderingTemplate id="ListForm" runat="server">
  <Template>
    <h1>Custom form!</h1>
  </Template>
</SharePoint:RenderingTemplate>

Make iisreset, navigate  some list and try to create a new item. You'll see our title instead of form. Okey, it  works perfectly for standard templates, but what if we want to change the template for only one Web Application or SiteCollection or change the template for DocumentSet display form, which stores its template in file DocSetTemplates.ascx. Let's look at ListFormWebPart. This class has property TemplateName which is used to get a template name by current context (Edit, Create or Display):
public string TemplateName
{
    get
    {
        if (this.templateName == null)
        {
            this.EnsureListAndForm();
            if (this.ItemContext != null)
            {
                SPContentType contentType = this.ItemContext.ContentType;
                if (contentType != null)
                {
                    switch (this.pageType)
                    {
                        case PAGETYPE.PAGE_DISPLAYFORM:
                            this.templateName = contentType.DisplayFormTemplateName;
                            break;

                        case PAGETYPE.PAGE_EDITFORM:
                            this.templateName = contentType.EditFormTemplateName;
                            break;

                        case PAGETYPE.PAGE_NEWFORM:
                            this.templateName = contentType.NewFormTemplateName;
                            break;
                    }
                    if ((this.templateName != null) && (this.templateName.Length > 0))
                    {
                        return this.templateName;
                    }
                }
            }
            if (this.form != null)
            {
                this.templateName = this.form.TemplateName;
            }
            else
            {
                this.templateName = "ListForm";
            }
        }
        return this.templateName;
    }
    set
    {
        this.templateName = value;
    }
}

To get the name of the templates it uses properties of ContentType: DisplayFormTemplateName, EditFormTemplateName, NewFormTemplateName. So, we can create a new template in CONTROLTEMPLATES directory and set its name into all content types we need with powershell, for example. If we remove our template nothing happens, because if the template is not found it uses a default ListForm template.