Why would I need to do this?

You have a 3rd-party assembly (DLL) you want to take advantage of in your SharePoint project. You need to make sure that when your WSP is deployed, this 3rd-party assembly goes along for the ride. I'll demonstrate how I accomplish this, by using my SPMeta-Northwind project as an example.


Solution Folders

I got into the habit years ago of placing 3rd-party assemblies in a solution folder. This is a fine way of ensuring that the assemblies are included with your solution when a member of your team "gets latest" from your source control system of choice (TFS, Git, etc.). If you have multiple projects in your solution that need the 3rd-party assembly, they can all reference the one location in the solution folder.

The initial setup of solution folders can be a little tricky, so I'll show step by step how to do it (in Visual Studio 2012). The trick is to create a directory structure on your file system, and then mirror it with solution folders in Visual Studio.

In Windows Explorer, browse to the directory where the solution exists. We're going to manually create a directory called SolutionItems.


Drill into that directory and create another directory called Assemblies.


Since SPMeta-Northwind relies on Camlex.NET, I'm going to place the Camlex.NET.dll assembly here.


Right-click on the solution, and choose Add -> New Solution Folder.


Name the solution folder SolutionItems.


Right-click on SolutionItems and again choose Add -> New Solution Folder.


Name this solution folder Assemblies.


Right-click on Assemblies and choose Add -> Existing Item.


Browse to the Assemblies directory where we placed the Camlex.NET.dll assembly earlier, and add the file.



Package.package

The next step is to update the Package.package file in each of your projects that depend on the 3rd-party assembly. Since SPMeta-Northwind, at present, only has one project, we just have one package to update.

Expand the Package folder, and double-click on Package.package.

Switch to the Advanced tab, and click on the Add button to the right, and choose Add Existing Assembly.

In the Add Existing Assembly dialog, for the Source Path, browse to the location of the Camlex.NET.dll file. Click OK to close the dialog.


Now the Camlex.NET assembly is included in our package and will be deployed to the GAC when our WSP is deployed.



References

The last step is to add a reference to the assembly in each project that needs it.

Under the Northwind.Common project, right-click on References and choose Add Reference. In the dialog, click Browse..., locate the Camlex.NET.dll file in our Assemblies folder, and click Add.


Click OK to close the dialog.

Now the project has a reference to the assembly, and you're ready to go.



Closing

I've found in practice that this is a great way to manage 3rd-party assemblies. You ensure that every developer on your team will get the same file in the same place when they get latest from source control. You ensure that all the projects that reference the assembly have resilient relative paths that are guaranteed not to break across machines. And you ensure that when you build a WSP from your project, it will contain the assembly, and the assembly will be deployed to the GAC when the WSP is deployed.

Read More

Intro

Which URL is nicer? This one:

/_layouts/northwind/hr/Default.aspx

Or this one:

/hr/Default.aspx

If you're like me, you notice the friendliness of URLs in web applications. I picked up a sense of URL friendliness during my time in the Ruby on Rails community, where they have a predilection for short, semantic URLs.

I'm going to discuss a technique for organizing and deploying custom ASPX pages to your SharePoint environment that will leave you with nicer URLs.


Deploying Pages to /_layouts/

The first URL listed above indicates a page that was deployed to the "layouts" folder in the SharePoint "hive". I'm going to assume that all SharePoint developers are familiar with this process. If you put a page in the "layouts" folder, it's going to be addressed with a URL starting with /_layouts/.

If you want to be a good citizen of your SharePoint farm, you're going to make a subdirectory for the files related to your application to live to keep things tidy. Using my SPMeta-Northwind project as a reference, it would probably make sense to call this subdirectory something like "northwind".

Let's say that Northwind has some custom pages that are related to the Human Resources (HR) department of the company. Again, for purposes of tidiness, we'll group these pages together in a subdirectory called "hr".

So all of our HR-related pages will get a URL that starts with /_layouts/northwind/hr/.

We can do better!


Deploying Pages to a Document Library

Here's another way to deploy our HR-related pages that will result in nicer URLs. It involves a document library and a module.

The SPMeta-Northwind project has a "Lists" folder. Let's add a document library list instance to it called "HR Pages".


Under the Northwind.Common project, we'll add a module called "HRPages". Now let's add a few pages to the module. Note: If you try to add a page to the module by choosing the "Application Page" item, Visual Studio freaks out and adds files completely outside of the module. I always add a "Text file" and manually change the extension to ASPX. After adding our pages, there are some tweaks to make to the module's Elements.xml.


Let's also go ahead and take a preemptive measure to ensure that if we add new pages to the module in the future, they'll be pushed out during feature upgrading. Specifically, we need to reference our module inside an ApplyElementManifests tag.


After activating/upgrading our feature, I'll navigate to /hr in a browser. ASP.NET will look for a Default.aspx file, and since we deployed one, that's where we'll end up.


If you go with the document library + module approach for your custom pages, a nice bonus you get is that you can easily hook into SharePoint's item-level security to secure your pages. Since each page is now an item in a list, you can apply permissions to them just like any other list item in SharePoint. Specify which groups/users have read permissions on the list item, and SharePoint will give its standard "access denied" message to unauthorized users that try to navigate to the corresponding page.


Closing

I prefer the document library + module approach for deploying custom ASPX pages. It's a great way to get clean, short URLs. And you get SharePoint's built-in security enforcement for free!

Read More

Taming CAML



As a SharePoint developer, CAML is a fact of life. It can be a pain to deal with, but there are ways to make your life easier. I'm going to discuss a few ways of constructing CAML queries. Some are better than others.


Producing a String of CAML

There are many ways to produce a string of CAML that you can include in your codebase. You can write it by hand or use a tool to spit out a string of CAML that you can copy/paste.


Writing By Hand

I recommend against writing CAML by hand. As it's based on XML, which is not exactly the basis for a succinct query language, strings of CAML get long and unwieldy. I mean, this is gross, right? Who wants to write this by hand?

activeProductsByCategory.Query = "<GroupBy Collapse='FALSE' GroupLimit='30'><FieldRef Name='Category' /></GroupBy><OrderBy><FieldRef Name='Title' /></OrderBy><Where><Eq><FieldRef Name='Discontinued' /><Value Type='Boolean'>0</Value></Eq></Where>";

We can do better.


U2U CAML Query Builder

There are several free third-party CAML builder tools that allow you to interactively construct queries via a GUI designer. U2U's tool is probably the most well known as it's been around the longest.




Tools like these are certainly much better than hand-rolling CAML queries. I'll break out U2U CAML Query Builder once in a while for quick, simple scenarios, but there's a much more comprehensive interactive CAML builder that I turn to time and time again. And this one's made by Microsoft themselves.


SharePoint's Web Form

It's easy to forget that the vast majority of the things we do as SharePoint developers are also possible by end users via SharePoint's built-in web forms. There's no shame in prototyping your ideas via SharePoint's own screens, and then reproducing the prototype in code. I do this often.

Let's say we need a new view to be added to a list. As a developer, for repeatability's sake, we want to create this view via code in a feature. But we can lean on SharePoint's out-of-the-box view creation screen to write the CAML query for us.




Once we've designed the view, we can fire up SharePoint Manager (or the SharePoint Management Shell) and copy the CAML that resulted.




I think this is the best way to produce a string of CAML that you can paste into your code, as you have the full power of Microsoft's own view design interface.


No CAML


What if you tired of having unsightly blobs of CAML littered throughout your codebase? Outside of the SharePoint world, CAML is replaced by SQL. And for years people have avoided having blobs of SQL in their codebases by using tools that generate SQL dynamically. Why not use a tool that generates your CAML dynamically?


LINQ to SharePoint

I touched on LINQ to SharePoint in a previous post. Just as LINQ to SQL takes a "snapshot" of your database schema and generates .NET classes that map to it, LINQ to SharePoint takes a snapshot of the lists in a site and generates .NET classes against it.

LINQ to SharePoint was very exciting to me when I first heard about it, as I was familiar with tools like LINQ to SQL and how convenient they made querying for data from code. Unfortunately, that excitement faded as I tried to press LINQ to SharePoint into action in a real-world application.

I'll invite you to read Bjørn Furuknap's post (especially "The Ugly" section), as he does a pretty good job of running down the practical concerns of using LINQ to SharePoint. For projects I've worked on, the biggest problem is that LINQ to SharePoint is just too static for a dynamic environment like SharePoint. It's too easy for the generated code to go out of sync with the reality of a particular site. I've always worked on SharePoint projects in teams of multiple developers, and when we've tried to use LINQ to SharePoint, we ran into constant problems with the sites in our local development machines going out of sync with the LINQ to SharePoint model and causing blow-ups at runtime. This situation is compounded when you have different developers working on different SharePoint features that make list changes and that may or may not be active on any given site.

LINQ to SharePoint may be great for some scenarios, but from my own personal experience, it's never been a practical choice.


Camlex.NET

My current favorite is a fantastic open source project called Camlex.NET. This project hits a sweet spot for me in that it alleviates the need to have large CAML strings in my codebase, while still being dynamic.

Here I'm setting up the query for a view on the Products list from SPMeta-Northwind called "Active Products By Category":

activeProductsByCategory.Query = "<GroupBy Collapse='FALSE' GroupLimit='30'><FieldRef Name='Category' /></GroupBy><OrderBy><FieldRef Name='Title' /></OrderBy><Where><Eq><FieldRef Name='Discontinued' /><Value Type='Boolean'>0</Value></Eq></Where>";

And here I'm setting up the same query with Camlex.NET:

activeProductsByCategory.Query = Camlex.Query().Where(x => (bool)x["Discontinued"] == false).OrderBy(x => x["Title"]).GroupBy(x => x["Category"], false, 30).ToString();

Although, to my eye, the Camlex.NET version here is much nicer, this is a pretty basic example of its power. Imagine situations where you're dynamically building up a WHERE clause containing an arbitrary number of ANDs/ORs based on variable conditions. It's times like that when you're concatenating large strings of CAML and playing String.Format() bingo that you really see the power of a tool like Camlex.NET.

As a bonus, the authors of Camlex.NET have a website where you can paste in CAML strings and see how you'd build their equivalents with Camlex.NET. It's pretty handy when you're first learning the tool.




Closing

As a SharePoint developer, you're going to need CAML queries. You can either go the path of having strings of CAML in your codebase, or having a tool dynamically generate CAML.

If you don't mind having blobs of CAML intermingled with your C#/VB code, then I recommend using Microsoft's own add/edit view form to build your CAML.

If you're like me, and you prefer to have your CAML built dynamically, then I recommend the amazing Camlex.NET project, which I believe is a more practical choice over LINQ to SharePoint.
Read More

Intro


It's not necessarily obvious how to handle changes to a deployed SharePoint-based application. I'm going to lay out a proven system for upgradeable SharePoint sites that I've successfully used on multiple projects.

To illustrate some of my points, I'm going to reference an open source project I created called SPMeta-Northwind. It's a reimagining of Microsoft's classic Northwind database schema as a SharePoint farm solution. I developed SPMeta-Northwind for SharePoint 2010 in Visual Studio 2012. If you're using SharePoint 2013 or a different version of Visual Studio, I believe the general concepts will still apply.


Feature Upgrading


SharePoint 2010 introduced the concept of feature upgrading. This capability provides a great way forward for pushing changes out to existing SharePoint sites. My Northwind solution (pictured below) contains one upgradeable feature, called Northwind.Common.


Setting up the feature properties

There are a few settings we'll need to configure in the Properties window for the Northwind.Common feature.


We need to configure which assembly ('Upgrade Actions Receiver Assembly') and class ('Upgrade Actions Receiver Class') will be invoked when the feature is upgraded. We'll set it to use the same assembly and class that are used on feature activation.

Decide on a beginning version number for the feature. I tend to go with '1.0.0.1'.

Setting up the feature manifest

To make the feature upgrade-aware, we'll need to make a small edit to the manifest.


Add an 'UpgradeActions' element with a 'CustomUpgradeAction' element inside it. For our purposes, the 'Name' attribute really doesn't matter--I usually just use 'Upgrade'.

There is another edit you may want to make to the feature manifest if you're using modules, to ensure that new files added to the module get deployed on upgrade, but I'll cover that topic in a future post.

Deploying feature upgrades

My Northwind solution includes some PowerShell scripts for deploying upgrades. I can go over them in depth in a future post, but for the purposes of this post, I'll just point out a couple lines from FeatureUtils.ps1 directly related to feature upgrading.

if ($feature.Version.CompareTo($feature.Definition.Version) -lt 0)
{
    # ...snip...

    $exceptions = $feature.Upgrade($false)
 
    # ...snip...
}

In code, you can compare the version of an activated feature against the version of its installed feature definition to tell if it needs to be upgraded. If so, you can call SPFeature.Upgrade() to bring the feature up to date.


Idempotent Feature Receivers


Feature receivers represent a powerful hook for running code against your SharePoint environment. My philosophy around feature receivers includes the concept of idempotence.

Not relying on version numbers

When my journey with SharePoint 2010 and its upgradeable feature concept began, I quickly learned that maintaining different code paths for specific feature versions was unsustainable. Since features are aware of which specific version number they had at the time of their last upgrade, you could be tempted to write conditional logic in your feature receiver that checks that version number and takes divergent paths based on it. Not only do I discourage you from doing this sort of thing, I discourage you from distinguishing between feature activation and feature upgrading.

FeatureActivated and FeatureUpgrading identical

Let's take a look at the Northwind.Common feature receiver.

public class NorthwindCommonEventReceiver : SPFeatureReceiver
{
    // ...snip...

    public override void FeatureActivated(SPFeatureReceiverProperties properties)
    {
        SPSite site = properties.Feature.Parent as SPSite;

        Ensure(site.RootWeb);
    }

    public override void FeatureUpgrading(SPFeatureReceiverProperties properties, string upgradeActionName, IDictionary<string, string> parameters)
    {
        SPSite site = properties.Feature.Parent as SPSite;

        Ensure(site.RootWeb);
    }

    // ...snip...
}

The contents of FeatureActivated and FeatureUpgrading are identical. I design my feature receivers to not only ignore which version they're upgrading from, but to not even care whether they're upgrading or activating. The resulting state will be the same regardless. This approach requires thinking about changes to your SharePoint environment as being "ensured."


"Ensuring"


The term I use to indicate idempotence in my code is "ensure." I was inspired to use this term by the SharePoint API's own SPWeb.EnsureUser(). If you're not familiar with this method, it simply does what it needs to do internally to make sure that a user with a particular login name exists and returns the user. It doesn't matter if you're calling it for the first time or the fiftieth time--the result is the same.

IEnsurableList

As I discussed in my last post, one of the ways I conceive of SharePoint is as a relational database system. And one of the most common upgrades I find myself doing is making "schema" changes, i.e., structural changes to SharePoint lists. This includes adding fields and views to lists and changing their various properties.

My approach to "ensuring" lists and their later schema changes is by maintaining one .NET class per list. Microsoft's famous Northwind database schema has 13 tables, and my Northwind solution has 11 "list classes" as I call them (I was able to eliminate 2 tables via SharePoint's unique functionality).

Microsoft's Northwind database schema

One "list class" per SharePoint list

What all lists have in common is that they can be ensured (FeatureActivated & FeatureUpgrading) or torn down (FeatureDeactivating). I express this commonality via a C# interface called IEnsurableList.

namespace Northwind.Common.Lists.Base
{
    public interface IEnsurableList
    {
        void Ensure(SPWeb web);
        void TearDown(SPWeb web);
    }
}

All of my list classes implement this interface. This allows each list to be treated identically by my ListManager class.

ListManager

The ListManager takes a collection of IEnsurableLists and knows how to ensure them and tear them down in the proper order.

public class NorthwindCommonEventReceiver : SPFeatureReceiver
{
    private readonly ListManager listManager = new ListManager(new List<IEnsurableList>
    {
        new CustomerDemographics(),
        new Customers(),
        new Employees(),
        new Shippers(),
        new Orders(),
        new Suppliers(),
        new Categories(),
        new Products(),
        new Regions(),
        new Territories(),
        new OrderDetails()
    });

    // ...snip...
}

I like to declare a ListManager instance right at the top of my feature receiver, so that you can see clearly which lists the feature ensures and in what order.

SPFieldCollection.Ensure<T>()

The Northwind solution contains several extension methods that augment the SharePoint API with "ensure" behavior. I'll talk about the most important one, SPFieldCollection.Ensure<T>().

public static T Ensure<T>(this SPFieldCollection fields, string fieldDisplayName) where T : SPField
{
    if (typeof(T) == typeof(SPFieldLookup))
    {
        throw new ArgumentException("Lookups are not supported");
    }

    SPFieldType fieldType = SharePointHelper.GetFieldType<T>();

    if (fields.ContainsField(fieldDisplayName))
    {
        SPField field = fields.GetField(fieldDisplayName);

        if (field.Type == fieldType)
        {
            return field as T;
        }

        field.Delete();
    }

    fields.Add(fieldDisplayName, fieldType, false);

    return fields.GetField(fieldDisplayName) as T;
}

Ensuring fields is so common that I tried to distill the process into its tersest form, reducing noise in the code, and maintaining maximum readability. I'll get more into depth about the design decisions behind my various extension methods in a later post, but for now, I just want to show how the concept of "ensuring" applies to fields. Below is a snippet from Employees.cs, where I'm ensuring the fields for the Employees list.

private void EnsureFields(SPList list)
{
    SPFieldText lastName = list.Fields.Ensure<SPFieldText>("Last Name");
    lastName.Required = true;
    lastName.MaxLength = 20;
    lastName.Update();

    SPFieldText firstName = list.Fields.Ensure<SPFieldText>("First Name");
    firstName.Required = true;
    firstName.MaxLength = 10;
    firstName.Update();

    // ...snip...

    SPFieldDateTime birthDate = list.Fields.Ensure<SPFieldDateTime>("Birth Date");
    birthDate.DisplayFormat = SPDateTimeFieldFormatType.DateOnly;
    birthDate.Update();

    // ...snip...

    SPFieldUrl photo = list.Fields.Ensure<SPFieldUrl>("Photo");
    photo.DisplayFormat = SPUrlFieldFormatType.Image;
    photo.Update();

    list.Fields.Ensure<SPFieldMultiLineText>("Notes");

    // ...snip...

    SPFieldCalculated displayName = list.Fields.Ensure<SPFieldCalculated>("Display Name");
    displayName.Formula = "=[First Name]&\" \"&[Last Name]";
    displayName.Update();

    // ...snip...
}


Closing


I've tried to provide a clear way forward for upgradeable SharePoint sites. We've looked at how to set up a SharePoint feature to be upgradeable, how to deploy upgrades via a PowerShell script, how to write idempotent feature receivers, and the concept of "ensuring." 

The SPMeta-Northwind project is a working example of my approach, and if you're a "show me the code" kind of person (like I am), then I hope you'll find it helpful.

See you next time.
Read More
Every web application has a database, right? If you're developing an ASP.NET application, your first choice is probably SQL Server, Microsoft's relational database product.

What if you're building an application on top of SharePoint? If you come from an ASP.NET background, you might reflexively reach for SQL Server and go nuts creating your tables, columns, views, etc.

If you're starting fresh, with no existing database to integrate with, I suggest that you consider taking advantage of SharePoint's out-of-the-box capabilities to be your data store. There are some serious advantages to this approach that I'll get into later. You may not need (or want) to reach for SQL Server after all.


If You Squint Hard Enough


SharePoint can start to take on the look of a web-based SQL Server Management Studio if you cock your head at a certain angle. I'm going to reference the venerable Northwind database to illustrate the similarities between SharePoint and a relational database like SQL Server. 

The world famous Northwind database


Table ⇔ List

We need a Customers table? How about a Customers list?

The Customers table becomes the Customers list


Column ⇔ Column/Field

SharePoint lists have columns (called "fields" in the API) just like database tables have columns. The Customers table in the Northwind database has a ContactName column with a data type of nvarchar(30). Let's add a column to our Customers list. To fit the requirements, we'll specify that the column is a "Single line of text" and the maximum number of characters is 30.

Adding the Contact Name column to the Customers list

Limit the number of characters in the column just like an nvarchar column in SQL Server

SharePoint columns can accommodate any data you could throw at them. I'll discuss the different data types in more detail in a future post.

You often want a primary key column on your table that auto-increments, right? SharePoint automatically includes a column in every list called ID, that does exactly that. I'll add the ID column to my view (more on those in a minute).

SharePoint includes an auto-incrementing primary key column called ID on every list

The Customers table has a CompanyName column. I might want to put some constraints on it. It would probably make sense to require a value for that column, as what good is a Customers record without the customer's name? Just as SQL Server allows you to decide if a column accepts nulls or not, in SharePoint, we can set "Require that this column contains information" to "Yes".

You might also want to put a uniqueness constraint on the CompanyName column. In SharePoint, we have the "Enforce unique values" setting.

We might even want to specify a default value for the Country field, perhaps defaulting it to "USA" if our customers are primarily American companies. SQL Server has default constraints, and SharePoint has the "Default value" setting.

SharePoint columns can have constraints familiar to SQL Server users

For performance reasons, you can even index a column.

SharePoint columns can be indexed


Foreign Key ⇔ Lookup

The Northwind database has an Orders table with a foreign key to the Customers table because each order has a corresponding customer that placed the order.

SharePoint's answer to the foreign key is the lookup column. Since tables are like lists, we could create an Orders list and add a lookup column.

Adding a lookup column on the Orders list
Just as the Orders table has a foreign key to the Customers table, the Orders list has a lookup to the Customers list

You can even enforce referential integrity through cascade constraints. We could tell SharePoint that we want it to delete any orders that go with a customer we've just deleted.

You can set up cascading delete behavior, just like in SQL Server


View ⇔ View

SharePoint lists can have views, analogous to the views you'd define in SQL Server.

Adding a view to the Customers list

These views can have sorts, filters, group bys and other features you'd expect (even joins).


Trigger ⇔ Event Receiver

In SQL Server, you might want to attach a trigger to a database table to run some custom code when a record is being inserted, updated, or deleted.

SharePoint's answer to the trigger is the event receiver. You can run custom .NET code when certain events occur. For example, when an item is being added, updated, or deleted in a list.

Event receivers have so many uses that I plan to elaborate on them in a separate blog post.


SQL ⇔ CAML

When it's time to write some queries against your SQL Server database, you turn to its namesake, SQL. SharePoint has its own language for expressing queries called CAML.

Being a dialect of XML, CAML is quite verbose and can be unwieldy to write by hand. There are tools that can make CAML easier to manage, and I discuss them in my post Taming CAML.


SQL Server Management Studio ⇔ SharePoint Manager

When you want to inspect your SQL Server databases, you fire up SQL Server Management Studio and crack open the Object Explorer. It lays out the whole structure of your SQL Server installation and lets you view the data in your tables.

Browsing your tables in SQL Server Management Studio

There's an indispensable open source project called SharePoint Manager that accomplishes roughly the same objectives in the SharePoint world. SharePoint Manager provides a nice GUI for browsing the site collections in your SharePoint farm, and inspecting every list, column, view, event receiver, and every other SharePoint object that exists.

Browsing your lists in SharePoint Manager



Querying Data from Code


If you've been in the .NET world for long enough, or if you're working with a legacy code base, you may be familiar with using "raw" ADO.NET classes to query for data in your database. If you're working in a .NET code base of a more recent vintage, you're probably familiar with object-relational mappers, like LINQ to SQL, that provide a nice strongly-typed object model for your database interactions.


Raw ADO.NET ⇔ SPListItem, SPQuery, etc.

The SharePoint API contains classes for getting data out of lists in a weakly-typed fashion. If you're familiar with classes from ADO.NET like SqlCommand and SqlDataReader, then SharePoint API classes like SPQuery and SPListItem will seem very familiar.


LINQ to SQL ⇔ LINQ to SharePoint

Object-relational mappers like LINQ to SQL provide a much more natural, object-oriented interface to your data. LINQ to SQL includes a tool called SqlMetal that can be run against an existing SQL Server database and will generate C# or VB classes that represent the tables in your database.

LINQ to SharePoint has SPMetal. You point SPMetal at your SharePoint site and it generates C# or VB classes that represent the lists in that site. LINQ to SharePoint has some drawbacks to consider, which I discuss in my post Taming CAML.



What's in It for Me?


Sure, SharePoint can be the relational database for your application. But why should it be?


Scaffolding for Free

Speaking from my own experience, I can say that there's nothing more tedious than writing the CRUD forms that wrap a database table.

When Ruby on Rails came along, one of the things that blew me away was this feature it had called scaffolding. Once you had your database schema in place, Rails would automatically create working forms for you to create, read, update, and delete records in your database. This was revolutionary! I hated writing that stuff by hand! And it took up so much time.


If you're storing your data in SharePoint lists, you've got something that looks an awful lot like scaffolding. SharePoint automatically provides forms for shuffling around the data in your list. Once we defined our Customers list above, SharePoint magically gave us all the forms for manipulating our customer records. Sweet!

SharePoint automatically generates all the forms needed to perform CRUD actions on our Customer data

This auto-magic CRUD functionality in SharePoint is deep, and I plan to elaborate on it in another blog post.


Deep Security Integration

SharePoint has a deeply integrated security model. There's a built-in security system for applying permissions to your lists and even individual records in your lists. Once you have your permissions configured, SharePoint automatically trims its UI based on the particular logged in user, and even restricts actions performed by methods in its API.

SharePoint's screen for declaring the permissions on a list

For example, you could configure the security on your Customers list so that certain groups or users only have read permissions. In this case, SharePoint will automatically hide any of its UI elements that involve adding an item to the Customers list when these certain users were logged in. Even if you're running custom code that calls into SharePoint's API to add an item to the Customers list, it will pick up the currently logged in user's permissions and refuse to add the item.

SharePoint's security model is extremely deep and is woven into every aspect of the product. I plan to cover it in depth in a future blog post.


Easy Schema Changes

One of the biggest headaches that comes with depending on a custom database is the difficulty of pushing out schema changes.

Developer A needs to add a new column to a table. He fires up SQL Server Management Studio on his local machine and adds the column. Now what? Developers B, C, D, E, and F all need that column added on their machines, too. So Developer A might shoot an email to the other developers telling them what the column is called, its data type, whether it allows nulls, etc. and ask them to manually make the change on their machines. If he's a nice guy, he might even write a snippet of T-SQL they can run and send that over. 

But you also have a production server, a staging server, and a QA server. So now someone has to log into those machines and take the same manual steps to get that new column added. What if there are new tables or new views? This method of keeping your database schema up to date across machines quickly becomes a time-consuming and error-prone nightmare.

SharePoint has a comprehensive API for "schema" changes. Want to add a column to a list? Add a new list? Add a view

SharePoint features provide the opportunity to make your schema changes in code and have them automatically applied when your application is deployed or upgraded. This is another deep topic, and one I elaborate on in my post A Model for Upgradeable SharePoint Sites.



Closing

In my career, I've seen teams working on SharePoint projects reflexively reach for a custom SQL Server database when it was time to store some data. I hope that in this post I was able to show that SharePoint can be your database, and there are definite advantages to using it in this way.

As I've noted throughout, several of the topics require elaboration. When I cover them in future posts, I'll come back here and link them up as appropriate.

See you next time.

Read More

Welcome to SPMeta

SharePoint is a beast, isn't it? I remember my early days coming up to speed from my background with vanilla ASP.NET, and being so confused (and angry) in this new landscape I found myself in. In those days I wasn't only going against the grain, I didn't even realize there was a grain.


After spending several years of my life working day in and day out with SharePoint, starting from scratch, learning everything the hard way, and in the process architecting and building multiple products using SharePoint as an application platform, I've come to the point where I'd like to stop and write down some of what I've learned.

My goal with SPMeta is to discuss some of the conceptual underpinnings of SharePoint, and, if I'm lucky, shed some light on what can be a very complex and intimidating product.

I have a rough outline of topics I plan to discuss, and I'm eager to get started. See you next time!
Read More