Adding custom fields to almost any type programatically

July 30, 2014 Digital Experience

Custom fields and custom types are one of the most powerful concepts in Sitefinity as they allow you to easily build very sophisticated content or application scenarios and utilize the entire infrastructure and editing experience. As developers and web administrators you get to decide on the structure of the main types in Sitefinity directly through the user interface - this includes all content types, user profiles, pages and ecommerce product types. In this post I provide an overview of how this works, how to work with it programatically and provide a little tool you can use to add fields to almost any type. 

A quick word on how Sitefinity Back-end Works with custom fields

The magic behind it is based on the metadata model that Sitefinity provides - the database layer supports schema changing APIs on top of which Sitefinity provides metadata managers and this allows you to effectively insert columns into the structure of the Database and then OpenAccess takes care of the abstraction of specifics - things like which database are you working with or data caching for example. 

Then the entire back-end and the majority of the out of the box widgets are built on a very dynamic concept called definitions. On the data layer a field name maps to a column, on the UI level configuration defines the mapping between field name, interface for inputting field value(the back-end edit portion) and interface for viewing the value(typically what gets displayed in the front end). This is why when you add a custom field through the interface for adding custom fields the UI immediately shows up - in it's logic it simply gets a collection of all so called field controls(the interfaces for input and reading the value) and then it renders them all. 

Behind the scenes adding a custom field for anything through the UI does a few things - adds a meta field to the type, creates a definition mapping between the field name and the type of the field control and sets some properties like is the field required. Then what the framework does once you hit save or publish is that the javascript takes all the values that are inputted in the field controls and it sends them over as json to the service. Once you get the hang of this you get to do all sorts of magic with Field Controls and custom fields and you can create fully custom back-end interfaces in Sitefinity that simply work with the infrastructure.

For a quick overview of the power of a structured interface you can check out this blog post


Adding the field

In this post I will focus on the part of programatically adding metadata and show you a quick trick that I like to use. For more information on field controls you can review the documentation. Most types in Sitefinity will support custom fields, even if they don't have an interface for it. This includes product attributes, product variations, orders, forums, forum threads, forum posts, subscribers, taxons and campaigns. To check if a type supports custom fields you can see in JustDecompile or in the online API reference if it implements Telerik.Sitefinity.Model.IDynamicFieldsContainer. The details of how these fields are created are covered very well in the documentation. This is usually what it takes:

App.WorkWith()
       .DynamicData()
       .Type(typeof(NewsItem))
       .Field()
            .TryCreateNew("ResearcherName",typeof(string))
            .SaveChanges(true);

 

Where of course the type you are adding to (Order,  Subscriber etc.) or the type of the field(Guid, ShortText, LString(that is localizable string), Int32 etc.) are types you can specify.

Usually you create fields only once, and then they safely reside in the database. It is not a bad plan do check if the field was already created.

On the question of when to add the custom field, since it is just once any hook will do including:

  • Adding it in global.asax but always checking if it was added - this is better because if you happen to deploy the code base to a different database, it will take care of the structural change. 
  • Adding it just once through some kind of custom interface(i use an aspx page) - this is also acceptable, however you need to re-add the field if you have another staging database or something like that.

Changing the value of a meta field is very easy. It uses the DataExtensions.SetValue and DataExtensions.GetValue methods and  you can cast the type you are passing to IDynamicFieldsContainer. 

 

DataExtensions.SetValue((IDynamicFieldsContainer)subscriber,"Company""Telerik");

 

Your Cheat Sheet

Something that I like to use is a universal page for adding custom fields. In this page I have simply data bound drop downs for each type in Sitefinity and each type of fields and you can use it to:

1) Make a type a Meta type. If the type is not yet a Meta type then you wouldn't be able to add meta fields to it. The first button in the page does that for any type

2) Add a meta field to this type - simply provide a name and a type

3) Test the changing of the fields

You can easily add more types to the drop downs directly through the code of the sample and changing the CommonCLRTypes and CommonSitefinityTypes lists. Simply get the code, paste the Pages folder in the root of your application, build, and navigate to /Pages/createcustomfields.aspx. 

Downlaod From Github

Have in mind that this is just a sample and it doesn't support all types and hasn't been tested for all scenarios. It is also mostly intended for trying out things, placing the logic in global asax or in a static method invoked on application start would be a more elegant configuration, simply because actions done with the page affect the database but leave no record in source control. 

If you add support for more scenarios, pull requests are always welcome. 

For a real world application of adding fields programatically check out this tutorial on adding custom gift messages to orders in the Ecommerce checkout. 

 

App.WorkWith()
       .DynamicData()
       .Type(typeof(NewsItem))
       .Field()
            .TryCreateNew("ResearcherName", typeof(string))
            .SaveChanges(true);

Where of course the type you are adding to (Order, Subscriber etc.) or the type of the field(Guid, ShortText, LString(that is localizable string), Int32 etc.) are types you can specify.

Usually you create fields only once, and then they safely reside in the database. It is not a bad plan do check if the field was already created.

On the question of when to add the custom field, since it is just once any hook will do including:

  • Adding it in global.asax or an application start method, but always checking if it was added - this is better because if you happen to deploy the code base to a different database, it will take care of the structural change. 
  • Adding it just once through some kind of custom interface(i use an aspx page) - this is also acceptable, however you need to re-add the field if you deploy your code to a different project. 

Changing the value of a meta field is very easy. It uses the DataExtensions.SetValue and DataExtensions.GetValue methods and  you can cast the type you are passing to IDynamicFieldsContainer. 

DataExtensions.SetValue((IDynamicFieldsContainer)subscriber, "Company", "Telerik");

Your Cheat Sheet

Something that I like to use as a tool to test out custom fields is an an aspx page for adding custom fields. In this page I have simply data bound drop downs for each type in Sitefinity and each type of fields and you can use it to:

1) Make a type a Meta type. If the type is not yet a Meta type then you wouldn't be able to add meta fields to it. The first button in the page does that for any type

2) Add a meta field to this type - simply provide a name and a type

3) Test the changing of the fields

You can easily add more types to the drop downs directly through the code of the sample and changing the CommonCLRTypes and CommonSitefinityTypes lists. 

Downlaod From Github

Have in mind that this is just a sample and it doesn't support all types and hasn't been tested for all scenarios. If you add support for more scenarios, pull requests are always welcome. Additionally adding fields through a page is good for testing some things, but for production scenarios I would certainly recommend using a hook in Global Asax or an Application Start triggered static method in your projects, as this page makes schema changes directly and if someone else needs to deploy your code to another application, they would have to replicate the entire action with the page, and this is not a good process to support. 

For a real world application of adding fields programmatically check out this tutorial on adding custom gift messages to orders in the Ecommerce checkout. 

Happy Coding!

Svetla Yankova