-
Notifications
You must be signed in to change notification settings - Fork 80
Tutorial 1: Step 2: Replacing EntityFramework with CQRS.NET queries
If you missed it, this is the second step, following on from step 1.
- Add the
System.Data.Linq
assembly to the project. - Update the
OrderViewModel
class in theModels
folder to be a partial class. - Add a new class for the
Order
entity as follows:
// C#
namespace KendoUI.Northwind.Dashboard.Code.Entities
{
using System;
using System.Data.Linq.Mapping;
using System.Runtime.Serialization;
using Cqrs.Entities;
[Serializable]
[DataContract]
[Table(Name = "Orders")]
public class OrderEntity : Entity
{
[DataMember]
[Column]
public override Guid Rsn { get; set; }
[DataMember]
[Column]
public override int SortingOrder { get; set; }
[DataMember]
[Column]
public override bool IsLogicallyDeleted { get; set; }
[DataMember]
[Column(Name = "OrderID", IsPrimaryKey = true, IsDbGenerated = true)]
public virtual int OrderId { get; set; }
[DataMember]
[Column(Name = "CustomerID")]
public virtual string CustomerId { get; set; }
[DataMember]
[Column(Name = "EmployeeID")]
public virtual int? EmployeeId { get; set; }
[DataMember]
[Column]
public virtual DateTime? OrderDate { get; set; }
[DataMember]
[Column]
public virtual DateTime? RequiredDate { get; set; }
[DataMember]
[Column]
public virtual DateTime? ShippedDate { get; set; }
[DataMember]
[Column(Name = "ShipVia")]
public virtual int? ShipViaId { get; set; }
[DataMember]
[Column]
public virtual decimal? Freight { get; set; }
[DataMember]
[Column]
public virtual string ShipName { get; set; }
[DataMember]
[Column]
public virtual string ShipAddress { get; set; }
[DataMember]
[Column]
public virtual string ShipCity { get; set; }
[DataMember]
[Column]
public virtual string ShipRegion { get; set; }
[DataMember]
[Column]
public virtual string ShipPostalCode { get; set; }
[DataMember]
[Column]
public virtual string ShipCountry { get; set; }
}
}
namespace KendoUI.Northwind.Dashboard.Models
{
using System;
using System.ComponentModel.DataAnnotations;
using Code.Entities;
using Cqrs.Entities;
public partial class OrderViewModel
{
public OrderViewModel()
{
Rsn = Guid.Empty;
}
[ScaffoldColumn(false)]
public Guid Rsn { get; set; }
/// <summary>
/// Explicit cast of <see cref="OrderEntity"/> to <see cref="OrderViewModel"/>
/// </summary>
/// <param name="entity">A <see cref="OrderEntity"/> <see cref="Entity"/> to convert</param>
/// <returns>A <see cref="OrderViewModel"/> object</returns>
public static explicit operator OrderViewModel(OrderEntity entity)
{
OrderViewModel result = new OrderViewModel
{
Rsn = entity.Rsn,
CustomerID = entity.CustomerId,
OrderID = entity.OrderId,
EmployeeID = entity.EmployeeId,
OrderDate = entity.OrderDate,
ShipCountry = entity.ShipCountry,
ShipVia = entity.ShipViaId,
ShippedDate = entity.ShippedDate,
ShipName = entity.ShipName,
ShipAddress = entity.ShipAddress,
ShipCity = entity.ShipCity,
ShipPostalCode = entity.ShipPostalCode
};
return result;
}
}
}
- Add a new interface for
IDomainDataStoreFactory
as follows:
// C#
namespace KendoUI.Northwind.Dashboard.Code.Factories
{
using Cqrs.DataStores;
using Entities;
/// <summary>
/// A factory for obtaining <see cref="IDataStore{TData}"/> instances
/// </summary>
public interface IDomainDataStoreFactory
{
IDataStore<OrderEntity> GetOrderDataStore();
}
}
- Add a new class for
DomainDataStoreFactory
as follows:
// C#
namespace KendoUI.Northwind.Dashboard.Code.Factories
{
using cdmdotnet.Logging;
using Cqrs.Configuration;
using Cqrs.DataStores;
using Entities;
/// <summary>
/// A factory for obtaining <see cref="IDataStore{TData}"/> instances using the built-in simplified Sql
/// </summary>
public class DomainSimplifiedSqlDataStoreFactory : IDomainDataStoreFactory
{
public DomainSimplifiedSqlDataStoreFactory(ILogger logger, IConfigurationManager configurationManager)
{
Logger = logger;
ConfigurationManager = configurationManager;
}
protected ILogger Logger { get; private set; }
protected IConfigurationManager ConfigurationManager { get; private set; }
#region Implementation of IDomainDataStoreFactory
public virtual IDataStore<OrderEntity> GetOrderDataStore()
{
IDataStore<OrderEntity> result = new SqlDataStore<OrderEntity>(ConfigurationManager, Logger);
return result;
}
#endregion
}
}
- Add a new class for
OrderQueryStrategy
as follows:
// C#
namespace KendoUI.Northwind.Dashboard.Code.Repositories.Queries.Strategies
{
using System;
using System.Collections.Generic;
using Cqrs.Repositories.Queries;
public class OrderQueryStrategy : QueryStrategy
{
internal IList<Func<int, OrderQueryStrategy>> SortingList { get; set; }
public OrderQueryStrategy()
{
SortingList = new List<Func<int, OrderQueryStrategy>>();
}
public virtual OrderQueryStrategy WithRsn(Guid rsn)
{
QueryPredicate = And(IsNotLogicallyDeleted());
QueryPredicate = And(BuildQueryPredicate(WithRsn, rsn));
return this;
}
public virtual OrderQueryStrategy WithOrderId(int orderId)
{
QueryPredicate = And(IsNotLogicallyDeleted());
QueryPredicate = And(BuildQueryPredicate(WithOrderId, orderId));
return this;
}
public virtual OrderQueryStrategy WithNoDeletedOrders()
{
QueryPredicate = And(IsNotLogicallyDeleted());
return this;
}
}
}
- Add a new interface for
IOrderRepository
as follows:
// C#
namespace KendoUI.Northwind.Dashboard.Code.Repositories
{
using Cqrs.Repositories;
using Queries.Strategies;
public interface IOrderRepository : IRepository<OrderQueryStrategy, Entities.OrderEntity>
{
}
}
- Add a new class for
OrderQueryStrategyBuilder
as follows:
// C#
namespace KendoUI.Northwind.Dashboard.Code.Repositories.Queries.Strategies
{
using System;
using System.Collections.Generic;
using System.Linq;
using Cqrs.Configuration;
using Cqrs.Repositories.Queries;
using Entities;
using Factories;
public class OrderQueryStrategyBuilder : QueryBuilder<OrderQueryStrategy, OrderEntity>
{
public OrderQueryStrategyBuilder(IDomainDataStoreFactory dataStoreFactory, IDependencyResolver dependencyResolver)
: base(dataStoreFactory.GetOrderDataStore(), dependencyResolver)
{
}
#region Overrides of QueryBuilder<OrderQueryStrategy, OrderEntity>
protected override IQueryable<OrderEntity> GeneratePredicate(QueryPredicate queryPredicate, IQueryable<OrderEntity> leftHandQueryable = null)
{
OrderQueryStrategy queryStrategy = GetNullQueryStrategy();
SortedSet<QueryParameter> parameters = queryPredicate.Parameters;
IQueryable<OrderEntity> resultingQueryable;
if (queryPredicate.Name == GetFunctionName<Guid>(queryStrategy.WithRsn))
{
resultingQueryable = GeneratePredicateWithRsn(parameters, leftHandQueryable);
return resultingQueryable;
}
if (queryPredicate.Name == GetFunctionName<int>(queryStrategy.WithOrderId))
{
resultingQueryable = GeneratePredicateWithOrderId(parameters, leftHandQueryable);
return resultingQueryable;
}
resultingQueryable = GetEmptyQueryPredicate();
return resultingQueryable;
}
#endregion
protected virtual IQueryable<OrderEntity> GeneratePredicateWithRsn(SortedSet<QueryParameter> parameters, IQueryable<OrderEntity> leftHandQueryable)
{
var rsn = parameters.GetValue<Guid>(0);
IQueryable<OrderEntity> query = (leftHandQueryable ?? GetEmptyQueryPredicate());
IQueryable<OrderEntity> resultingQueryable = query.Where
(
order => order.Rsn == rsn
);
return resultingQueryable;
}
protected virtual IQueryable<OrderEntity> GeneratePredicateWithOrderId(SortedSet<QueryParameter> parameters, IQueryable<OrderEntity> leftHandQueryable)
{
var orderId = parameters.GetValue<int>(0);
IQueryable<OrderEntity> query = (leftHandQueryable ?? GetEmptyQueryPredicate());
IQueryable<OrderEntity> resultingQueryable = query.Where
(
order => order.OrderId == orderId
);
return resultingQueryable;
}
protected override IQueryable<OrderEntity> GetEmptyQueryPredicate()
{
return DataStore;
}
}
}
- Add a new class for
OrderRepository
as follows:
// C#
namespace KendoUI.Northwind.Dashboard.Code.Repositories
{
using Cqrs.Repositories;
using Factories;
using Queries.Strategies;
public class OrderRepository : Repository<OrderQueryStrategy, OrderQueryStrategyBuilder, Entities.OrderEntity>, IOrderRepository
{
public OrderRepository(IDomainDataStoreFactory dataStoreFactory, OrderQueryStrategyBuilder orderQueryBuilder)
: base(dataStoreFactory.GetOrderDataStore, orderQueryBuilder)
{
}
}
}
- Add the following namespaces to
Global.asax.cs
using System;
using cdmdotnet.Logging;
- Add the following to
Global.asax.cs
// C#
protected override void Application_BeginRequest(object sender, EventArgs e)
{
try
{
ICorrelationIdHelper correlationIdHelper = NinjectDependencyResolver.Current.Resolve<ICorrelationIdHelper>();
correlationIdHelper.SetCorrelationId(Guid.NewGuid());
}
catch (NullReferenceException) { }
}
- Add the following to the top of the web.config file
<!-- XML -->
<configSections>
<section name="LoggerSettings" type="cdmdotnet.Logging.Configuration.LoggerSettingsConfigurationSection, cdmdotnet.Logging" />
</configSections>
<LoggerSettings EnableInfo="false" EnableDebug="false" EnableProgress="false" EnableWarning="true" EnableError="true" EnableFatalError="true" EnableSensitive="false" EnableThreadedLogging="true" ModuleName="MyCompany" Instance="MyApplication" EnvironmentInstance="Server1" Environment="Production" EnableThreadedLoggingOutput="false" UsePerformanceCounters="false" UseApplicationInsightTelemetryHelper="false" SqlDatabaseLogsConnectionStringName="Logging" SqlDatabaseTableName="Logs" />
- Add a new class to the
App_Start
folder as follows:
// C#
namespace KendoUI.Northwind.Dashboard
{
using Code.Factories;
using Code.Repositories;
using Ninject.Modules;
public class NorthwindModule : NinjectModule
{
#region Overrides of NinjectModule
/// <summary>
/// Loads the module into the kernel.
/// </summary>
public override void Load()
{
LoadFactories();
LoadRepositories();
}
#endregion
/// <summary>
/// Load all required Repositories
/// </summary>
protected virtual void LoadRepositories()
{
Bind<IOrderRepository>()
.To<OrderRepository>()
.InSingletonScope();
}
/// <summary>
/// Load all required factories.
/// </summary>
protected virtual void LoadFactories()
{
Bind<IDomainDataStoreFactory>()
.To<DomainSimplifiedSqlDataStoreFactory>()
.InSingletonScope();
}
}
}
- Add a new class to the
App_Start
folder as follows:
// C#
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(KendoUI.Northwind.Dashboard.NorthwindConfiguration), "ConfigureNinject", Order = 40)]
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(KendoUI.Northwind.Dashboard.NorthwindConfiguration), "ConfigureMvc", Order = 60)]
namespace KendoUI.Northwind.Dashboard
{
using System.Web.Mvc;
using Cqrs.Ninject.Configuration;
public static class NorthwindConfiguration
{
public static void ConfigureNinject()
{
NinjectDependencyResolver.ModulesToLoad.Add(new NorthwindModule());
}
public static void ConfigureMvc()
{
// Tell ASP.NET MVC 3 to use our Ninject DI Container
DependencyResolver.SetResolver(new Ninject.Web.Mvc.NinjectDependencyResolver(((NinjectDependencyResolver)NinjectDependencyResolver.Current).Kernel));
}
}
}
- Add the following app setting:
<add key="Cqrs.SqlDataStore.ConnectionStringName" value="CQRSNorthwindDataStore" />
- Add the following connection string (adjusting as necessary):
<add name="CQRSNorthwindDataStore" connectionString="Data Source=(LocalDb)\MSSQLLocalDB;AttachDbFilename=|DataDirectory|\NORTHWND.MDF;Integrated Security=SSPI;MultipleActiveResultSets=True;Connect Timeout=30;Application Name=CQRS DataStore;User Instance=True" providerName="System.Data.SqlClient" />
- CQRS.NET ensures a safe migration for existing system by adding, not modifying existing tables. run the following
SQL
on the Orders table:
-- SQL
BEGIN TRANSACTION
GO
ALTER TABLE dbo.Orders ADD
Rsn uniqueidentifier NOT NULL CONSTRAINT DF_Orders_Rsn DEFAULT newId(),
SortingOrder int NOT NULL CONSTRAINT DF_Orders_SortingOrder DEFAULT 0,
IsLogicallyDeleted bit NOT NULL CONSTRAINT DF_Orders_IsLogicallyDeleted DEFAULT 0
GO
COMMIT
- Update the
OrdersController
class by replacing adding the following namespaces:
using Cqrs.Repositories.Queries;
using KendoUI.Northwind.Dashboard.Code.Entities;
using KendoUI.Northwind.Dashboard.Code.Repositories;
using KendoUI.Northwind.Dashboard.Code.Repositories.Queries.Strategies;
- Update the
OrdersController
class by replacing the existingGetOrders
method will the following:
// C#
private IQueryable<OrderViewModel> GetOrders(Guid? orderRsn = null, int? orderId = null)
{
// Define Query
ICollectionResultQuery<OrderQueryStrategy, OrderEntity> query = QueryFactory.CreateNewCollectionResultQuery<OrderQueryStrategy, OrderEntity>();
// We don't want entities we've marked as deleted.
query.QueryStrategy.WithNoDeletedOrders();
// If an orderRsn is provided, query on that
if (orderRsn != null)
query.QueryStrategy.WithRsn(orderRsn.Value);
// If an orderId is provided, query on that
if (orderId != null)
query.QueryStrategy.WithOrderId(orderId.Value);
// Retrieve Data
query = OrderRepository.Retrieve(query);
IEnumerable<OrderEntity> queryResults = query.Result;
IQueryable<OrderViewModel> orders = queryResults
.Select(x => (OrderViewModel)x)
.AsQueryable();
return orders;
}
protected IOrderRepository OrderRepository { get; private set; }
protected IQueryFactory QueryFactory { get; private set; }
public OrdersController(IOrderRepository orderRepository, IQueryFactory queryFactory)
{
OrderRepository = orderRepository;
QueryFactory = queryFactory;
}
- Compile and run the site.
You have now replaced EntityFramework queries with CQRS.NET queries on an existing website without changing your existing DB in a way that would break any existing code. Your code is now ready for step 3, where we will replace object creation (database row insert) operations that use EntityFramework with CQRS.NET.