Data via Entity Framework
Creating a Data Provider That Reads Data via Entity Framework
Let’s create a data provider that exposes data to C1 CMS from tables in a database (e.g. "Northwind") on Microsoft SQL Server via Entity Framework.
First of all, create a new project, add required assembly references:
- In Visual Studio, create a new Class Library project.
- Add a reference to
Composite.dll
andMicrosoft.Practices.EnterpriseLibrary.Common.dll
from your website, alsoSystem.Configuration.dll
. - Set the "Copy Local" property for these references to "False".
Now add an Entity Data Model to the project and select the tables you want to expose data from:
- Add an Entity Data Model to the project (Project | Add New Item | ADO.NET Entity Data Model) and follow the steps in the wizards.
- Choose Model Contents: Generate from database.
- Choose Your Data Connection: Specify the database server and the database (e.g. Northwind).
- If necessary, create a new connection.
- Also, make sure, you have selected the option "Save entity connection settings in App.config as"
- Chose Your Database Objects: Select the needed tables (for example,
Suppliers
andProducts
).
IData Interfaces
Next, for each table added to the project (Step 4 above):
- Add a type interface inherited from the
IData
interface (e.g.ISupplier
andIProduct
) - Add needed properties to represent actual columns in the database. (Properties that you would like to expose in C1 CMS could be a subset of the actual columns in the database, but the key column should be one of them.)
- Add the required attributes to the type interface and to its properties (see below).
- Add a class that implements
IDataId
to hold the value of the key column in the table (e.g.DataId
). The class can be shared between tables if the key column is the same type in all tables (see the sample code). - Add a base class (e.g.
SupplierBase
andProductBase
) inheriting fromEntityObject
and implementing the type interface from Step 1 (e.g.ISupplier
orIProduct
) that only handles theDataSourceId
property.
These are the attributes you should add to the type interfaces and their properties (Step 3 above):
Type attributes:
- KeyPropertyName
- ImmutableTypeId
- DataScope
- DataAncestorProvider
Property attributes:
- StoreFieldType
- ImmutableFieldId
Note: To be able to use exposed data in the CMS Console (e.g. in Visual Functions) as well as in the code (e.g. via DataConnection
), consider also using the attributes RelevantToUserType
set to UserType.Developer
as well as Title
and LabelPropertyName
set to user-friendly names.
(For more information on creating type interfaces in C1 CMS, please refer to Data Types Using C#.)
As to the base class for handling DataSourceId
(Step 5 above), it is convenient to add such a class that implements the table’s IData
interface. When or if the Entities classes are regenerated, it will be easy to add this functionality.
Note: This class should only implement the DataSourceId
property using the provider (see further below) to get the DataContext instance (see the sample code).
Entities Classes
When tables has been added to Entity Data Model file (.edmx
), Visual Studio generates Entities classes (.designer.cs
), one class for each added table.
So now do the following:
- Close the visual part of the LINQ to SQL file (
.edmx
). - Edit the Entities classes (
.designer.cs
). - Make each class generated for every table to implement the corresponding
IData
-based interface and inherit from the corresponding base class. For example:
public partial class Supplier : SupplierBase, ISupplier
Important: Please note that when you open the visual part of the Entity Data Model file (.edmx), Visual Studio may regenerate EntitiesClasses, so all your changes here are lost.
If you do open the visual part, make sure that these classes should implement the proper interfaces and inherit from the proper classes making changes as described above.
Data Provider
Next, create the DataProvider class:
- Create a custom data provider class (e.g.
NorthwindDataProvider
) and have it implement theIDataProvider
interface. - For each call to the data provider, create a new instance of the Entities classes, generated when you have added an Entity Data Model (e.g.
NorthwindEntities
). - Implement the
GetData<>()
andGetData<>(IDataId)
methods and use the Entities classes instance for that. This instance is also accessed from the base classes (see above: "IDate Interfaces", Step 5).
The provider should cast the Table<>
on the EntitiesClasses to IQueryable<IData>
and return (see the sample code).
Important: Create the Entities class for each call to the data provider. This would make simultaneous calls into the provider thread-safe. (Entity Framework contexts are not thread-safe).
This requires some mechanism for calling Dispose()
on the Entities class. You can implement it as follows:
private NorthwindEntities GetEntityContext() { NorthwindEntities context = new NorthwindEntities(); ThreadDataManager.GetCurrentNotNull().OnDispose += () => { context.Dispose(); }; return context; }
When implementing GetData()
, you can access entities like this:
IQueryable<IProduct> products = GetEntityContext().Products;
Registering the Custom Data Provider in C1 CMS
Once you have created your custom data provider:
- Compile the project and copy the project’s DLL to
/Bin
folder of your website. - Copy the connection string from the generated
App.config
in your project toWeb.config
of your website. For example:<configuration> <connectionStrings> <add name="NorthwindEntities" connectionString="metadata=res://*/Northwind.csdl|res://*/Northwind.ssdl|res://*/Northwind.msl;provider=System.Data.SqlClient;providerconnection string="Data Source=.\SQLEXPRESS;Initial Catalog=Northwind;Integrated Security=True;MultipleActiveResultSets=True"" providerName="System.Data.EntityClient" /> </connectionStrings> </configuration>
- Edit the
/App_Data/Composite/Composite.config
file and locate the element:Composite.Data.Plugins.DataProviderConfiguration/DataProviderPlugins
- Add the configuration element below (just before its end tag
</DataProviderPlugins>
) specifying its type and name. For example:<add type="C1NorthwindIntegration.NorthwindDataProvider, C1NorthwindIntegration" name="NorthwindDataProvider" />
- Log in to the CMS Administrative Console and then execute Tools | Restart Server.
(You can use the exposed data types in console applications via XML tree definition files. If so, create a tree definition file and copy it to /App_Data/Composite/TreeDefinitions
. Please see the sample code for an example.)