Code Only Scenario
This article will guide you in the process of creating your first Data Access model and will introduce you to the Fluent Mapping API. While executing the steps in it, you will integrate the Telerik.DataAccess.Fluent and Telerik.DataAccess.Core NuGet packages in a class library, you will manually implement the fluent model, and you will deploy a database based on the model on the database server.
- Creating A Class Library For The Model
- Implementing The Fluent Model
- Deploy The Database To The Server
Preparing A Class Library For The Model
To prepare a class library for your fluent model:
- Select File > New Project in Visual Studio.
- In the list of Installed Templates on the left side of the dialog, select Visual C# or Visual Basic.
-
Then select Class Library. Name the project SampleFluentModel, and then click OK.
-
The template will create an empty class library project. To start coding your Data Access model you need to integrate the Telerik.DataAccess.Fluent and Telerik.DataAccess.Core NuGet packages. Therefore, run NuGet Package Manager from the TOOLS menu in Visual Studio (TOOLS > NuGet Package Manager > Manage NuGet Packages For Solution...) and search for Telerik.DataAccess.
Select Telerik.DataAccess.Fluent and click Install. The package depends on Telerik.DataAccess.Core, so it will be installed as well.
-
The installation process requires you to accept the license agreement of the packages. When the License Acceptance window pops up, select I Accept.
NuGet Package Manager will download and integrate the packages in the SampleFluentModel project.
What Just Happened?
When you install the Telerik Data Access NuGet packages in your project, the manager:
- Downloads the Telerik Data Access Enhancer tool, the OpenAccessNuGet.targets file, and the dependent Data Access assemblies.
- Modifies the SampleFluentModel project file, in order to import a post-build task which calls Enhancer.
- Adds references in the SampleFluentModel project to the Data Access assemblies listed below:
- Telerik.OpenAccess
- Telerik.OpenAccess.35.Extensions
- Telerik.OpenAccess.40.Extensions
- Telerik.OpenAccess.Runtime
- Includes the OpenAccessNuGet.targets file in the SampleFluentModel project.
Implementing The Fluent Model
The fluent model consists of three logical parts: persistent classes, a metadatasource class, and a context class. The main responsibility of the persistent classes is to serve as a shape for the database objects you retrieve, insert or modify. In your code they should look like POCO classes with no knowledge about Telerik Data Access at all. However, you need to let Telerik Data Access be aware of them. This happens in the context class. Basically, the context should expose a public property of type IQueryable<T> (where T is a persistent class) for each of your persistent classes and should inherit from OpenAccessContext in order to provide access to the Data Access runtime features. The persistent classes should have public properties, which correspond to the columns in the database tables. This mapping information is declared in the metadatasource class through the Fluent Mapping API. It should describe details like:
- Which persistent class corresponds to a given table?
- Which class property corresponds to a given table column?
- Which class property corresponds to the primary key column of the table?
- Which SQL type corresponds to the type of the class property?
Knowing this, you are ready to add the code for your first fluent model.
Create A Persistent Class
Here, you are going to create a simple POCO class, called Customer.
- Expand Solution Explorer in Visual Studio.
-
Add a new class named Customer in the SampleFluentModel project and paste in the code below. This class will be mapped to a database table by Telerik Data Access.
public class Customer { public int ID { get; set; } public string Name { get; set; } public DateTime DateCreated { get; set; } public string EmailAddress { get; set; } }
Public Class Customer Private _id As Integer Public Property Id() As Integer Get Return _id End Get Set(ByVal value As Integer) _id = value End Set End Property Private _name As String Public Property Name() As String Get Return _name End Get Set(ByVal value As String) _name = value End Set End Property Private _dateCreated As Date Public Property DateCreated() As Date Get Return _dateCreated End Get Set(ByVal value As Date) _dateCreated = value End Set End Property Private _emailAddress As String Public Property EmailAddress() As String Get Return _emailAddress End Get Set(ByVal value As String) _emailAddress = value End Set End Property End Class
Create Mapping For The Class
At this point, you are going to implement the metadatasource class of the model. It will contain the mapping information for the Customer class. The implementation in this example will be provided through the Default Mapping functionality of Telerik Data Access.
The metadatasource class has to derive from the abstract Telerik.OpenAccess.Metadata.Fluent.FluentMetadataSource class. Your implementation will override the abstract PrepareMapping method that serves as an entry point for working with the Fluent Mapping API. Later on this method will be called when you create instances of the context and the mapping needs to be obtained.
Simply put the new FluentModelMetadataSource class will hold the entire configuration for your model.
The custom FluentModelMetadataSource class and your POCO classes must be located in the same project.
- Add a new class named FluentModelMetadataSource in the SampleFluentModel project.
-
Open FluentModelMetadataSource.cs, and paste the following code in it. This code default maps the Customer class to a table called Customer. In this case the MapType method receives an anonymous type as a parameter, which basically notifies Telerik Data Access to decide the SQL types for the Customer properties on its own. The properties of the anonymous type are what Telerik Data Access uses to create columns in the database. The ToTable method tells Telerik Data Access how the Customer table should be named in the database.
public partial class FluentModelMetadataSource : FluentMetadataSource { protected override IList<MappingConfiguration> PrepareMapping() { List<MappingConfiguration> configurations = new List<MappingConfiguration>(); var customerMapping = new MappingConfiguration<Customer>(); customerMapping.MapType(customer => new { ID = customer.ID, Name = customer.Name, EmailAddress = customer.EmailAddress, DateCreated = customer.DateCreated }).ToTable("Customer"); customerMapping.HasProperty(c => c.ID).IsIdentity(); configurations.Add(customerMapping); return configurations; } }
Partial Public Class FluentModelMetadataSource Inherits FluentMetadataSource Protected Overrides Function PrepareMapping() As _ System.Collections.Generic.IList(Of MappingConfiguration) Dim configurations As New List(Of MappingConfiguration)() Dim customerMapping = New MappingConfiguration(Of Customer)() customerMapping.MapType(Function(customer) New With { Key .ID = customer.Id, Key .Name = customer.Name, Key .EmailAddress = customer.EmailAddress, Key .DateCreated = customer.DateCreated}). ToTable("Customer") customerMapping.FieldNamingRules.AddPrefix = "_" customerMapping.HasProperty(Function(p) p.Id).IsIdentity() configurations.Add(customerMapping) Return configurations End Function End Class
Create The Context Class
To implement a context that is specific to your model and your entities, you simply create a new class that derives from OpenAccessContext and provides properties of type IQueryable<T>. To connect the instances of your context to the underlying database, you need to implement a constructor which calls the base one and passes to it three things:
- A connection string to the database.
- A new instance of the BackendConfiguration class where you specify the backend to be used (in this example - MS SQL database)
- An instance of the FluentModelMetadataSource class you created in the previous section.
Our recommendation is to define the connection string in the configuration file (App.config). In this example, you are going to pass the connection string name to the base constructor.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<connectionStrings>
<add name="connectionId"
connectionString="data source=.\sqlexpress;initial catalog=FluentMappingDatabase;
integrated security=True"
providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
- Add a new class named FluentModel in the SampleFluentModel project.
-
Open FluentModel.cs and paste the following code in it. The code exposes the Customer objects as an IQueryable<Customer>, which allows Telerik Data Access to offload queries to the database to do the heavy lifting. It also makes it easy to query entity sets using standard LINQ.
public partial class FluentModel : OpenAccessContext { private static string connectionStringName = @"connectionId"; private static BackendConfiguration backend = GetBackendConfiguration(); private static MetadataSource metadataSource = new FluentModelMetadataSource(); public FluentModel() :base(connectionStringName, backend, metadataSource) { } public IQueryable<Customer> Customers { get { return this.GetAll<Customer>(); } } public static BackendConfiguration GetBackendConfiguration() { BackendConfiguration backend = new BackendConfiguration(); backend.Backend = "MsSql"; backend.ProviderName = "System.Data.SqlClient"; return backend; } }
Public Partial Class FluentModel Inherits OpenAccessContext Private Shared connectionStringName As String = "connectionId" Private Shared backend As BackendConfiguration = GetBackendConfiguration() Private Shared metadataSource As MetadataSource = New FluentModelMetadataSource() Public Sub New() MyBase.New(connectionStringName, backend, metadataSource) End Sub Public ReadOnly Property Customers() As IQueryable(Of Customer) Get Return Me.GetAll(Of Customer)() End Get End Property Public Shared Function GetBackendConfiguration() _ As BackendConfiguration Dim backend As BackendConfiguration = New BackendConfiguration() backend.Backend = "MsSql" backend.ProviderName = "System.Data.SqlClient" Return backend End Function End Class
At this point the model is fully configured.
Deploy The Database To The Server
You need a project that will consume your SampleFluentModel project. You might be asking why you created a separate project for your classes. The answer is simply because you want to compile your classes into a separate and distinct assembly from the UI project.
- For the sake of simplicity, add to your solution a new Console Application. For example, name the project FluentModelClient.
-
Make the FluentModelClient project a startup project (right click on the ConsoleApplication in Solution Explorer, and select Set as StartUp Project).
-
Copy the App.Config file from the SampleFluentModel project and paste it in the FluentModelClient project. The reason for that action is that the FluentModelClient project is the main (executable) project for the application. When you run the application all settings (configurations) are taken from that project. Respectively, the OpenAccessContext will try to retrieve the connection string from the config file in the main project. If a such doesn't exist, the initialization of the OpenAccessContext will fail.
-
Integrate the Telerik.DataAccess.Core NuGet package in the FluentModelClient project. With this you ensure that the Data Access assemblies required for the proper runtime behaviour of the project will always be available:
- Right-click on the FluentModelClient project and select the Manage NuGet Packages... command.
- In the dialogue, search for Telerik.DataAccess.Core.
- When the package is found, click Install.
-
Add a reference to the SampleFluentModel class library.
At this point the configured project should look like this:
-
Open Program.cs in the Console Application. Add the following code for initializing the database.
using Telerik.OpenAccess; namespace FluentModelClient { class Program { static void Main(string[] args) { UpdateDatabase(); } private static void UpdateDatabase() { using (var context = new FluentModel.FluentModel()) { var schemaHandler = context.GetSchemaHandler(); EnsureDB(schemaHandler); } } private static void EnsureDB(ISchemaHandler schemaHandler) { string script = null; if (schemaHandler.DatabaseExists()) { script = schemaHandler.CreateUpdateDDLScript(null); } else { schemaHandler.CreateDatabase(); script = schemaHandler.CreateDDLScript(); } if (!string.IsNullOrEmpty(script)) { schemaHandler.ExecuteDDLScript(script); } } } }
Imports Telerik.OpenAccess Module Module1 Sub Main() UpdateDatabase() End Sub Private Sub UpdateDatabase() Using context = New FluentModel.FluentModel() Dim schemaHandler = context.GetSchemaHandler() EnsureDB(schemaHandler) End Using End Sub Private Sub EnsureDB(ByVal schemaHandler As ISchemaHandler) Dim script As String = Nothing If schemaHandler.DatabaseExists() Then script = schemaHandler.CreateUpdateDDLScript(Nothing) Else schemaHandler.CreateDatabase() script = schemaHandler.CreateDDLScript() End If If Not String.IsNullOrEmpty(script) Then schemaHandler.ExecuteDDLScript(script) End If End Sub End Module
This code creates a new instance of the OpenAccessContext created when building the model using the Fluent Mapping API. It then checks to see if the database exists, if not the database is created, and then the schema is applied. If the database already exists, Telerik Data Access will create and run a migration script against the database. At this point when you run the application Telerik Data Access will update the database based on any changes made in the fluent mapping.
Next Steps
For more information, check out the Code-Only section.