Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /// <summary>
- /// API Controller class for <see cref="CourseCatalog"/> data.
- /// </summary>
- [Route("api/v1/course/catalog/")]
- [ApiController]
- public sealed class CourseCatalogController : ControllerBase
- {
- /// <summary>
- /// The object responsible for executing commands and queries.
- /// </summary>
- private readonly Messenger messenger;
- /// <summary>
- /// Initializes a new instance of the
- /// <see cref="CourseCatalogController"/> class with a
- /// <see cref="Messenger"/> object.
- /// </summary>
- /// <param name="messenger">
- /// The object responsible for executing commands and queries. Must not
- /// be <c>null</c>.
- /// </param>
- /// <exception cref="ArgumentNullException">
- /// The argument passed to parameter <see cref="messenger"/> is
- /// <c>null</c>.
- /// </exception>
- public CourseCatalogController(Messenger messenger)
- {
- this.messenger =
- messenger ??
- throw new ArgumentNullException(nameof(messenger));
- }
- /// <summary>
- /// Gets a course catalog that includes only those courses of the
- /// language selected by the site visitor.
- /// </summary>
- /// <param name="languageOfCourse">
- /// The language of the courses to include in the course catalog.
- /// </param>
- /// <returns>
- /// Returns a JSON representation of the course catalog.
- /// </returns>
- [HttpGet("{languageOfCourse}")]
- public async Task<ActionResult<CourseCatalog>> GetAsync(
- [Required, FromRoute] string languageOfCourse)
- {
- return await
- this.GetCourseCatalogAsync(
- new CourseCatalogQuery(languageOfCourse))
- .ConfigureAwait(false);
- }
- /// <summary>
- /// Gets a course catalog that includes only those courses of the
- /// language and of the subject selected by the site visitor.
- /// </summary>
- /// <param name="languageOfCourse">
- /// The language of the courses to include in the course catalog.
- /// </param>
- /// <param name="subjectOfCourse">
- /// The subject of the courses to include in the course catalog.
- /// </param>
- /// <returns>
- /// Returns a JSON representation of the course catalog.
- /// </returns>
- [HttpGet("{languageOfCourse}/{subjectOfCourse}")]
- public async Task<ActionResult<CourseCatalog>> GetAsync(
- [Required, FromRoute] string languageOfCourse,
- [Required, FromRoute] string subjectOfCourse)
- {
- return await
- this.GetCourseCatalogAsync(
- new CourseCatalogQuery(languageOfCourse, subjectOfCourse))
- .ConfigureAwait(false);
- }
- /// <summary>
- /// Returns a JSON representation of the course catalog that is
- /// returned by the specified query.
- /// </summary>
- /// <param name="query">
- /// The query object that returns the course catalog for which a JSON
- /// representation is returned.
- /// </param>
- /// <returns>
- /// Returns a JSON representation of the course catalog that is
- /// returned by the specified query.
- /// </returns>
- private async Task<ActionResult<CourseCatalog>> GetCourseCatalogAsync(
- CourseCatalogQuery query)
- {
- return await this.messenger
- .Send(query)
- .ConfigureAwait(false);
- }
- }
- /// <summary>
- /// Base class for an object that executes queries.
- /// </summary>
- /// <typeparam name="TQuery">
- /// The <see cref="Type"/> of query.
- /// </typeparam>
- /// <typeparam name="TResult">
- /// The <see cref="Type"/> of result returned by the query.
- /// </typeparam>
- public abstract class QueryExecutor<TQuery, TResult>
- where TQuery : Query<TResult>
- {
- /// <summary>
- /// Executes the specified query.
- /// </summary>
- /// <param name="query">
- /// The query to execute. Must not be <c>null</c>.
- /// </param>
- /// <returns>
- /// Returns the result of the query.
- /// </returns>
- /// <exception cref="ArgumentNullException">
- /// The argument passed to parameter <paramref name="query"/> is
- /// <c>null</c>.
- /// </exception>
- /// <exception cref="QueryExecutionException">
- /// An error has occurred during the execution of a query.
- /// </exception>
- public Task<TResult> ExecuteAsync(TQuery query)
- {
- if (query is null)
- throw new ArgumentNullException(paramName: nameof(query));
- async Task<TResult> Execute()
- {
- try
- {
- return await this.ExecuteQueryAsync(query)
- .ConfigureAwait(continueOnCapturedContext: false);
- }
- catch (Exception e)
- {
- throw new QueryExecutionException(
- typeOfQueryExecutor: this.GetType(), innerException: e);
- }
- }
- return Execute();
- }
- /// <summary>
- /// When overridden in a derived class, executes the query.
- /// </summary>
- /// <param name="query">
- /// The query to execute. Guaranteed not <c>null</c>.
- /// </param>
- /// <returns>
- /// When overridden in a derived class, returns the result of the query.
- /// </returns>
- protected abstract Task<TResult> ExecuteQueryAsync(TQuery query);
- }
- /// <summary>
- /// Interface for a class that executes queries that retrieve course catalogs.
- /// </summary>
- public abstract class CourseCatalogQueryExecutor
- : QueryExecutor<CourseCatalogQuery, CourseCatalog>
- {
- }
- /// <summary>
- /// Query object that provides the parameters for retrieving a
- /// <see cref="CourseCatalog"/> from a data source. Objects of this type
- /// are immutable.
- /// </summary>
- public sealed class CourseCatalogQuery : Query<CourseCatalog>
- {
- /// <summary>
- /// Initializes a new instance of the <see cref="CourseCatalogQuery"/>
- /// class with a language.
- /// </summary>
- /// <param name="languageOfCourse">
- /// The language of the courses to include in the course catalog.
- /// Must not be <c>null</c>.
- /// </param>
- /// <exception cref="ArgumentNullException">
- /// The argument passed to parameter <paramref name="languageOfCourse"/>
- /// is <c>null</c>.
- /// </exception>
- public CourseCatalogQuery(string languageOfCourse)
- {
- this.LanguageOfCourse =
- languageOfCourse
- ?? throw new ArgumentNullException(nameof(languageOfCourse));
- this.SubjectOfCourse = string.Empty;
- }
- /// <summary>
- /// Initializes a new instance of the <see cref="CourseCatalogQuery"/>
- /// class with a language
- /// and a subject.
- /// </summary>
- /// <param name="languageOfCourse">
- /// The language of the courses to include in the course catalog. Must
- /// not be <c>null</c>.
- /// </param>
- /// <param name="subjectOfCourse">
- /// The subject of the courses to include in the course catalog. Must
- /// not <c>null</c>.
- /// </param>
- /// <exception cref="ArgumentNullException">
- /// The argument passed to parameter <paramref name="languageOfCourse"/>
- /// or parameter <paramref name="languageOfCourse"/> is <c>null</c>.
- /// </exception>
- public CourseCatalogQuery(string languageOfCourse, string subjectOfCourse)
- {
- this.LanguageOfCourse =
- languageOfCourse
- ?? throw new ArgumentNullException(nameof(languageOfCourse));
- this.SubjectOfCourse =
- subjectOfCourse
- ?? throw new ArgumentNullException(nameof(subjectOfCourse));
- }
- /// <summary>
- /// Gets language of the courses to include in the course catalog.
- /// Guaranteed not <c>null</c>.
- /// </summary>
- public string LanguageOfCourse { get; }
- /// <summary>
- /// Gets the subject of the courses to include in the course catalog.
- /// Guaranteed not <c>null</c>.
- /// </summary>
- public string SubjectOfCourse { get; }
- }
- /// <summary>
- /// Class for an object that executes queries that retrieve course
- /// catalogs from a SQL data store.
- /// </summary>
- public sealed class DapperCourseCatalogQueryExecutor
- : CourseCatalogQueryExecutor
- {
- /// <summary>
- /// The database connection through which data is retrieved.
- /// </summary>
- public readonly DbConnection database;
- /// <summary>
- /// Initializes a new instance of the
- /// <see cref="DapperCourseCatalogQueryExecutor"/> class with a
- /// database connection.
- /// </summary>
- /// <param name="database">
- /// The database connection through which data is retrieved.
- /// </param>
- /// <exception cref="ArgumentNullException">
- /// The argument passed to parameter <paramref name="database"/> is
- /// <c>null</c>.
- /// </exception>
- public DapperCourseCatalogQueryExecutor(DbConnection database)
- {
- this.database =
- database
- ?? throw new ArgumentNullException(paramName: nameof(database));
- }
- /// <summary>
- /// Executes the specified <see cref="CourseCatalogQuery"/> query.
- /// </summary>
- /// <param name="query">
- /// The query to execute. Guaranteed not <c>null</c>.
- /// </param>
- /// <returns>
- /// Returns a <see cref="CourseCatalogQuery"/>.
- /// </returns>
- protected override async Task<CourseCatalog> ExecuteQueryAsync(
- CourseCatalogQuery query)
- {
- var sql = this.ConstructSqlStatement(query.SubjectOfCourse);
- var parameters = new {
- query.LanguageOfCourse,
- query.SubjectOfCourse
- };
- var data = await this.database
- .QueryMultipleAsync(sql, parameters)
- .ConfigureAwait(false);
- var languageData = data.ReadAsync();
- var subjectData = data.ReadAsync();
- var courseData = data.ReadAsync();
- await Task.WhenAll(languageData, subjectData, courseData)
- .ConfigureAwait(false);
- return this.ConstructCourseCatalog(
- languageOfCatalog: query.LanguageOfCourse,
- languageData: languageData.Result,
- subjectData: subjectData.Result,
- courseData: courseData.Result);
- }
- /// <summary>
- /// Contructs a <see cref="CourseCatalog"/> using the provided data.
- /// </summary>
- /// <param name="languageOfCatalog">
- /// The languange in which the course catalog is published.
- /// </param>
- /// <param name="languageData">
- /// This list of languages in which the course catalog may be viewed.
- /// </param>
- /// <param name="subjectData">
- /// The list of subjects for which there are courses available.
- /// </param>
- /// <param name="courseData">
- /// The list of courses that are available to site visitors.
- /// </param>
- /// <returns>
- /// Returns a <see cref="CourseCatalog"/>.
- /// </returns>
- private CourseCatalog ConstructCourseCatalog(
- string languageOfCatalog,
- IEnumerable<dynamic> languageData,
- IEnumerable<dynamic> subjectData,
- IEnumerable<dynamic> courseData)
- {
- var availableLanguages =
- from language in languageData
- select new Language(
- language.Name,
- language.DisplayName,
- language.Country);
- var availableSubjects =
- from course in subjectData
- let language = new Language(
- course.CourseLanguageName,
- course.CourseLanguageDisplayName,
- course.CourseLanguageCountry)
- select new Subject(
- course.CourseSubjectId,
- course.CourseSubjectName, language);
- var availableCourses =
- from course in courseData
- let language = new Language(
- course.CourseLanguageName,
- course.CourseLanguageDisplayName,
- course.CourseLanguageCountry)
- let subject = new Subject(
- course.CourseSubjectId,
- course.CourseSubjectName,
- language)
- select new Course(
- course.CourseId,
- course.CourseTitle,
- course.CourseDescription,
- course.CourseImage,
- language,
- subject);
- return new CourseCatalog(
- languageOfCatalog,
- availableLanguages,
- availableSubjects,
- availableCourses);
- }
- /// <summary>
- /// Forms the sql statements that retrieve from the database all data
- /// that is necessary to contruct
- /// a <see cref="CourseCatalog"/>.
- /// </summary>
- /// <param name="subjectOfCourse">
- /// The subject of the courses to include in the course catalog.
- /// </param>
- /// <returns>
- /// Returns the sql statements that retrieve from the database all data
- /// that is necessary to contruct
- /// a <see cref="CourseCatalog"/>.
- /// </returns>
- private string ConstructSqlStatement(string subjectOfCourse)
- {
- var parameter =
- string.IsNullOrWhiteSpace(subjectOfCourse)
- ? "[CourseSubjectId]"
- : "@SubjectOfCourse";
- return $"{this.SqlStatementToRetrieveListOfAvailableLanguages}" +
- $"{this.SqlStatementToRetrieveDistintListOfSubjects}" +
- string.Format($"{SqlStatementToRetrieveTheListOfCourses}", parameter);
- }
- private string SqlStatementToRetrieveListOfAvailableLanguages =>
- $@"SELECT [Name]
- ,[DisplayName]
- ,[Country]
- FROM [Common].[Languages];{Environment.NewLine}";
- private string SqlStatementToRetrieveDistintListOfSubjects =>
- $@"SELECT DISTINCT
- [CourseLanguageName]
- ,[CourseLanguageDisplayName]
- ,[CourseLanguageCountry]
- ,[CourseSubjectId]
- ,[CourseSubjectName]
- FROM [SpacedReps].[ReadModel].[CourseCatalog]
- WHERE [CourseLanguageMoniker] = @LanguageOfCourse{Environment.NewLine}";
- private string SqlStatementToRetrieveTheListOfCourses =>
- @"SELECT [CourseId]
- ,[CourseTitle]
- ,[CourseDescription]
- ,[CourseImage]
- ,[CourseLanguageName]
- ,[CourseLanguageDisplayName]
- ,[CourseLanguageCountry]
- ,[CourseSubjectId]
- ,[CourseSubjectName]
- FROM [SpacedReps].[ReadModel].[CourseCatalog]
- WHERE [CourseLanguageMoniker] = @LanguageOfCourse
- AND [CourseSubjectId] = {0}";
- }
- /// <summary>
- /// Responsible for sending commands or queries to their corresponding
- /// executors.
- /// </summary>
- public sealed class Messenger
- {
- /// <summary>
- /// The object that provides custom services to this object. In this
- /// particular case, provides the service of dynamically creating
- /// instances of the command or query executors.
- /// </summary>
- private IServiceProvider provider;
- /// <summary>
- /// Initializes a new instance of the <see cref="Messenger"/> class
- /// with a provider.
- /// </summary>
- /// <param name="provider">
- /// The object that provides custom services to this object. In this
- /// particular case, provides the service of dynamically creating
- /// instances of the command or query executors. Must not be <c>null</c>.
- /// </param>
- /// <exception cref="ArgumentNullException">
- /// The argument passed to parameter <paramref name="provider"/> is
- /// <c>null</c>.
- /// </exception>
- public Messenger(IServiceProvider provider)
- {
- this.provider =
- provider
- ?? throw new ArgumentNullException(paramName: nameof(provider));
- }
- /// <summary>
- /// Sends the specified query to its executor where it is executed.
- /// </summary>
- /// <typeparam name="T">
- /// The <see cref="Type"/> of query.
- /// </typeparam>
- /// <param name="query">
- /// The query to execute. Must not be <c>null</c>.
- /// </param>
- /// <returns>
- /// Returns the result of the query.
- /// </returns>
- /// <exception cref="ArgumentNullException">
- /// The argument passed to parameter <paramref name="query"/> is
- /// <c>null</c>.
- /// </exception>
- /// <exception cref="QueryExecutionException">
- /// An error has occurred during the execution of a query.
- /// </exception>
- public async Task<T> Send<T>(Query<T> query)
- {
- if (query is null)
- throw new ArgumentNullException(nameof(query));
- Type type = typeof(QueryExecutor<,>);
- Type[] typeArguments = { query.GetType(), typeof(T) };
- Type typeOfExecutor = type.MakeGenericType(typeArguments);
- dynamic executor = this.provider.GetService(typeOfExecutor);
- T result = await executor.ExecuteAsync((dynamic)query)
- .ConfigureAwait(false);
- return result;
- }
- }
- /// <summary>
- /// Configures dependency injection for the application. Use this method to register services
- /// with the IoC container. This method gets called by the runtime.
- /// </summary>
- /// <param name="services">
- /// The container object to which services are added, thereby making those services available
- /// for dependency injection.
- /// </param>
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddMvc()
- .SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
- var y = new SqlConnectionStringBuilder();
- y.DataSource = @".SQLEXPRESS";
- y.InitialCatalog = "TheDatabase";
- y.IntegratedSecurity = true;
- y.ConnectTimeout = 3;
- services.AddTransient<DbConnection, SqlConnection>(x => new SqlConnection(y.ConnectionString));
- services.AddTransient<QueryExecutor<CourseCatalogQuery, CourseCatalog>, DapperCourseCatalogQueryExecutor>();
- services.AddSingleton<Messenger>();
- }
Add Comment
Please, Sign In to add comment