Skip to content

Tutorial 1: Step 2: Replacing EntityFramework with CQRS.NET queries

cdmdotnet edited this page Feb 1, 2018 · 10 revisions

Tutorial 1 Step 2

If you missed it, this is the second step, following on from step 1.

Add CQRS.NET queries

  1. Add the System.Data.Linq assembly to the project.
  2. Update the OrderViewModel class in the Models folder to be a partial class.
  3. 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;
		}
	}
}
  1. 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();
	}
}
  1. 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
	}
}
  1. 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;
		}
	}
}
  1. 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>
	{
	}
}
  1. 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;
		}
	}
}
  1. 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)
		{
		}
	}
}
  1. Add the following namespaces to Global.asax.cs
  • using System;
  • using cdmdotnet.Logging;
  1. 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) { }
	}
  1. 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" />
  1. 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();
		}
	}
}
  1. 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));
		}
	}
}
  1. Add the following app setting:

<add key="Cqrs.SqlDataStore.ConnectionStringName" value="CQRSNorthwindDataStore" />

  1. 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" />

  1. 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
  1. 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;
  1. Update the OrdersController class by replacing the existing GetOrders 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;
	}
  1. Compile and run the site.

Tutorial 1 Step 3

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.

Clone this wiki locally