Showing preview only (233K chars total). Download the full file or copy to clipboard to get everything.
Repository: dotnetjunkie/solidservices
Branch: master
Commit: 2d7c3de74877
Files: 131
Total size: 201.7 KB
Directory structure:
gitextract_3fjw8qzp/
├── .gitignore
├── LICENSE
├── README.md
└── src/
├── .gitignore
├── BusinessLayer/
│ ├── BusinessLayer.csproj
│ ├── BusinessLayerBootstrapper.cs
│ ├── CommandHandlers/
│ │ ├── CreateOrderCommandHandler.cs
│ │ └── ShipOrderCommandHandler.cs
│ ├── CrossCuttingConcerns/
│ │ ├── AuthorizationCommandHandlerDecorator.cs
│ │ ├── AuthorizationQueryHandlerDecorator.cs
│ │ ├── DataAnnotationsValidator.cs
│ │ ├── StructuredLoggingCommandHandlerDecorator.cs
│ │ ├── StructuredLoggingQueryHandlerDecorator.cs
│ │ ├── StructuredMessageLogger.cs
│ │ ├── ValidationCommandHandlerDecorator.cs
│ │ └── ValidationQueryHandlerDecorator.cs
│ ├── Helpers/
│ │ └── PagingExtensions.cs
│ ├── ICommandHandler.cs
│ ├── ILogger.cs
│ ├── IQueryHandler.cs
│ ├── IValidator.cs
│ ├── Properties/
│ │ └── AssemblyInfo.cs
│ ├── QueryHandlers/
│ │ ├── GetOrderByIdQueryHandler.cs
│ │ └── GetUnshippedOrdersQueryHandler.cs
│ └── packages.config
├── Client/
│ ├── App.config
│ ├── Bootstrapper.cs
│ ├── Client.csproj
│ ├── Code/
│ │ ├── CommandServiceClient.cs
│ │ ├── DynamicQueryProcessor.cs
│ │ ├── QueryServiceClient.cs
│ │ ├── WcfServiceCommandHandlerProxy.cs
│ │ └── WcfServiceQueryHandlerProxy.cs
│ ├── Controllers/
│ │ ├── CommandExampleController.cs
│ │ └── QueryExampleController.cs
│ ├── CrossCuttingConcerns/
│ │ └── FromWcfFaultTranslatorCommandHandlerDecorator.cs
│ ├── ICommandHandler.cs
│ ├── IQueryHandler.cs
│ ├── Program.cs
│ ├── Properties/
│ │ └── AssemblyInfo.cs
│ ├── Wcf/
│ │ ├── KnownCommandTypesAttribute.cs
│ │ ├── KnownQueryAndResultTypesAttribute.cs
│ │ ├── KnownTypesAttribute.cs
│ │ └── KnownTypesDataContractResolver.cs
│ └── packages.config
├── Contract/
│ ├── Commands/
│ │ └── Orders/
│ │ ├── CreateOrder.cs
│ │ └── ShipOrder.cs
│ ├── Contract.csproj
│ ├── DTOs/
│ │ ├── Address.cs
│ │ └── OrderInfo.cs
│ ├── ICommand.cs
│ ├── IQuery.cs
│ ├── IQueryProcessor.cs
│ ├── Properties/
│ │ └── AssemblyInfo.cs
│ ├── Queries/
│ │ ├── Orders/
│ │ │ ├── GetOrderById.cs
│ │ │ └── GetUnshippedOrders.cs
│ │ ├── PageInfo.cs
│ │ └── Paged.cs
│ └── Validators/
│ ├── CompositeValidationResult.cs
│ ├── NonEmptyGuidAttribute.cs
│ └── ValidateObjectAttribute.cs
├── Settings.StyleCop
├── SolidServices.sln
├── WcfService/
│ ├── Bootstrapper.cs
│ ├── Code/
│ │ ├── DebugLogger.cs
│ │ └── WcfExceptionTranslator.cs
│ ├── CommandService.svc
│ ├── CommandService.svc.cs
│ ├── CrossCuttingConcerns/
│ │ └── ToWcfFaultTranslatorCommandHandlerDecorator.cs
│ ├── Global.asax
│ ├── Global.asax.cs
│ ├── NonDotNetQueryService.cs
│ ├── NonDotNetQueryService.svc
│ ├── NonDotNetQueryService.tt
│ ├── Properties/
│ │ └── AssemblyInfo.cs
│ ├── QueryService.svc
│ ├── QueryService.svc.cs
│ ├── ValidationError.cs
│ ├── WcfService.csproj
│ ├── Web.Debug.config
│ ├── Web.Release.config
│ ├── Web.config
│ └── packages.config
├── WebApiService/
│ ├── App_Data/
│ │ └── .gitignore
│ ├── App_Start/
│ │ ├── FilterConfig.cs
│ │ ├── RouteConfig.cs
│ │ ├── SwaggerConfig.cs
│ │ └── WebApiConfig.cs
│ ├── Bootstrapper.cs
│ ├── Code/
│ │ ├── CommandDelegatingHandler.cs
│ │ ├── ExampleObjectCreator.cs
│ │ ├── QueryDelegatingHandler.cs
│ │ ├── SerializationHelpers.cs
│ │ └── WebApiExceptionTranslator.cs
│ ├── Global.asax
│ ├── Global.asax.cs
│ ├── Properties/
│ │ └── AssemblyInfo.cs
│ ├── Web.Debug.config
│ ├── Web.Release.config
│ ├── Web.config
│ ├── WebApiService.csproj
│ └── packages.config
├── WebCore3Service/
│ ├── Bootstrapper.cs
│ ├── Code/
│ │ ├── CommandHandlerMiddleware.cs
│ │ ├── HeaderDictionaryExtensions.cs
│ │ ├── HttpContextExtensions.cs
│ │ ├── QueryHandlerMiddleware.cs
│ │ ├── SerializationHelpers.cs
│ │ ├── StreamExtensions.cs
│ │ └── WebApiErrorResponseBuilder.cs
│ ├── Program.cs
│ ├── Properties/
│ │ └── launchSettings.json
│ ├── Startup.cs
│ ├── WebCore3Service.csproj
│ ├── appsettings.Development.json
│ └── appsettings.json
└── WebCore6Service/
├── Bootstrapper.cs
├── Code/
│ ├── Commands.cs
│ ├── FlatApiMessageMappingBuilder.cs
│ ├── IMessageMappingBuilder.cs
│ ├── MessageMapping.cs
│ ├── MessageMappingExtensions.cs
│ ├── Queries.cs
│ └── WebApiErrorResponseBuilder.cs
├── Program.cs
├── Properties/
│ └── launchSettings.json
├── Swagger/
│ ├── SwaggerExtensions.cs
│ └── XmlDocumentationTypeDescriptionProvider.cs
├── WebCore6Service.csproj
├── appsettings.Development.json
└── appsettings.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
################################################################################
# This .gitignore file was automatically created by Microsoft(R) Visual Studio.
################################################################################
/.vs
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2019 Steven van Deursen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.md
================================================
# Highly Maintainable Web Services
The Highly Maintainable Web Services project is a reference architecture application for .NET that demonstrates how to build highly maintainable web services.
This project contains no documentation, just code. Please visit the following article for the reasoning behind this project:
* [Writing Highly Maintainable WCF services](https://blogs.cuttingedge.it/steven/posts/2012/writing-highly-maintainable-wcf-services/)
For more background about the used design, please read the following articles:
* [Meanwhile… on the command side of my architecture](https://blogs.cuttingedge.it/steven/p/commands/)
* [Meanwhile… on the query side of my architecture](https://blogs.cuttingedge.it/steven/p/queries/)
This project contains the following Web Service projects that all expose the same set of commands and queries:
* [WCF](https://github.com/dotnetjunkie/solidservices/tree/master/src/WcfService). This project exposes command and query messages through a WCF SOAP service, while all messages are specified explicitly through a WSDL. This exposes an explicit contract to the client, although serialization and deserialization of messages is quite limited in WCF, which likely causes problems when sending and receiving messages, unless the messages are explicitly designed with the WCF-serialization constraints in mind. The [Client](https://github.com/dotnetjunkie/solidservices/tree/master/src/Client) project sends queries and commands through the WCF Service. Due to the setup, it gives full integration into the WCF pipeline, which includes security, logging, encryption, and authorization.
* [ASP.NET 'Classic' 4.8 Web API](https://github.com/dotnetjunkie/solidservices/tree/master/src/WebApiService) (includes a Swagger API). This project exposes commands and queries as REST API through the System.Web.Http stack (the 'legacy' ASP.NET Web API) of .NET 4.8. REST makes the contract less explicit, but allows more flexibility over a SOAP service. It uses JSON.NET as serialization mechanism, which allows much flexibility in defining command and query messages. Incoming requests are mapped to HttpMessageHandlers, which dispatch messages to underlying command and query handlers. In doing so, it circumvents a lot of the Web API infrastructure, which means logging and security might need to be dealt with separately. This project uses an external NuGet library to allow exposing its API through an OpenAPI/Swagger interface.
* [ASP.NET Core 3.1 Web API](https://github.com/dotnetjunkie/solidservices/tree/master/src/WebCore3Service). This project exposes commands and queries as REST API through ASP.NET Core 3.1's Web API. REST makes the contract less explicit but allows more flexibility over a SOAP service. Just as the previous 'classic' Web API project, it uses JSON.NET as serialization mechanism, which allows much flexibility in defining command and query messages. Incoming requests are mapped to specific Middleware, which dispatches messages to underlying command and query handlers. In doing so, it circumvents a lot of the ASP.NET Core Web API infrastructure, which means logging and security might need to be dealt with separately. This project has _no_ support for exposing its API through OpenAPI/Swagger.
* [ASP.NET Core 6 Web API](https://github.com/dotnetjunkie/solidservices/tree/master/src/WebCore6Service) (includes a Swagger API). This project exposes commands and queries as REST API through ASP.NET Core 6 Minimal API. The project uses .NET 6's System.Text.Json as serialization mechanism, which is the built-in mechanism. It is less flexible compared to JSON.NET, but gives superb performance. This project makes full use of the new Minimal API functionality and maps each query and command to a specific URL. This allows full integration into the ASP.NET Core pipeline, including logging, security, and OpenAPI/Swagger. There is some extra code added to expose command and query XML documentation summaries through as part of the operation's documentation. Due to limitations in the Minimal API framework, queries only support HTTP POST operations.
================================================
FILE: src/.gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# Security
*.snk
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
build/
bld/
[Bb]in/
[Oo]bj/
Help/
*.boltdata/
# Visual Studo 2015 cache/options directory
.vs/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding addin-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# Windows Azure Build Output
csx/
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
*.[Cc]ache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.pfx
*.publishsettings
node_modules/
bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
================================================
FILE: src/BusinessLayer/BusinessLayer.csproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{5E9B468D-FD1B-4AE5-8A10-C9B09CE2690C}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>BusinessLayer</RootNamespace>
<AssemblyName>BusinessLayer</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.1.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
</Reference>
<Reference Include="SimpleInjector, Version=5.0.0.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
<HintPath>..\packages\SimpleInjector.5.3.2\lib\net461\SimpleInjector.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Core" />
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="BusinessLayerBootstrapper.cs" />
<Compile Include="CommandHandlers\CreateOrderCommandHandler.cs" />
<Compile Include="CommandHandlers\ShipOrderCommandHandler.cs" />
<Compile Include="CrossCuttingConcerns\AuthorizationCommandHandlerDecorator.cs" />
<Compile Include="CrossCuttingConcerns\AuthorizationQueryHandlerDecorator.cs" />
<Compile Include="CrossCuttingConcerns\DataAnnotationsValidator.cs" />
<Compile Include="CrossCuttingConcerns\StructuredLoggingCommandHandlerDecorator.cs" />
<Compile Include="CrossCuttingConcerns\StructuredLoggingQueryHandlerDecorator.cs" />
<Compile Include="CrossCuttingConcerns\StructuredMessageLogger.cs" />
<Compile Include="CrossCuttingConcerns\ValidationCommandHandlerDecorator.cs" />
<Compile Include="CrossCuttingConcerns\ValidationQueryHandlerDecorator.cs" />
<Compile Include="Helpers\PagingExtensions.cs" />
<Compile Include="ICommandHandler.cs" />
<Compile Include="ILogger.cs" />
<Compile Include="IQueryHandler.cs" />
<Compile Include="IValidator.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="QueryHandlers\GetOrderByIdQueryHandler.cs" />
<Compile Include="QueryHandlers\GetUnshippedOrdersQueryHandler.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Contract\Contract.csproj">
<Project>{DDD88351-9A73-4212-85DC-F769B37D5057}</Project>
<Name>Contract</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
================================================
FILE: src/BusinessLayer/BusinessLayerBootstrapper.cs
================================================
namespace BusinessLayer
{
using BusinessLayer.CrossCuttingConcerns;
using Contract;
using SimpleInjector;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
// This class allows registering all types that are defined in the business layer, and are shared across
// all applications that use this layer (WCF and Web API). For simplicity, this class is placed inside
// this assembly, but this does couple the business layer assembly to the used container. If this is a
// concern, create a specific BusinessLayer.Bootstrap project with this class.
public static class BusinessLayerBootstrapper
{
private static readonly Assembly[] ContractAssemblies = new[] { typeof(IQuery<>).Assembly };
private static readonly Assembly[] BusinessLayerAssemblies = new[] { Assembly.GetExecutingAssembly() };
public static void Bootstrap(Container container)
{
if (container == null)
{
throw new ArgumentNullException(nameof(container));
}
container.RegisterInstance<IValidator>(new DataAnnotationsValidator());
container.Register(typeof(ICommandHandler<>), BusinessLayerAssemblies);
container.RegisterDecorator(typeof(ICommandHandler<>), typeof(ValidationCommandHandlerDecorator<>));
container.RegisterDecorator(typeof(ICommandHandler<>), typeof(AuthorizationCommandHandlerDecorator<>));
container.Register(typeof(IQueryHandler<,>), BusinessLayerAssemblies);
container.RegisterDecorator(typeof(IQueryHandler<,>), typeof(ValidationQueryHandlerDecorator<,>));
container.RegisterDecorator(typeof(IQueryHandler<,>), typeof(AuthorizationQueryHandlerDecorator<,>));
}
public static IEnumerable<Type> GetCommandTypes() =>
from assembly in ContractAssemblies
from type in assembly.GetExportedTypes()
where typeof(ICommand).IsAssignableFrom(type)
where !type.IsAbstract
select type;
public static Type CreateQueryHandlerType(Type queryType) =>
typeof(IQueryHandler<,>).MakeGenericType(queryType, DetermineResultTypes(queryType).Single());
public static IEnumerable<(Type QueryType, Type ResultType)> GetQueryTypes() =>
from assembly in ContractAssemblies
from type in assembly.GetExportedTypes()
where IsQuery(type)
select (type, DetermineResultTypes(type).Single());
public static Type GetQueryResultType(Type queryType) => DetermineResultTypes(queryType).Single();
private static bool IsQuery(Type type) => DetermineResultTypes(type).Any();
private static IEnumerable<Type> DetermineResultTypes(Type type) =>
from interfaceType in type.GetInterfaces()
where interfaceType.IsGenericType
where interfaceType.GetGenericTypeDefinition() == typeof(IQuery<>)
select interfaceType.GetGenericArguments()[0];
}
}
================================================
FILE: src/BusinessLayer/CommandHandlers/CreateOrderCommandHandler.cs
================================================
namespace BusinessLayer.CommandHandlers
{
using Contract;
using Contract.Commands.Orders;
public class CreateOrderCommandHandler : ICommandHandler<CreateOrder>
{
private readonly ILogger logger;
public CreateOrderCommandHandler(ILogger logger)
{
this.logger = logger;
}
public void Handle(CreateOrder command)
{
// Do something useful here.
this.logger.Log(this.GetType().Name + " has been executed successfully.");
}
}
}
================================================
FILE: src/BusinessLayer/CommandHandlers/ShipOrderCommandHandler.cs
================================================
namespace BusinessLayer.CommandHandlers
{
using Contract;
using Contract.Commands.Orders;
public class ShipOrderCommandHandler : ICommandHandler<ShipOrder>
{
private readonly ILogger logger;
public ShipOrderCommandHandler(ILogger logger)
{
this.logger = logger;
}
public void Handle(ShipOrder command)
{
this.logger.Log("Shipping order " + command.OrderId);
}
}
}
================================================
FILE: src/BusinessLayer/CrossCuttingConcerns/AuthorizationCommandHandlerDecorator.cs
================================================
namespace BusinessLayer.CrossCuttingConcerns
{
using System.Security;
using System.Security.Principal;
using Contract;
public class AuthorizationCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand> where TCommand : ICommand
{
private readonly ICommandHandler<TCommand> decoratedHandler;
private readonly IPrincipal currentUser;
private readonly ILogger logger;
public AuthorizationCommandHandlerDecorator(ICommandHandler<TCommand> decoratedHandler,
IPrincipal currentUser, ILogger logger)
{
this.decoratedHandler = decoratedHandler;
this.currentUser = currentUser;
this.logger = logger;
}
public void Handle(TCommand query)
{
this.Authorize();
this.decoratedHandler.Handle(query);
}
private void Authorize()
{
// Some useful authorization logic here.
if (typeof(TCommand).Namespace.Contains("Admin") && !this.currentUser.IsInRole("Admin"))
{
throw new SecurityException();
}
this.logger.Log("User " + this.currentUser.Identity.Name + " has been authorized to execute " +
typeof(TCommand).Name);
}
}
}
================================================
FILE: src/BusinessLayer/CrossCuttingConcerns/AuthorizationQueryHandlerDecorator.cs
================================================
namespace BusinessLayer.CrossCuttingConcerns
{
using System.Security;
using System.Security.Principal;
using Contract;
public class AuthorizationQueryHandlerDecorator<TQuery, TResult> : IQueryHandler<TQuery, TResult>
where TQuery : IQuery<TResult>
{
private readonly IQueryHandler<TQuery, TResult> decoratedHandler;
private readonly IPrincipal currentUser;
private readonly ILogger logger;
public AuthorizationQueryHandlerDecorator(IQueryHandler<TQuery, TResult> decoratedHandler,
IPrincipal currentUser, ILogger logger)
{
this.decoratedHandler = decoratedHandler;
this.currentUser = currentUser;
this.logger = logger;
}
public TResult Handle(TQuery query)
{
this.Authorize();
return this.decoratedHandler.Handle(query);
}
private void Authorize()
{
// Some useful authorization logic here.
if (typeof(TQuery).Namespace.Contains("Admin") && !this.currentUser.IsInRole("Admin"))
{
throw new SecurityException();
}
this.logger.Log("User " + this.currentUser.Identity.Name + " has been authorized to execute " +
typeof(TQuery).Name);
}
}
}
================================================
FILE: src/BusinessLayer/CrossCuttingConcerns/DataAnnotationsValidator.cs
================================================
namespace BusinessLayer.CrossCuttingConcerns
{
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
public class DataAnnotationsValidator : IValidator
{
[DebuggerStepThrough]
void IValidator.ValidateObject(object instance)
{
var context = new ValidationContext(instance, null, null);
// Throws an exception when instance is invalid.
Validator.ValidateObject(instance, context, validateAllProperties: true);
}
}
}
================================================
FILE: src/BusinessLayer/CrossCuttingConcerns/StructuredLoggingCommandHandlerDecorator.cs
================================================
namespace BusinessLayer.CrossCuttingConcerns
{
using Contract;
using System.Diagnostics;
public sealed class StructuredLoggingCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand>
where TCommand : ICommand
{
private readonly StructuredMessageLogger<TCommand> logger;
private readonly ICommandHandler<TCommand> decoratee;
public StructuredLoggingCommandHandlerDecorator(
StructuredMessageLogger<TCommand> logger, ICommandHandler<TCommand> decoratee)
{
this.logger = logger;
this.decoratee = decoratee;
}
public void Handle(TCommand command)
{
var watch = Stopwatch.StartNew();
this.decoratee.Handle(command);
this.logger.Log(command, watch.Elapsed);
}
}
}
================================================
FILE: src/BusinessLayer/CrossCuttingConcerns/StructuredLoggingQueryHandlerDecorator.cs
================================================
namespace BusinessLayer.CrossCuttingConcerns
{
using Contract;
using System.Diagnostics;
public sealed class StructuredLoggingQueryHandlerDecorator<TQuery, TResult>
: IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResult>
{
private readonly StructuredMessageLogger<TQuery> logger;
private readonly IQueryHandler<TQuery, TResult> decoratee;
public StructuredLoggingQueryHandlerDecorator(
StructuredMessageLogger<TQuery> logger, IQueryHandler<TQuery, TResult> decoratee)
{
this.logger = logger;
this.decoratee = decoratee;
}
public TResult Handle(TQuery query)
{
var watch = Stopwatch.StartNew();
var result = this.decoratee.Handle(query);
this.logger.Log(query, watch.Elapsed);
return result;
}
}
}
================================================
FILE: src/BusinessLayer/CrossCuttingConcerns/StructuredMessageLogger.cs
================================================
namespace BusinessLayer.CrossCuttingConcerns
{
using System;
using System.Linq;
using System.Reflection;
/// <summary>
/// Logs information about the succesfull execution of a given TMessage, where the used template is specific to
/// the TMessage type with its specified properties. For instance, it might log the following:
/// <code>
/// this.logger.LogInformation(
/// "{Message} executed in {Milliseconds} with parameters {OrderId}",
/// "GetOrderById", // <-- {Message}
/// stopwatch.ElapsedMilliseconds, // <-- {Milliseconds}
/// message.OrderId); // <-- {OrderId}
/// </code>
/// </summary>
/// <typeparam name="TMessage"></typeparam>
public sealed class StructuredMessageLogger<TMessage>
{
private static readonly string MessageName;
private static readonly PropertyInfo[] MessageProperties;
private static readonly string LogTemplate;
private static readonly Type[] SupportedPropertyTypes =
typeof(int).Assembly.GetExportedTypes().Where(t => t.IsPrimitive)
.Concat(new[] { typeof(string), typeof(Guid) })
.ToArray();
private readonly ILogger logger;
static StructuredMessageLogger()
{
// PERF: By using a static constructor, initialization is done just once.
MessageName = typeof(TMessage).Name;
MessageProperties = GetLoggableMessageProperties();
LogTemplate = "{Message} executed in {Milliseconds}";
if (MessageProperties.Length > 0)
{
LogTemplate +=
" with parameters " +
string.Join(", ", MessageProperties.Select(prop => "{" + prop.Name + "}"));
}
}
public StructuredMessageLogger(ILogger logger)
{
this.logger = logger;
}
public void Log(TMessage message, TimeSpan elapsed)
{
object[] parameters = this.BuildParameters(message, elapsed);
this.logger.LogInformation(LogTemplate, parameters);
}
private object[] BuildParameters(TMessage message, TimeSpan elapsed)
{
var parameters = new object[MessageProperties.Length + 2];
parameters[0] = MessageName;
parameters[1] = (long)elapsed.TotalMilliseconds;
for (int i = 0; i < MessageProperties.Length; i++)
{
PropertyInfo property = MessageProperties[i];
// PERF: PropertyInfo.GetValue is pretty slow. If needed this can be optimized by compiling Expression
// trees.
parameters[i + 2] = property.GetValue(message);
}
return parameters;
}
private static PropertyInfo[] GetLoggableMessageProperties()
{
// TODO: Filter out unwanted properties (e.g. complex one or one's with sensitive info). You
// can do this based on an attribute that you place on the property or only include properties
// of certain primitive types (or both). The example here uses a white list of supported types
return (
from prop in typeof(TMessage).GetProperties(BindingFlags.Instance | BindingFlags.Public)
where SupportedPropertyTypes.Contains(prop.PropertyType)
orderby prop.Name // Sorting is important, because ordering is not guaranteed across restarts
select prop)
.ToArray();
}
}
}
================================================
FILE: src/BusinessLayer/CrossCuttingConcerns/ValidationCommandHandlerDecorator.cs
================================================
namespace BusinessLayer.CrossCuttingConcerns
{
using Contract;
using System;
public class ValidationCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand> where TCommand : ICommand
{
private readonly IValidator validator;
private readonly ICommandHandler<TCommand> handler;
public ValidationCommandHandlerDecorator(IValidator validator, ICommandHandler<TCommand> handler)
{
this.validator = validator;
this.handler = handler;
}
void ICommandHandler<TCommand>.Handle(TCommand command)
{
if (command == null) throw new ArgumentNullException(nameof(command));
// validate the supplied command.
this.validator.ValidateObject(command);
// forward the (valid) command to the real command handler.
this.handler.Handle(command);
}
}
}
================================================
FILE: src/BusinessLayer/CrossCuttingConcerns/ValidationQueryHandlerDecorator.cs
================================================
namespace BusinessLayer.CrossCuttingConcerns
{
using System;
using Contract;
public class ValidationQueryHandlerDecorator<TQuery, TResult> : IQueryHandler<TQuery, TResult>
where TQuery : IQuery<TResult>
{
private readonly IValidator validator;
private readonly IQueryHandler<TQuery, TResult> handler;
public ValidationQueryHandlerDecorator(IValidator validator, IQueryHandler<TQuery, TResult> handler)
{
this.validator = validator;
this.handler = handler;
}
TResult IQueryHandler<TQuery, TResult>.Handle(TQuery query)
{
if (query == null) throw new ArgumentNullException(nameof(query));
// validate the supplied command.
this.validator.ValidateObject(query);
// forward the (valid) command to the real command handler.
return this.handler.Handle(query);
}
}
}
================================================
FILE: src/BusinessLayer/Helpers/PagingExtensions.cs
================================================
namespace BusinessLayer
{
using System.Collections.Generic;
using System.Linq;
using Contract.Queries;
public static class PagingExtensions
{
/// <summary>Apply paging in memory, using LINQ to Objects.</summary>
/// <typeparam name="T">The type of objects to enumerate.</typeparam>
/// <param name="collection">The collection</param>
/// <param name="paging">The optional paging object. When null, the default paging values will be used.</param>
/// <returns>A paged result.</returns>
public static Paged<T> Page<T>(this IEnumerable<T> collection, PageInfo paging)
{
paging = paging ?? new PageInfo();
IEnumerable<T> items = paging.IsSinglePage()
? collection
: collection.Skip(paging.PageIndex * paging.PageSize).Take(paging.PageSize);
return new Paged<T> { Items = items.ToArray(), Paging = paging };
}
/// <summary>Apply paging using an efficient database query.</summary>
/// <typeparam name="T">The type of objects to enumerate.</typeparam>
/// <param name="collection">The collection</param>
/// <param name="paging">The optional paging object. When null, the default paging values will be used.</param>
/// <returns>A paged result.</returns>
public static Paged<T> Page<T>(this IQueryable<T> collection, PageInfo paging)
{
paging = paging ?? new PageInfo();
IQueryable<T> items = paging.IsSinglePage()
? collection
: collection.Skip(paging.PageIndex * paging.PageSize).Take(paging.PageSize);
return new Paged<T> { Items = items.ToArray(), Paging = paging };
}
}
}
================================================
FILE: src/BusinessLayer/ICommandHandler.cs
================================================
using Contract;
namespace BusinessLayer
{
public interface ICommandHandler<TCommand> where TCommand : ICommand
{
void Handle(TCommand command);
}
}
================================================
FILE: src/BusinessLayer/ILogger.cs
================================================
namespace BusinessLayer
{
public interface ILogger
{
void Log(string message);
}
public static class LoggerExtensions
{
public static void LogInformation(this ILogger logger, string messageTemplate, params object[] parameters)
{
// TODO: Use real structured logging here.
logger.Log(messageTemplate);
}
}
}
================================================
FILE: src/BusinessLayer/IQueryHandler.cs
================================================
namespace Contract
{
public interface IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResult>
{
TResult Handle(TQuery query);
}
}
================================================
FILE: src/BusinessLayer/IValidator.cs
================================================
namespace BusinessLayer
{
public interface IValidator
{
/// <summary>Validates the given instance.</summary>
/// <param name="instance">The instance to validate.</param>
/// <exception cref="ArgumentNullException">Thrown when the instance is a null reference.</exception>
/// <exception cref="ValidationException">Thrown when the instance is invalid.</exception>
void ValidateObject(object instance);
}
}
================================================
FILE: src/BusinessLayer/Properties/AssemblyInfo.cs
================================================
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("BusinessLayer")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("BusinessLayer")]
[assembly: AssemblyCopyright("Copyright © 2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("06b95749-3b64-47c3-b720-edb06233ed33")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
================================================
FILE: src/BusinessLayer/QueryHandlers/GetOrderByIdQueryHandler.cs
================================================
namespace BusinessLayer.QueryHandlers
{
using System;
using System.Collections.Generic;
using Contract;
using Contract.DTOs;
using Contract.Queries.Orders;
public class GetOrderByIdQueryHandler : IQueryHandler<GetOrderById, OrderInfo>
{
public OrderInfo Handle(GetOrderById query)
{
if (query.OrderId == Guid.Empty)
{
throw new KeyNotFoundException();
}
return new OrderInfo
{
Id = query.OrderId,
CreationDate = DateTime.Today,
TotalAmount = 300
};
}
}
}
================================================
FILE: src/BusinessLayer/QueryHandlers/GetUnshippedOrdersQueryHandler.cs
================================================
namespace BusinessLayer.QueryHandlers
{
using System;
using System.Collections.Generic;
using System.Linq;
using Contract;
using Contract.DTOs;
using Contract.Queries;
using Contract.Queries.Orders;
public class GetUnshippedOrdersQueryHandler : IQueryHandler<GetUnshippedOrders, Paged<OrderInfo>>
{
private readonly ILogger logger;
public GetUnshippedOrdersQueryHandler(ILogger logger)
{
this.logger = logger;
}
public Paged<OrderInfo> Handle(GetUnshippedOrders query)
{
this.logger.Log(string.Format("{0} {{ Paging = {{ PageIndex = {1}, PageSize = {2} }} }}",
query.GetType().Name, query.Paging?.PageIndex, query.Paging?.PageSize));
return GetAllOrders().Page(query.Paging);
}
private static IEnumerable<OrderInfo> GetAllOrders()
{
var random = new Random();
return
from number in Enumerable.Range(1, 100000)
select new OrderInfo
{
Id = Guid.NewGuid(),
TotalAmount = random.Next(100, 1000),
CreationDate = DateTime.Today.AddDays(-number)
};
}
}
}
================================================
FILE: src/BusinessLayer/packages.config
================================================
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Bcl.AsyncInterfaces" version="1.0.0" targetFramework="net48" />
<package id="SimpleInjector" version="5.3.2" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensions" version="4.5.2" targetFramework="net48" />
</packages>
================================================
FILE: src/Client/App.config
================================================
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_CommandService" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None" realm=""/>
<message clientCredentialType="UserName" algorithmSuite="Default"/>
</security>
</binding>
<binding name="BasicHttpBinding_QueryService" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384"/>
<security mode="None">
<transport clientCredentialType="None" proxyCredentialType="None" realm=""/>
<message clientCredentialType="UserName" algorithmSuite="Default"/>
</security>
</binding>
</basicHttpBinding>
</bindings>
<client>
<endpoint address="http://localhost:9998/CommandService.svc" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_CommandService" contract="CommandServices.CommandService" name="BasicHttpBinding_CommandService"/>
<endpoint address="http://localhost:9998/QueryService.svc" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_QueryService" contract="QueryServices.QueryService" name="BasicHttpBinding_QueryService"/>
</client>
</system.serviceModel>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="SimpleInjector" publicKeyToken="984cb50dea722e99" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0"/>
</dependentAssembly>
</assemblyBinding>
</runtime>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8"/></startup></configuration>
================================================
FILE: src/Client/Bootstrapper.cs
================================================
namespace Client
{
using Client.Code;
using Client.Controllers;
using Client.CrossCuttingConcerns;
using Contract;
using SimpleInjector;
public static class Bootstrapper
{
private static Container container;
public static void Bootstrap()
{
container = new Container();
container.RegisterInstance<IQueryProcessor>(new DynamicQueryProcessor(container));
container.Register(typeof(ICommandHandler<>), typeof(WcfServiceCommandHandlerProxy<>));
container.Register(typeof(IQueryHandler<,>), typeof(WcfServiceQueryHandlerProxy<,>));
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(FromWcfFaultTranslatorCommandHandlerDecorator<>));
container.Register<CommandExampleController>();
container.Register<QueryExampleController>();
container.Verify();
}
public static TService GetInstance<TService>() where TService : class
{
return container.GetInstance<TService>();
}
}
}
================================================
FILE: src/Client/Client.csproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{9562D251-49BE-4650-93BD-FA6D66DBA61C}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Client</RootNamespace>
<AssemblyName>Client</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.1.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
</Reference>
<Reference Include="SimpleInjector, Version=5.0.0.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
<HintPath>..\packages\SimpleInjector.5.3.2\lib\net461\SimpleInjector.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Core" />
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Bootstrapper.cs" />
<Compile Include="Code\CommandServiceClient.cs" />
<Compile Include="Code\DynamicQueryProcessor.cs" />
<Compile Include="Code\QueryServiceClient.cs" />
<Compile Include="Wcf\KnownCommandTypesAttribute.cs" />
<Compile Include="Wcf\KnownQueryAndResultTypesAttribute.cs" />
<Compile Include="Wcf\KnownTypesAttribute.cs" />
<Compile Include="Wcf\KnownTypesDataContractResolver.cs" />
<Compile Include="Code\WcfServiceCommandHandlerProxy.cs" />
<Compile Include="Code\WcfServiceQueryHandlerProxy.cs" />
<Compile Include="Controllers\CommandExampleController.cs" />
<Compile Include="Controllers\QueryExampleController.cs" />
<Compile Include="CrossCuttingConcerns\FromWcfFaultTranslatorCommandHandlerDecorator.cs" />
<Compile Include="ICommandHandler.cs" />
<Compile Include="IQueryHandler.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Contract\Contract.csproj">
<Project>{DDD88351-9A73-4212-85DC-F769B37D5057}</Project>
<Name>Contract</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
================================================
FILE: src/Client/Code/CommandServiceClient.cs
================================================
namespace Client.Code
{
using System.Diagnostics;
using System.ServiceModel;
using Client.Wcf;
// This service reference is hand-coded. This allows us to use our custom KnownCommandTypesAttribute,
// which allows providing WCF with known types at runtime. This prevents us to have to update the client
// reference each time a new command is added to the system.
[KnownCommandTypes]
[ServiceContract(
Namespace = "http://www.solid.net/commandservice/v1.0",
ConfigurationName = "CommandServices.CommandService")]
public interface CommandService
{
[OperationContract(
Action = "http://www.solid.net/commandservice/v1.0/CommandService/Execute",
ReplyAction = "http://www.solid.net/commandservice/v1.0/CommandService/ExecuteResponse")]
object Execute(object command);
}
public class CommandServiceClient : ClientBase<CommandService>, CommandService
{
[DebuggerStepThrough]
public object Execute(object command) => this.Channel.Execute(command);
}
}
================================================
FILE: src/Client/Code/DynamicQueryProcessor.cs
================================================
namespace Client.Code
{
using System.Diagnostics;
using SimpleInjector;
using Contract;
public sealed class DynamicQueryProcessor : IQueryProcessor
{
private readonly Container container;
public DynamicQueryProcessor(Container container)
{
this.container = container;
}
[DebuggerStepThrough]
public TResult Execute<TResult>(IQuery<TResult> query)
{
var handlerType = typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), typeof(TResult));
dynamic handler = this.container.GetInstance(handlerType);
return handler.Handle((dynamic)query);
}
}
}
================================================
FILE: src/Client/Code/QueryServiceClient.cs
================================================
namespace Client.Code
{
using System.Diagnostics;
using System.ServiceModel;
using Client.Wcf;
// This service reference is hand-coded. This allows us to use the KnownQueryAndResultTypesAttribute,
// which allows providing WCF with known types at runtime. This prevents us to have to update the client
// reference each time a new command is added to the system.
[KnownQueryAndResultTypes]
[ServiceContract(
Namespace = "http://www.cuttingedge.it/solid/queryservice/v1.0",
ConfigurationName = "QueryServices.QueryService")]
public interface QueryService
{
[OperationContract(
Action = "http://www.cuttingedge.it/solid/queryservice/v1.0/QueryService/Execute",
ReplyAction = "http://www.cuttingedge.it/solid/queryservice/v1.0/QueryService/ExecuteResponse")]
object Execute(object query);
}
public class QueryServiceClient : ClientBase<QueryService>, QueryService
{
[DebuggerStepThrough]
public object Execute(object query) => this.Channel.Execute(query);
}
}
================================================
FILE: src/Client/Code/WcfServiceCommandHandlerProxy.cs
================================================
namespace Client.Code
{
using System;
using System.Diagnostics;
public sealed class WcfServiceCommandHandlerProxy<TCommand> : ICommandHandler<TCommand>
{
[DebuggerStepThrough]
public void Handle(TCommand command)
{
var service = new CommandServiceClient();
try
{
service.Execute(command);
}
finally
{
try
{
((IDisposable)service).Dispose();
}
catch
{
// Against good practice and the Framework Design Guidelines, WCF can throw an
// exception during a call to Dispose, which can result in loss of the original exception.
// See: https://marcgravell.blogspot.com/2008/11/dontdontuse-using.html
// See: https://msdn.microsoft.com/en-us/library/aa355056.aspx
}
}
}
}
}
================================================
FILE: src/Client/Code/WcfServiceQueryHandlerProxy.cs
================================================
namespace Client.Code
{
using System;
using System.Diagnostics;
using Contract;
public sealed class WcfServiceQueryHandlerProxy<TQuery, TResult> : IQueryHandler<TQuery, TResult>
where TQuery : IQuery<TResult>
{
[DebuggerStepThrough]
public TResult Handle(TQuery query)
{
var service = new QueryServiceClient();
try
{
return (TResult)service.Execute(query);
}
finally
{
try
{
((IDisposable)service).Dispose();
}
catch
{
// Against good practice and the Framework Design Guidelines, WCF can throw an
// exception during a call to Dispose, which can result in loss of the original exception.
// See: https://marcgravell.blogspot.com/2008/11/dontdontuse-using.html
// See: https://msdn.microsoft.com/en-us/library/aa355056.aspx
}
}
}
}
}
================================================
FILE: src/Client/Controllers/CommandExampleController.cs
================================================
namespace Client.Controllers
{
using System;
using Contract.Commands.Orders;
using Contract.DTOs;
public class CommandExampleController
{
private readonly ICommandHandler<CreateOrder> createOrderhandler;
private readonly ICommandHandler<ShipOrder> shipOrderhandler;
public CommandExampleController(
ICommandHandler<CreateOrder> createOrderhandler,
ICommandHandler<ShipOrder> shipOrderhandler)
{
this.createOrderhandler = createOrderhandler;
this.shipOrderhandler = shipOrderhandler;
}
public Guid CreateOrder()
{
var createOrderCommand = new CreateOrder
{
NewOrderId = Guid.NewGuid(),
ShippingAddress = new Address
{
Country = "The Netherlands",
City = "Nijmegen",
Street = ".NET Street"
}
};
this.createOrderhandler.Handle(createOrderCommand);
Console.WriteLine("Order with ID {0} has been created.", createOrderCommand.NewOrderId);
return createOrderCommand.NewOrderId;
}
public void ShipOrder(Guid orderId)
{
this.shipOrderhandler.Handle(new ShipOrder { OrderId = orderId });
Console.WriteLine("Order with ID {0} is shipped.", orderId);
}
}
}
================================================
FILE: src/Client/Controllers/QueryExampleController.cs
================================================
namespace Client.Controllers
{
using System;
using System.Linq;
using Contract;
using Contract.DTOs;
using Contract.Queries;
using Contract.Queries.Orders;
public class QueryExampleController
{
private readonly IQueryProcessor queryProcessor;
public QueryExampleController(IQueryProcessor queryProcessor)
{
this.queryProcessor = queryProcessor;
}
public void ShowOrders(int pageIndex, int pageSize)
{
var orders = this.queryProcessor.Execute(new GetUnshippedOrders
{
Paging = new PageInfo { PageIndex = pageIndex, PageSize = pageSize }
});
Console.WriteLine();
Console.WriteLine("Query returned {0} orders: ", orders.Items.Length);
foreach (var order in orders.Items)
{
Console.WriteLine("OrderId: {0}, Amount: {1}, ShipDate: {2:d}",
order.Id, order.TotalAmount, order.CreationDate);
}
Console.WriteLine("Total: " + orders.Items.Sum(order => order.TotalAmount));
}
}
}
================================================
FILE: src/Client/CrossCuttingConcerns/FromWcfFaultTranslatorCommandHandlerDecorator.cs
================================================
namespace Client.CrossCuttingConcerns
{
using System.ComponentModel.DataAnnotations;
using System.ServiceModel;
public class FromWcfFaultTranslatorCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand>
{
private readonly ICommandHandler<TCommand> decoratee;
public FromWcfFaultTranslatorCommandHandlerDecorator(ICommandHandler<TCommand> decoratee)
{
this.decoratee = decoratee;
}
public void Handle(TCommand command)
{
try
{
this.decoratee.Handle(command);
}
catch (FaultException ex) when (ex.Code?.Name == "ValidationError")
{
// The WCF service communicates this specific error back to us in case of a validation
// error. We translate it back to an exception that the client can handle..
throw new ValidationException(ex.Message);
}
}
}
}
================================================
FILE: src/Client/ICommandHandler.cs
================================================
namespace Client
{
public interface ICommandHandler<TCommand>
{
void Handle(TCommand command);
}
}
================================================
FILE: src/Client/IQueryHandler.cs
================================================
namespace Client
{
using Contract;
public interface IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResult>
{
TResult Handle(TQuery query);
}
}
================================================
FILE: src/Client/Program.cs
================================================
namespace Client
{
using System;
using Client.Controllers;
public class Program
{
public static void Main(string[] args)
{
Bootstrapper.Bootstrap();
var orderController = Bootstrapper.GetInstance<CommandExampleController>();
var orderId = orderController.CreateOrder();
orderController.ShipOrder(orderId);
var showUnshippedOrdersController = Bootstrapper.GetInstance<QueryExampleController>();
showUnshippedOrdersController.ShowOrders(pageIndex: 0, pageSize: 10);
Console.ReadLine();
}
}
}
================================================
FILE: src/Client/Properties/AssemblyInfo.cs
================================================
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Client")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Client")]
[assembly: AssemblyCopyright("Copyright © 2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("3a03603f-acfe-4d73-b924-da926f09a67e")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
================================================
FILE: src/Client/Wcf/KnownCommandTypesAttribute.cs
================================================
namespace Client.Wcf
{
using System;
using System.Collections.Generic;
using System.Linq;
using Contract;
public class KnownCommandTypesAttribute : KnownTypesAttribute
{
public KnownCommandTypesAttribute() : base(new KnownTypesDataContractResolver(CommandTypes))
{
}
private static IEnumerable<Type> CommandTypes =>
from type in typeof(Contract.Commands.Orders.CreateOrder).Assembly.GetExportedTypes()
where typeof(ICommand).IsAssignableFrom(type)
where !type.IsAbstract
select type;
}
}
================================================
FILE: src/Client/Wcf/KnownQueryAndResultTypesAttribute.cs
================================================
namespace Client.Wcf
{
using System;
using System.Collections.Generic;
using System.Linq;
using Contract;
public class KnownQueryAndResultTypesAttribute : KnownTypesAttribute
{
public KnownQueryAndResultTypesAttribute()
: base(new KnownTypesDataContractResolver(QueryTypes.Union(ResultTypes)))
{
}
private static IEnumerable<Type> ResultTypes => QueryTypes.Select(GetResultType);
private static IEnumerable<Type> QueryTypes =>
typeof(Contract.Queries.Orders.GetOrderById).Assembly.GetExportedTypes().Where(IsQueryType);
private static bool IsQueryType(Type type) => GetResultType(type) != null;
private static Type GetResultType(Type queryType) => (
from iface in queryType.GetInterfaces()
where iface.IsGenericType && iface.GetGenericTypeDefinition() == typeof(IQuery<>)
select iface.GetGenericArguments()[0])
.SingleOrDefault();
}
}
================================================
FILE: src/Client/Wcf/KnownTypesAttribute.cs
================================================
namespace Client.Wcf
{
using System;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)]
public abstract class KnownTypesAttribute : Attribute, IContractBehavior
{
private readonly KnownTypesDataContractResolver resolver;
public KnownTypesAttribute(KnownTypesDataContractResolver resolver)
{
this.resolver = resolver;
}
public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint,
BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint,
ClientRuntime clientRuntime)
{
this.CreateMyDataContractSerializerOperationBehaviors(contractDescription);
}
public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint,
DispatchRuntime dispatchRuntime)
{
this.CreateMyDataContractSerializerOperationBehaviors(contractDescription);
}
public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint)
{
}
private void CreateMyDataContractSerializerOperationBehaviors(ContractDescription description)
{
foreach (OperationDescription operationDescription in description.Operations)
{
this.CreateMyDataContractSerializerOperationBehavior(operationDescription);
}
}
private void CreateMyDataContractSerializerOperationBehavior(OperationDescription operationDescription)
{
DataContractSerializerOperationBehavior dataContractSerializerOperationbehavior =
operationDescription.Behaviors.Find<DataContractSerializerOperationBehavior>();
dataContractSerializerOperationbehavior.DataContractResolver = this.resolver;
}
}
}
================================================
FILE: src/Client/Wcf/KnownTypesDataContractResolver.cs
================================================
namespace Client.Wcf
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.Serialization;
using System.Xml;
// source: https://msdn.microsoft.com/en-us/library/dd807519%28v=vs.110%29.aspx
public sealed class KnownTypesDataContractResolver : DataContractResolver
{
private readonly Dictionary<string, Type> knownTypes;
public KnownTypesDataContractResolver(IEnumerable<Type> types)
{
this.knownTypes = types.Distinct().ToDictionary(GetName);
}
public override Type ResolveName(
string typeName, string typeNamespace, Type declaredType, DataContractResolver knownTypeResolver)
{
try
{
Type type;
return this.knownTypes.TryGetValue(typeName, out type)
? type
: knownTypeResolver.ResolveName(typeName, typeNamespace, declaredType, null);
}
catch (Exception ex)
{
throw new InvalidOperationException(
$"Unable to resolve type {typeName}. {ex.InnerException} " +
"If the given type name is postfixed with a weird base64 encoded value, it means that " +
"the type is a generic type. WCF postfixes the name with a hash based on the " +
"namespaces of the generic type arguments. To fix this, mark the class with the " +
"DataContractAttribute to force a specific name. Example:" +
"[DataContract(Name = nameof(YourType<T>) + \"Of{0}\")]. And don't forget to mark the type's " +
"properties with the DataMemberAttribute.", ex);
}
}
[DebuggerStepThrough]
public override bool TryResolveType(Type type, Type declaredType, DataContractResolver knownTypeResolver,
out XmlDictionaryString typeName, out XmlDictionaryString typeNamespace)
{
if (!knownTypeResolver.TryResolveType(type, declaredType, null, out typeName, out typeNamespace))
{
typeName = new XmlDictionaryString(XmlDictionary.Empty, type.Name, 0);
typeNamespace = new XmlDictionaryString(XmlDictionary.Empty, type.Namespace, 0);
}
return true;
}
private static string GetName(Type type) =>
type.IsArray
? "ArrayOf" + GetName(type.GetElementType())
: type.IsGenericType ? GetGenericName(type) : type.Name;
private static string GetGenericName(Type type)
{
Type typeDef = type.GetGenericTypeDefinition();
string name = typeDef.Name.Substring(0, typeDef.Name.IndexOf('`'));
return name + "Of" + string.Join(string.Empty, type.GetGenericArguments().Select(GetName));
}
}
}
================================================
FILE: src/Client/packages.config
================================================
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Bcl.AsyncInterfaces" version="1.0.0" targetFramework="net48" />
<package id="SimpleInjector" version="5.3.2" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensions" version="4.5.2" targetFramework="net48" />
</packages>
================================================
FILE: src/Contract/Commands/Orders/CreateOrder.cs
================================================
namespace Contract.Commands.Orders
{
using System;
using System.ComponentModel.DataAnnotations;
using Contract.DTOs;
using Contract.Validators;
/// <summary>Creates a new order.</summary>
public class CreateOrder : ICommand
{
/// <summary>The order id of the new order.</summary>
[NonEmptyGuid]
public Guid NewOrderId { get; set; }
/// <summary>The order's shipping address.</summary>
[Required, ValidateObject]
public Address ShippingAddress { get; set; }
}
}
================================================
FILE: src/Contract/Commands/Orders/ShipOrder.cs
================================================
namespace Contract.Commands.Orders
{
using System;
using Contract.Validators;
/// <summary>Commands an order to be shipped.</summary>
public class ShipOrder : ICommand
{
/// <summary>The id of the order.</summary>
[NonEmptyGuid]
public Guid OrderId { get; set; }
}
}
================================================
FILE: src/Contract/Contract.csproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{DDD88351-9A73-4212-85DC-F769B37D5057}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Contract</RootNamespace>
<AssemblyName>Contract</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
<TargetFrameworkProfile>
</TargetFrameworkProfile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<Prefer32Bit>false</Prefer32Bit>
<DocumentationFile>bin\Debug\Contract.XML</DocumentationFile>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<Prefer32Bit>false</Prefer32Bit>
<DocumentationFile>bin\Release\Contract.XML</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Data" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Commands\Orders\CreateOrder.cs" />
<Compile Include="Commands\Orders\ShipOrder.cs" />
<Compile Include="DTOs\Address.cs" />
<Compile Include="DTOs\OrderInfo.cs" />
<Compile Include="ICommand.cs" />
<Compile Include="IQuery.cs" />
<Compile Include="IQueryProcessor.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Queries\Orders\GetOrderById.cs" />
<Compile Include="Queries\Orders\GetUnshippedOrders.cs" />
<Compile Include="Queries\Paged.cs" />
<Compile Include="Queries\PageInfo.cs" />
<Compile Include="Validators\CompositeValidationResult.cs" />
<Compile Include="Validators\NonEmptyGuidAttribute.cs" />
<Compile Include="Validators\ValidateObjectAttribute.cs" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
================================================
FILE: src/Contract/DTOs/Address.cs
================================================
namespace Contract.DTOs
{
using System.ComponentModel.DataAnnotations;
public class Address
{
/// <summary>The country.</summary>
[Required(AllowEmptyStrings = false)]
[StringLength(100)]
public string Country { get; set; }
/// <summary>The city.</summary>
[Required(AllowEmptyStrings = false)]
[StringLength(100)]
public string City { get; set; }
/// <summary>The street name including number.</summary>
[Required(AllowEmptyStrings = false)]
[StringLength(100)]
public string Street { get; set; }
}
}
================================================
FILE: src/Contract/DTOs/OrderInfo.cs
================================================
namespace Contract.DTOs
{
using System;
public class OrderInfo
{
public Guid Id { get; set; }
public DateTime CreationDate { get; set; }
public decimal TotalAmount { get; set; }
}
}
================================================
FILE: src/Contract/ICommand.cs
================================================
namespace Contract
{
public interface ICommand { }
}
================================================
FILE: src/Contract/IQuery.cs
================================================
namespace Contract
{
/// <summary>Defines a query message.</summary>
/// <typeparam name="TResult"></typeparam>
public interface IQuery<TResult>
{
}
}
================================================
FILE: src/Contract/IQueryProcessor.cs
================================================
namespace Contract
{
public interface IQueryProcessor
{
TResult Execute<TResult>(IQuery<TResult> query);
}
}
================================================
FILE: src/Contract/Properties/AssemblyInfo.cs
================================================
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Contract")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Contract")]
[assembly: AssemblyCopyright("Copyright © 2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("1281e947-813d-46c8-8b1f-59eba87bb298")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
================================================
FILE: src/Contract/Queries/Orders/GetOrderById.cs
================================================
namespace Contract.Queries.Orders
{
using System;
using Contract.DTOs;
using Validators;
/// <summary>Gets order information of a single order by its id.</summary>
public class GetOrderById : IQuery<OrderInfo>
{
/// <summary>The id of the order to get.</summary>
[NonEmptyGuid]
public Guid OrderId { get; set; }
}
}
================================================
FILE: src/Contract/Queries/Orders/GetUnshippedOrders.cs
================================================
namespace Contract.Queries.Orders
{
using Contract.DTOs;
/// <summary>
/// Gets a paged list of all unshipped orders for the current logged in user.
/// </summary>
public class GetUnshippedOrders : IQuery<Paged<OrderInfo>>
{
/// <summary>The paging information.</summary>
public PageInfo Paging { get; set; }
}
}
================================================
FILE: src/Contract/Queries/PageInfo.cs
================================================
namespace Contract.Queries
{
/// <summary>Object containing information about paging.</summary>
public class PageInfo
{
/// <summary>Returns a PageInfo that represents the request for a single page.</summary>
public static PageInfo SinglePage() => new PageInfo { PageIndex = 0, PageSize = -1 };
/// <summary>The 0-based page index.</summary>
public int PageIndex { get; set; }
/// <summary>The number of items in a page.</summary>
public int PageSize { get; set; } = 20;
/// <summary>Gets the value indicating whether the page info represents the request for a single page.</summary>
public bool IsSinglePage() => this.PageIndex == 0 && this.PageSize == -1;
}
}
================================================
FILE: src/Contract/Queries/Paged.cs
================================================
namespace Contract.Queries
{
using System.Runtime.Serialization;
// Applying the DataContract attribute to generic types prevents WCF from postfixing the closed-generic
// type name with a seemingly random hexadecimal code.
/// <summary>Contains a set of items for the requested page.</summary>
/// <typeparam name="T">The item type.</typeparam>
[DataContract(Name = nameof(Paged<T>) + "Of{0}")]
public class Paged<T>
{
/// <summary>Information about the requested page.</summary>
[DataMember] public PageInfo Paging { get; set; }
/// <summary>The list of items for the given page.</summary>
[DataMember] public T[] Items { get; set; }
}
}
================================================
FILE: src/Contract/Validators/CompositeValidationResult.cs
================================================
namespace Contract.Validators
{
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
/// <inheritdoc />
public class CompositeValidationResult : ValidationResult, IEnumerable<ValidationResult>
{
public CompositeValidationResult(string errorMessage, IEnumerable<ValidationResult> results)
: base(errorMessage)
{
this.Results = results;
}
public IEnumerable<ValidationResult> Results { get; }
public IEnumerator<ValidationResult> GetEnumerator() => this.Results.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
}
}
================================================
FILE: src/Contract/Validators/NonEmptyGuidAttribute.cs
================================================
namespace Contract.Validators
{
using System;
using System.ComponentModel.DataAnnotations;
/// <inheritdoc />
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class NonEmptyGuidAttribute : RequiredAttribute
{
/// <inheritdoc />
public override bool IsValid(object value)
{
if (value == null)
{
return false;
}
if (!(value is Guid))
{
return false;
}
return ((Guid)value) != Guid.Empty;
}
}
}
================================================
FILE: src/Contract/Validators/ValidateObjectAttribute.cs
================================================
namespace Contract.Validators
{
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
/// <inheritdoc />
public class ValidateObjectAttribute : ValidationAttribute
{
/// <inheritdoc />
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var context = new ValidationContext(value, null, null);
var results = new List<ValidationResult>();
Validator.TryValidateObject(value, context, results, validateAllProperties: true);
if (results.Count == 0)
{
return ValidationResult.Success;
}
return new CompositeValidationResult(
string.Format("Validation for {0} failed!", validationContext.DisplayName),
results);
}
}
}
================================================
FILE: src/Settings.StyleCop
================================================
<StyleCopSettings Version="105">
<GlobalSettings>
<StringProperty Name="MergeSettingsFiles">NoMerge</StringProperty>
</GlobalSettings>
<Analyzers>
<Analyzer AnalyzerId="StyleCop.CSharp.NamingRules">
<AnalyzerSettings>
<CollectionProperty Name="Hungarian">
<Value>is</Value>
</CollectionProperty>
</AnalyzerSettings>
</Analyzer>
<Analyzer AnalyzerId="StyleCop.CSharp.DocumentationRules">
<Rules>
<Rule Name="FileMustHaveHeader">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="DocumentationHeadersMustNotContainBlankLines">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementsMustBeDocumented">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="PartialElementsMustBeDocumented">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="EnumerationItemsMustBeDocumented">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="DocumentationMustContainValidXml">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementDocumentationMustHaveSummary">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="PartialElementDocumentationMustHaveSummary">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementDocumentationMustHaveSummaryText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="PartialElementDocumentationMustHaveSummaryText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementDocumentationMustNotHaveDefaultSummary">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementParametersMustBeDocumented">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementParameterDocumentationMustMatchElementParameters">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementParameterDocumentationMustDeclareParameterName">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementParameterDocumentationMustHaveText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementReturnValueMustBeDocumented">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementReturnValueDocumentationMustHaveText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="VoidReturnValueMustNotBeDocumented">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="GenericTypeParametersMustBeDocumented">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="GenericTypeParametersMustBeDocumentedPartialClass">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="GenericTypeParameterDocumentationMustMatchTypeParameters">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="GenericTypeParameterDocumentationMustDeclareParameterName">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="GenericTypeParameterDocumentationMustHaveText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="PropertySummaryDocumentationMustMatchAccessors">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="PropertySummaryDocumentationMustOmitSetAccessorWithRestrictedAccess">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ElementDocumentationMustNotBeCopiedAndPasted">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="SingleLineCommentsMustNotUseDocumentationStyleSlashes">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="DocumentationTextMustNotBeEmpty">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="DocumentationTextMustContainWhitespace">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="DocumentationMustMeetCharacterPercentage">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ConstructorSummaryDocumentationMustBeginWithStandardText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="DestructorSummaryDocumentationMustBeginWithStandardText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="IncludedDocumentationXPathDoesNotExist">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="IncludeNodeDoesNotContainValidFileAndPath">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="InheritDocMustBeUsedWithInheritingClass">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileHeaderMustShowCopyright">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileHeaderMustHaveCopyrightText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileHeaderMustContainFileName">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileHeaderFileNameDocumentationMustMatchFileName">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileHeaderMustHaveValidCompanyText">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="FileHeaderFileNameDocumentationMustMatchTypeName">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings>
<BooleanProperty Name="IgnorePrivates">True</BooleanProperty>
<BooleanProperty Name="IgnoreInternals">True</BooleanProperty>
</AnalyzerSettings>
</Analyzer>
<Analyzer AnalyzerId="StyleCop.CSharp.ReadabilityRules">
<Rules>
<Rule Name="ParameterMustFollowComma">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="SplitParametersMustStartOnLineAfterDeclaration">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ParametersMustBeOnSameLineOrSeparateLines">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
<Rule Name="ParameterMustNotSpanMultipleLines">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings />
</Analyzer>
<Analyzer AnalyzerId="StyleCop.CSharp.OrderingRules">
<Rules>
<Rule Name="StaticElementsMustAppearBeforeInstanceElements">
<RuleSettings>
<BooleanProperty Name="Enabled">False</BooleanProperty>
</RuleSettings>
</Rule>
</Rules>
<AnalyzerSettings />
</Analyzer>
</Analyzers>
</StyleCopSettings>
================================================
FILE: src/SolidServices.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31919.166
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{9F659AB9-6E1D-47EE-8DF8-EC7C593129D7}"
ProjectSection(SolutionItems) = preProject
Settings.StyleCop = Settings.StyleCop
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Client", "Client\Client.csproj", "{9562D251-49BE-4650-93BD-FA6D66DBA61C}"
ProjectSection(ProjectDependencies) = postProject
{CCFA6865-6B10-4D7C-B6B8-8C37BBFE7DA1} = {CCFA6865-6B10-4D7C-B6B8-8C37BBFE7DA1}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WcfService", "WcfService\WcfService.csproj", "{CCFA6865-6B10-4D7C-B6B8-8C37BBFE7DA1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Contract", "Contract\Contract.csproj", "{DDD88351-9A73-4212-85DC-F769B37D5057}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BusinessLayer", "BusinessLayer\BusinessLayer.csproj", "{5E9B468D-FD1B-4AE5-8A10-C9B09CE2690C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebApiService", "WebApiService\WebApiService.csproj", "{B71A8419-1827-48A4-913E-467B52A7C2F1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebCore3Service", "WebCore3Service\WebCore3Service.csproj", "{BB7A06CC-A96E-4B96-B8B6-2E96F3F20783}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebCore6Service", "WebCore6Service\WebCore6Service.csproj", "{1941CDB2-31FD-4DEC-B0A6-D3FA78431CC9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|Mixed Platforms = Debug|Mixed Platforms
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|Mixed Platforms = Release|Mixed Platforms
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9562D251-49BE-4650-93BD-FA6D66DBA61C}.Debug|Any CPU.ActiveCfg = Debug|x86
{9562D251-49BE-4650-93BD-FA6D66DBA61C}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{9562D251-49BE-4650-93BD-FA6D66DBA61C}.Debug|Mixed Platforms.Build.0 = Debug|x86
{9562D251-49BE-4650-93BD-FA6D66DBA61C}.Debug|x86.ActiveCfg = Debug|x86
{9562D251-49BE-4650-93BD-FA6D66DBA61C}.Debug|x86.Build.0 = Debug|x86
{9562D251-49BE-4650-93BD-FA6D66DBA61C}.Release|Any CPU.ActiveCfg = Release|x86
{9562D251-49BE-4650-93BD-FA6D66DBA61C}.Release|Mixed Platforms.ActiveCfg = Release|x86
{9562D251-49BE-4650-93BD-FA6D66DBA61C}.Release|Mixed Platforms.Build.0 = Release|x86
{9562D251-49BE-4650-93BD-FA6D66DBA61C}.Release|x86.ActiveCfg = Release|x86
{9562D251-49BE-4650-93BD-FA6D66DBA61C}.Release|x86.Build.0 = Release|x86
{CCFA6865-6B10-4D7C-B6B8-8C37BBFE7DA1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CCFA6865-6B10-4D7C-B6B8-8C37BBFE7DA1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CCFA6865-6B10-4D7C-B6B8-8C37BBFE7DA1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{CCFA6865-6B10-4D7C-B6B8-8C37BBFE7DA1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{CCFA6865-6B10-4D7C-B6B8-8C37BBFE7DA1}.Debug|x86.ActiveCfg = Debug|Any CPU
{CCFA6865-6B10-4D7C-B6B8-8C37BBFE7DA1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CCFA6865-6B10-4D7C-B6B8-8C37BBFE7DA1}.Release|Any CPU.Build.0 = Release|Any CPU
{CCFA6865-6B10-4D7C-B6B8-8C37BBFE7DA1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{CCFA6865-6B10-4D7C-B6B8-8C37BBFE7DA1}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{CCFA6865-6B10-4D7C-B6B8-8C37BBFE7DA1}.Release|x86.ActiveCfg = Release|Any CPU
{DDD88351-9A73-4212-85DC-F769B37D5057}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DDD88351-9A73-4212-85DC-F769B37D5057}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DDD88351-9A73-4212-85DC-F769B37D5057}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{DDD88351-9A73-4212-85DC-F769B37D5057}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{DDD88351-9A73-4212-85DC-F769B37D5057}.Debug|x86.ActiveCfg = Debug|Any CPU
{DDD88351-9A73-4212-85DC-F769B37D5057}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DDD88351-9A73-4212-85DC-F769B37D5057}.Release|Any CPU.Build.0 = Release|Any CPU
{DDD88351-9A73-4212-85DC-F769B37D5057}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{DDD88351-9A73-4212-85DC-F769B37D5057}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{DDD88351-9A73-4212-85DC-F769B37D5057}.Release|x86.ActiveCfg = Release|Any CPU
{5E9B468D-FD1B-4AE5-8A10-C9B09CE2690C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5E9B468D-FD1B-4AE5-8A10-C9B09CE2690C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5E9B468D-FD1B-4AE5-8A10-C9B09CE2690C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{5E9B468D-FD1B-4AE5-8A10-C9B09CE2690C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{5E9B468D-FD1B-4AE5-8A10-C9B09CE2690C}.Debug|x86.ActiveCfg = Debug|Any CPU
{5E9B468D-FD1B-4AE5-8A10-C9B09CE2690C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5E9B468D-FD1B-4AE5-8A10-C9B09CE2690C}.Release|Any CPU.Build.0 = Release|Any CPU
{5E9B468D-FD1B-4AE5-8A10-C9B09CE2690C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{5E9B468D-FD1B-4AE5-8A10-C9B09CE2690C}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{5E9B468D-FD1B-4AE5-8A10-C9B09CE2690C}.Release|x86.ActiveCfg = Release|Any CPU
{B71A8419-1827-48A4-913E-467B52A7C2F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B71A8419-1827-48A4-913E-467B52A7C2F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B71A8419-1827-48A4-913E-467B52A7C2F1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{B71A8419-1827-48A4-913E-467B52A7C2F1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{B71A8419-1827-48A4-913E-467B52A7C2F1}.Debug|x86.ActiveCfg = Debug|Any CPU
{B71A8419-1827-48A4-913E-467B52A7C2F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B71A8419-1827-48A4-913E-467B52A7C2F1}.Release|Any CPU.Build.0 = Release|Any CPU
{B71A8419-1827-48A4-913E-467B52A7C2F1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{B71A8419-1827-48A4-913E-467B52A7C2F1}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{B71A8419-1827-48A4-913E-467B52A7C2F1}.Release|x86.ActiveCfg = Release|Any CPU
{BB7A06CC-A96E-4B96-B8B6-2E96F3F20783}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{BB7A06CC-A96E-4B96-B8B6-2E96F3F20783}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BB7A06CC-A96E-4B96-B8B6-2E96F3F20783}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{BB7A06CC-A96E-4B96-B8B6-2E96F3F20783}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{BB7A06CC-A96E-4B96-B8B6-2E96F3F20783}.Debug|x86.ActiveCfg = Debug|Any CPU
{BB7A06CC-A96E-4B96-B8B6-2E96F3F20783}.Debug|x86.Build.0 = Debug|Any CPU
{BB7A06CC-A96E-4B96-B8B6-2E96F3F20783}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BB7A06CC-A96E-4B96-B8B6-2E96F3F20783}.Release|Any CPU.Build.0 = Release|Any CPU
{BB7A06CC-A96E-4B96-B8B6-2E96F3F20783}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{BB7A06CC-A96E-4B96-B8B6-2E96F3F20783}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{BB7A06CC-A96E-4B96-B8B6-2E96F3F20783}.Release|x86.ActiveCfg = Release|Any CPU
{BB7A06CC-A96E-4B96-B8B6-2E96F3F20783}.Release|x86.Build.0 = Release|Any CPU
{1941CDB2-31FD-4DEC-B0A6-D3FA78431CC9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1941CDB2-31FD-4DEC-B0A6-D3FA78431CC9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1941CDB2-31FD-4DEC-B0A6-D3FA78431CC9}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{1941CDB2-31FD-4DEC-B0A6-D3FA78431CC9}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{1941CDB2-31FD-4DEC-B0A6-D3FA78431CC9}.Debug|x86.ActiveCfg = Debug|Any CPU
{1941CDB2-31FD-4DEC-B0A6-D3FA78431CC9}.Debug|x86.Build.0 = Debug|Any CPU
{1941CDB2-31FD-4DEC-B0A6-D3FA78431CC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1941CDB2-31FD-4DEC-B0A6-D3FA78431CC9}.Release|Any CPU.Build.0 = Release|Any CPU
{1941CDB2-31FD-4DEC-B0A6-D3FA78431CC9}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{1941CDB2-31FD-4DEC-B0A6-D3FA78431CC9}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{1941CDB2-31FD-4DEC-B0A6-D3FA78431CC9}.Release|x86.ActiveCfg = Release|Any CPU
{1941CDB2-31FD-4DEC-B0A6-D3FA78431CC9}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {57B78032-8930-4257-B608-F0610294E5DE}
EndGlobalSection
EndGlobal
================================================
FILE: src/WcfService/Bootstrapper.cs
================================================
namespace WcfService
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Security.Principal;
using System.Threading;
using BusinessLayer;
using SimpleInjector;
using WcfService.Code;
using WcfService.CrossCuttingConcerns;
public static class Bootstrapper
{
private static Container container;
public static object GetCommandHandler(Type commandType) =>
container.GetInstance(typeof(ICommandHandler<>).MakeGenericType(commandType));
public static object GetQueryHandler(Type queryType) =>
container.GetInstance(BusinessLayerBootstrapper.CreateQueryHandlerType(queryType));
public static IEnumerable<Type> GetCommandTypes() => BusinessLayerBootstrapper.GetCommandTypes();
public static IEnumerable<Type> GetQueryAndResultTypes()
{
var queryTypes = BusinessLayerBootstrapper.GetQueryTypes().Select(q => q.QueryType);
var resultTypes = BusinessLayerBootstrapper.GetQueryTypes().Select(q => q.ResultType).Distinct();
return queryTypes.Concat(resultTypes);
}
public static void Bootstrap()
{
container = new Container();
BusinessLayerBootstrapper.Bootstrap(container);
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(ToWcfFaultTranslatorCommandHandlerDecorator<>));
container.RegisterWcfServices(Assembly.GetExecutingAssembly());
RegisterWcfSpecificDependencies();
container.Verify();
}
public static void Log(Exception ex)
{
Debug.WriteLine(ex.ToString());
}
private static void RegisterWcfSpecificDependencies()
{
container.RegisterInstance<ILogger>(new DebugLogger());
container.Register<IPrincipal>(() => Thread.CurrentPrincipal);
}
}
}
================================================
FILE: src/WcfService/Code/DebugLogger.cs
================================================
namespace WcfService.Code
{
using System.Diagnostics;
using BusinessLayer;
public sealed class DebugLogger : ILogger
{
public void Log(string message)
{
Debug.WriteLine(message);
}
}
}
================================================
FILE: src/WcfService/Code/WcfExceptionTranslator.cs
================================================
namespace WcfService.Code
{
using System;
using System.ComponentModel.DataAnnotations;
using System.ServiceModel;
public static class WcfExceptionTranslator
{
public static FaultException CreateFaultExceptionOrNull(Exception exception)
{
if (exception is ValidationException)
{
return new FaultException<ValidationError>(
new ValidationError { ErrorMessage = exception.Message }, exception.Message);
}
#if DEBUG
return new FaultException(exception.ToString());
#else
return null;
#endif
}
}
}
================================================
FILE: src/WcfService/CommandService.svc
================================================
<%@ ServiceHost
Language="C#"
Debug="true"
Service="WcfService.CommandService"
CodeBehind="CommandService.svc.cs"
%>
================================================
FILE: src/WcfService/CommandService.svc.cs
================================================
namespace WcfService
{
using System;
using System.Collections.Generic;
using System.Reflection;
using System.ServiceModel;
using Code;
[ServiceContract(Namespace = "http://www.solid.net/commandservice/v1.0")]
[ServiceKnownType(nameof(GetKnownTypes))]
public class CommandService
{
public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider) =>
Bootstrapper.GetCommandTypes();
[OperationContract]
[FaultContract(typeof(ValidationError))]
public void Execute(dynamic command)
{
try
{
dynamic commandHandler = Bootstrapper.GetCommandHandler(command.GetType());
commandHandler.Handle(command);
}
catch (Exception ex)
{
Bootstrapper.Log(ex);
var faultException = WcfExceptionTranslator.CreateFaultExceptionOrNull(ex);
if (faultException != null)
{
throw faultException;
}
throw;
}
}
}
}
================================================
FILE: src/WcfService/CrossCuttingConcerns/ToWcfFaultTranslatorCommandHandlerDecorator.cs
================================================
namespace WcfService.CrossCuttingConcerns
{
using System.ComponentModel.DataAnnotations;
using System.ServiceModel;
using BusinessLayer;
using Contract;
public class ToWcfFaultTranslatorCommandHandlerDecorator<TCommand> : ICommandHandler<TCommand>
where TCommand : ICommand
{
private readonly ICommandHandler<TCommand> decoratee;
public ToWcfFaultTranslatorCommandHandlerDecorator(ICommandHandler<TCommand> decoratee)
{
this.decoratee = decoratee;
}
public void Handle(TCommand command)
{
try
{
this.decoratee.Handle(command);
}
catch (ValidationException ex)
{
// This ensures that validation errors are communicated to the client,
// while other exceptions are filtered by WCF (if configured correctly).
throw new FaultException(ex.Message, new FaultCode("ValidationError"));
}
}
}
}
================================================
FILE: src/WcfService/Global.asax
================================================
<%@ Application Codebehind="Global.asax.cs" Inherits="WcfService.Global" Language="C#" %>
================================================
FILE: src/WcfService/Global.asax.cs
================================================
namespace WcfService
{
using System;
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
Bootstrapper.Bootstrap();
}
}
}
================================================
FILE: src/WcfService/NonDotNetQueryService.cs
================================================
================================================
FILE: src/WcfService/NonDotNetQueryService.svc
================================================
<%@ ServiceHost Language="C#" Debug="true" Service="WcfService.NonDotNetQueryService" CodeBehind="NonDotNetQueryService.cs" %>
================================================
FILE: src/WcfService/NonDotNetQueryService.tt
================================================
<#
/*
Generates a service class for queries to be consumed by non-.NET clients.
*/
#>
<#@ template language="C#" debug="true" hostspecific="true" #>
<#@ assembly name="System.Core" #>
<#@ assembly name="Microsoft.VisualStudio.Shell.Interop.8.0" #>
<#@ assembly name="EnvDTE" #>
<#@ assembly name="EnvDTE80" #>
<#@ assembly name="VSLangProj" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="Microsoft.VisualStudio.Shell.Interop" #>
<#@ import namespace="EnvDTE" #>
<#@ import namespace="EnvDTE80" #>
<#@ import namespace="Microsoft.VisualStudio.TextTemplating" #>
<#@ output extension=".cs" #>
<#
// To debug, uncomment the next two lines !!
// System.Diagnostics.Debugger.Launch();
// System.Diagnostics.Debugger.Break();
Initialize(this);
var queryTypes = GetAllQueryTypes(ContractProject).ToArray();
var referencedNamespaces = GetAllReferencedNamespaces(queryTypes);
#>
// <auto-generated />
#pragma warning disable 1591
namespace WcfService
{
using System.ServiceModel;
using Contract;
<# foreach (var referencedNamespace in referencedNamespaces) { #>
using <#=referencedNamespace #>;
<#} #>
[ServiceContract(Namespace = "http://www.cuttingedge.it/solid/queryservice/v1.0")]
public class NonDotNetQueryService
{
<#
foreach (var queryType in queryTypes)
{
var @interface = GetQueryInterfaceForCodeClass(queryType);
var resultType = GetQueryResultType(@interface);
#> [OperationContract]
[FaultContract(typeof(ValidationError))]
public <#= resultType #> <#= queryType.Name.Replace("Query", "") #>(<#= queryType.Name #> query) => Execute(query);
<# } #>
private static TResult Execute<TResult>(IQuery<TResult> query) => (TResult)QueryService.ExecuteQuery(query);
}
}
<#+
const string QueryInterface = "IQuery";
static DTE Dte;
static Project CurrentProject;
static Project ContractProject;
static TextTransformation TT;
static string T4FileName;
static string T4Folder;
// static string GeneratedCode = @"GeneratedCode(""SolidServices"", ""1.0"")";
static Microsoft.CSharp.CSharpCodeProvider codeProvider = new Microsoft.CSharp.CSharpCodeProvider();
void Initialize(TextTransformation tt) {
TT = tt;
T4FileName = Path.GetFileName(Host.TemplateFile);
T4Folder = Path.GetDirectoryName(Host.TemplateFile);
// Get the DTE service from the host
var serviceProvider = Host as IServiceProvider;
if (serviceProvider != null) {
Dte = serviceProvider.GetService(typeof(SDTE)) as DTE;
}
// Fail if we couldn't get the DTE. This can happen when trying to run in TextTransform.exe
if (Dte == null) {
throw new Exception("This template can only be executed through the Visual Studio host");
}
CurrentProject = GetProjectContainingT4File(Dte);
ContractProject = GetContractProject(Dte);
}
Project GetProjectContainingT4File(DTE dte) {
// Find the .tt file's ProjectItem
ProjectItem projectItem = dte.Solution.FindProjectItem(Host.TemplateFile);
// If the .tt file is not opened, open it
if (projectItem.Document == null)
projectItem.Open(Constants.vsViewKindCode);
// Mark the .tt file as unsaved. This way it will be saved and update itself next time the
// project is built. Basically, it keeps marking itself as unsaved to make the next build work.
// Note: this is certainly hacky, but is the best I could come up with so far.
projectItem.Document.Saved = false;
return projectItem.ContainingProject;
}
Project GetContractProject(DTE dte)
{
string queryCsFile = QueryInterface + ".cs";
ProjectItem projectItem = dte.Solution.FindProjectItem(queryCsFile);
if (projectItem == null) {
Error("Could not find the VS Project containing the " + queryCsFile + " file.");
return null;
}
return projectItem.ContainingProject;
}
IEnumerable<CodeClass2> GetAllQueryTypes(params Project[] projects)
{
return
from Project project in projects
from ProjectItem projectItem in project.ProjectItems
from type in GetAllQueryTypesRecursive(projectItem)
select type;
}
IEnumerable<CodeClass2> GetAllQueryTypesRecursive(ProjectItem projectItem)
{
var queryTypes = GetAllQueryTypes(projectItem);
var recursiveQueryTypes =
from ProjectItem subItem in projectItem.ProjectItems
from type in GetAllQueryTypesRecursive(subItem)
select type;
return queryTypes.Union(recursiveQueryTypes);
}
IEnumerable<CodeClass2> GetAllQueryTypes(ProjectItem projectItem)
{
if (projectItem.FileCodeModel == null)
{
return Enumerable.Empty<CodeClass2>();
}
var elements = projectItem.FileCodeModel.CodeElements.OfType<object>().ToArray();
var namespacedTypes =
from @namespace in projectItem.FileCodeModel.CodeElements.OfType<CodeNamespace>()
from type in @namespace.Members.OfType<CodeClass2>()
select type;
var rootTypes = projectItem.FileCodeModel.CodeElements.OfType<CodeClass2>();
return
from type in rootTypes.Union(namespacedTypes)
where ImplementsQueryInterface(type)
select type;
}
private string[] GetAllReferencedNamespaces(IEnumerable<CodeClass2> queryTypes)
{
return (
from type in queryTypes
orderby type.Namespace.Name
select type.Namespace.Name)
.Distinct()
.ToArray();
}
private bool ImplementsQueryInterface(CodeClass2 type)
{
return GetQueryInterfaceForCodeClass(type) != null;
}
private CodeInterface GetQueryInterfaceForCodeClass(CodeClass2 type)
{
var queryInterfaces =
from implementedInterface in type.ImplementedInterfaces.OfType<CodeInterface>()
where implementedInterface.Name.StartsWith(QueryInterface)
select implementedInterface;
return queryInterfaces.FirstOrDefault();
}
private static readonly int iqueryTypeNameLength = "Contract.IQuery".Length;
private static string GetQueryResultType(CodeInterface queryType)
{
return queryType.FullName.Substring(iqueryTypeNameLength + 1, queryType.FullName.Length - (iqueryTypeNameLength + 2));
}
#>
================================================
FILE: src/WcfService/Properties/AssemblyInfo.cs
================================================
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("WcfService")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("WcfService")]
[assembly: AssemblyCopyright("Copyright © 2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("7f49468b-8e26-4e45-8d87-2de4eccc022b")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
================================================
FILE: src/WcfService/QueryService.svc
================================================
<%@ ServiceHost Language="C#" Debug="true" Service="WcfService.QueryService" CodeBehind="QueryService.svc.cs" %>
================================================
FILE: src/WcfService/QueryService.svc.cs
================================================
namespace WcfService
{
using System;
using System.Collections.Generic;
using System.Reflection;
using System.ServiceModel;
using Code;
[ServiceContract(Namespace = "http://www.cuttingedge.it/solid/queryservice/v1.0")]
[ServiceKnownType(nameof(GetKnownTypes))]
public class QueryService
{
public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider provider) =>
Bootstrapper.GetQueryAndResultTypes();
[OperationContract]
[FaultContract(typeof(ValidationError))]
public object Execute(dynamic query) => ExecuteQuery(query);
internal static object ExecuteQuery(dynamic query)
{
Type queryType = query.GetType();
dynamic queryHandler = Bootstrapper.GetQueryHandler(query.GetType());
try
{
return queryHandler.Handle(query);
}
catch (Exception ex)
{
Bootstrapper.Log(ex);
var faultException = WcfExceptionTranslator.CreateFaultExceptionOrNull(ex);
if (faultException != null)
{
throw faultException;
}
throw;
}
}
}
}
================================================
FILE: src/WcfService/ValidationError.cs
================================================
namespace WcfService
{
public class ValidationError
{
public string ErrorMessage { get; set; }
}
}
================================================
FILE: src/WcfService/WcfService.csproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>
</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{CCFA6865-6B10-4D7C-B6B8-8C37BBFE7DA1}</ProjectGuid>
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>WcfService</RootNamespace>
<AssemblyName>WcfService</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<UseIISExpress>true</UseIISExpress>
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>4.0</OldToolsVersion>
<IISExpressSSLPort />
<IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
<UseGlobalApplicationHostFile />
<TargetFrameworkProfile />
<Use64BitIISExpress />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.1.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
</Reference>
<Reference Include="SimpleInjector, Version=5.0.0.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
<HintPath>..\packages\SimpleInjector.5.3.2\lib\net461\SimpleInjector.dll</HintPath>
</Reference>
<Reference Include="SimpleInjector.Integration.Wcf, Version=5.0.0.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
<HintPath>..\packages\SimpleInjector.Integration.Wcf.5.0.0\lib\net45\SimpleInjector.Integration.Wcf.dll</HintPath>
</Reference>
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.ServiceModel.Activation" />
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.Web.DynamicData" />
<Reference Include="System.Web.Entity" />
<Reference Include="System.Web.ApplicationServices" />
<Reference Include="System" />
<Reference Include="System.Configuration" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.EnterpriseServices" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.ServiceModel" />
<Reference Include="System.ServiceModel.Web" />
<Reference Include="System.Web" />
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Web.Services" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="WebActivator">
<HintPath>..\packages\WebActivator.1.4.4\lib\net40\WebActivator.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Content Include="CommandService.svc" />
<Content Include="Global.asax" />
<Content Include="NonDotNetQueryService.svc" />
<Content Include="QueryService.svc" />
<Content Include="Web.config" />
<Content Include="Web.Debug.config">
<DependentUpon>Web.config</DependentUpon>
</Content>
<Content Include="Web.Release.config">
<DependentUpon>Web.config</DependentUpon>
</Content>
</ItemGroup>
<ItemGroup>
<Compile Include="Code\WcfExceptionTranslator.cs" />
<Compile Include="CommandService.svc.cs">
<DependentUpon>CommandService.svc</DependentUpon>
</Compile>
<Compile Include="Bootstrapper.cs" />
<Compile Include="Code\DebugLogger.cs" />
<Compile Include="CrossCuttingConcerns\ToWcfFaultTranslatorCommandHandlerDecorator.cs" />
<Compile Include="Global.asax.cs">
<DependentUpon>Global.asax</DependentUpon>
</Compile>
<Compile Include="NonDotNetQueryService.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>NonDotNetQueryService.tt</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="QueryService.svc.cs">
<DependentUpon>QueryService.svc</DependentUpon>
</Compile>
<Compile Include="ValidationError.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="NonDotNetQueryService.tt">
<Generator>TextTemplatingFileGenerator</Generator>
<LastGenOutput>NonDotNetQueryService.cs</LastGenOutput>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BusinessLayer\BusinessLayer.csproj">
<Project>{5E9B468D-FD1B-4AE5-8A10-C9B09CE2690C}</Project>
<Name>BusinessLayer</Name>
</ProjectReference>
<ProjectReference Include="..\Contract\Contract.csproj">
<Project>{DDD88351-9A73-4212-85DC-F769B37D5057}</Project>
<Name>Contract</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>False</AutoAssignPort>
<DevelopmentServerPort>9999</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:9998/</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
</CustomServerUrl>
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
</WebProjectProperties>
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
================================================
FILE: src/WcfService/Web.Debug.config
================================================
<?xml version="1.0"?>
<!-- For more information on using web.config transformation visit http://go.microsoft.com/fwlink/?LinkId=125889 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
</configuration>
================================================
FILE: src/WcfService/Web.Release.config
================================================
<?xml version="1.0"?>
<!-- For more information on using web.config transformation visit http://go.microsoft.com/fwlink/?LinkId=125889 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<system.web>
<compilation xdt:Transform="RemoveAttributes(debug)" />
</system.web>
</configuration>
================================================
FILE: src/WcfService/Web.config
================================================
<?xml version="1.0"?>
<configuration>
<!--
For a description of web.config changes see http://go.microsoft.com/fwlink/?LinkId=235367.
The following attributes can be set on the <httpRuntime> tag.
<system.Web>
<httpRuntime targetFramework="4.8" />
</system.Web>
-->
<system.web>
<compilation debug="true" targetFramework="4.8"/>
<pages controlRenderingCompatibilityVersion="4.0"/>
</system.web>
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
<serviceMetadata httpGetEnabled="true"/>
<!-- To receive exception details in faults for debugging purposes, set the value below to true. Set to false before deployment to avoid disclosing exception information -->
<serviceDebug includeExceptionDetailInFaults="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
<diagnostics>
<messageLogging logEntireMessage="true" logMalformedMessages="false" logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="false"/>
</diagnostics>
</system.serviceModel>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true"/>
</system.webServer>
<system.diagnostics>
<sources>
<source name="System.ServiceModel" switchValue="Verbose">
<listeners>
<add name="xml"/>
</listeners>
</source>
<source name="System.ServiceModel.MessageLogging" switchValue="Verbose, ActivityTracing">
<listeners>
<add name="xml"/>
</listeners>
</source>
<source name="System.Runtime.Serialization" switchValue="Verbose">
<listeners>
<add name="xml"/>
</listeners>
</source>
</sources>
<sharedListeners>
<add name="xml" type="System.Diagnostics.XmlWriterTraceListener" traceOutputOptions="LogicalOperationStack" initializeData="C:\temp\solidservices.log"/>
</sharedListeners>
<trace autoflush="true"/>
</system.diagnostics>
</configuration>
================================================
FILE: src/WcfService/packages.config
================================================
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Bcl.AsyncInterfaces" version="1.0.0" targetFramework="net48" />
<package id="SimpleInjector" version="5.3.2" targetFramework="net48" />
<package id="SimpleInjector.Integration.Wcf" version="5.0.0" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensions" version="4.5.2" targetFramework="net48" />
</packages>
================================================
FILE: src/WebApiService/App_Data/.gitignore
================================================
*.xml
================================================
FILE: src/WebApiService/App_Start/FilterConfig.cs
================================================
namespace WebApiService
{
using System.Web.Mvc;
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
}
}
}
================================================
FILE: src/WebApiService/App_Start/RouteConfig.cs
================================================
namespace WebApiService
{
using System.Web.Mvc;
using System.Web.Routing;
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional });
}
}
}
================================================
FILE: src/WebApiService/App_Start/SwaggerConfig.cs
================================================
[assembly: System.Web.PreApplicationStartMethod(typeof(WebApiService.SwaggerConfig), "Register")]
namespace WebApiService
{
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Web.Hosting;
using System.Web.Http;
using System.Web.Http.Description;
using Contract.Commands.Orders;
using SolidServices.Controllerless.WebApi.Description;
using Swashbuckle.Application;
using Swashbuckle.Swagger;
// NOTE: To see Swagger in action, view this Web API in your browser: http://localhost:2591/swagger/
public static class SwaggerConfig
{
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
var contractAssembly = typeof(CreateOrder).Assembly;
GlobalConfiguration.Configuration.EnableSwagger(c =>
{
c.SingleApiVersion("v1", "SOLID Services API");
IncludeXmlCommentsFromAppDataFolder(c);
})
.EnableSwaggerUi(c => { });
}
private static void IncludeXmlCommentsFromAppDataFolder(SwaggerDocsConfig c)
{
var appDataPath = HostingEnvironment.MapPath("~/App_Data");
// The XML comment files are copied using a post-build event (see project settings / Build Events).
string[] xmlCommentsPaths = Directory.GetFiles(appDataPath, "*.xml");
foreach (string xmlCommentsPath in xmlCommentsPaths)
{
c.IncludeXmlComments(xmlCommentsPath);
}
var filter = new ControllerlessActionOperationFilter(xmlCommentsPaths);
c.OperationFilter(() => filter);
if (!xmlCommentsPaths.Any())
{
throw new ConfigurationErrorsException("No .xml files were found in the App_Data folder.");
}
}
private sealed class ControllerlessActionOperationFilter : IOperationFilter
{
private readonly ITypeDescriptionProvider[] providers;
public ControllerlessActionOperationFilter(params string[] xmlCommentsPaths)
{
this.providers = xmlCommentsPaths.Select(p => new XmlDocumentationTypeDescriptionProvider(p)).ToArray();
}
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
var descriptor = apiDescription.ActionDescriptor as ControllerlessActionDescriptor;
if (descriptor != null)
{
operation.summary = this.GetSummaries(descriptor.MessageType).FirstOrDefault() ?? operation.summary;
}
}
private IEnumerable<string> GetSummaries(Type type) =>
from provider in providers
let description = provider.GetDescription(type)
where description != null
select description;
}
}
}
================================================
FILE: src/WebApiService/App_Start/WebApiConfig.cs
================================================
namespace WebApiService
{
using System.Linq;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Cors;
using System.Web.Http.Description;
using BusinessLayer;
using Code;
using Newtonsoft.Json.Serialization;
using SimpleInjector;
using SolidServices.Controllerless.WebApi.Description;
public static class WebApiConfig
{
public static void Register(HttpConfiguration config, Container container)
{
// Setting the same-origin policy to 'unrestricted'. Remove or change this line if you want to
// restrict web pages from making AJAX requests to other domains. For more information, see:
// https://docs.microsoft.com/en-us/aspnet/web-api/overview/security/enabling-cross-origin-requests-in-web-api#scope-rules-for-enablecors
config.EnableCors(new EnableCorsAttribute(origins: "*", headers: "*", methods: "*"));
UseCamelCaseJsonSerialization(config);
#if DEBUG
UseIndentJsonSerialization(config);
#endif
MapRoutes(config, container);
UseControllerlessApiDocumentation(config);
}
private static void MapRoutes(HttpConfiguration config, Container container)
{
config.Routes.MapHttpRoute(
name: "QueryApi",
routeTemplate: "api/queries/{query}",
defaults: new { },
constraints: new { },
handler: new QueryDelegatingHandler(
handlerFactory: container.GetInstance,
queryTypes: Bootstrapper.GetKnownQueryTypes()));
config.Routes.MapHttpRoute(
name: "CommandApi",
routeTemplate: "api/commands/{command}",
defaults: new { },
constraints: new { },
handler: new CommandDelegatingHandler(
handlerFactory: container.GetInstance,
commandTypes: Bootstrapper.GetKnownCommandTypes()));
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional });
}
private static void UseControllerlessApiDocumentation(HttpConfiguration config)
{
var queryApiExplorer = new ControllerlessApiExplorer(
messageTypes: Bootstrapper.GetKnownQueryTypes().Select(t => t.QueryType),
responseTypeSelector: type => BusinessLayerBootstrapper.GetQueryResultType(type))
{
ControllerName = "queries",
ParameterSourceSelector = type => ApiParameterSource.FromUri,
HttpMethodSelector = type => HttpMethod.Get,
ActionNameSelector = type => type.ToFriendlyName()
};
var commandApiExplorer = new ControllerlessApiExplorer(
messageTypes: Bootstrapper.GetKnownCommandTypes(),
responseTypeSelector: type => typeof(void))
{
ControllerName = "commands",
ParameterName = "command",
ActionNameSelector = type => type.ToFriendlyName(),
};
config.Services.Replace(typeof(IApiExplorer),
new CompositeApiExplorer(
config.Services.GetApiExplorer(),
commandApiExplorer,
queryApiExplorer));
}
private static void UseCamelCaseJsonSerialization(HttpConfiguration config)
{
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver =
new CamelCasePropertyNamesContractResolver();
}
private static void UseIndentJsonSerialization(HttpConfiguration config)
{
config.Formatters.JsonFormatter.Indent = true;
}
}
}
================================================
FILE: src/WebApiService/Bootstrapper.cs
================================================
namespace WebApiService
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Security.Principal;
using System.Threading;
using System.Web;
using BusinessLayer;
using SimpleInjector;
using SimpleInjector.Lifestyles;
// NOTE: Here are two example urls for queries:
// * http://localhost:2591/api/queries/GetUnshippedOrdersForCurrentCustomer?Paging.PageIndex=3&Paging.PageSize=10
// * http://localhost:2591/api/queries/GetOrderById?OrderId=97fc6660-283d-44b6-b170-7db0c2e2afae
public static class Bootstrapper
{
public static IEnumerable<Type> GetKnownCommandTypes() => BusinessLayerBootstrapper.GetCommandTypes();
public static IEnumerable<(Type QueryType, Type ResultType)> GetKnownQueryTypes() =>
BusinessLayerBootstrapper.GetQueryTypes();
public static Container Bootstrap()
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
BusinessLayerBootstrapper.Bootstrap(container);
container.RegisterInstance<IPrincipal>(new HttpContextPrincipal());
container.RegisterInstance<ILogger>(new DebugLogger());
return container;
}
private sealed class HttpContextPrincipal : IPrincipal
{
public IIdentity Identity => this.Principal.Identity;
private IPrincipal Principal => HttpContext.Current.User ?? Thread.CurrentPrincipal;
public bool IsInRole(string role) => this.Principal.IsInRole(role);
}
private sealed class DebugLogger : ILogger
{
public void Log(string message)
{
Debug.WriteLine(message);
}
}
}
}
================================================
FILE: src/WebApiService/Code/CommandDelegatingHandler.cs
================================================
namespace WebApiService.Code
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Threading;
using System.Threading.Tasks;
using BusinessLayer;
using Newtonsoft.Json;
using SimpleInjector;
public sealed class CommandDelegatingHandler : DelegatingHandler
{
private readonly Func<Type, object> handlerFactory;
private readonly Dictionary<string, Type> commandTypes;
public CommandDelegatingHandler(Func<Type, object> handlerFactory, IEnumerable<Type> commandTypes)
{
this.handlerFactory = handlerFactory;
this.commandTypes = commandTypes.ToDictionary(
keySelector: type => type.ToFriendlyName(),
elementSelector: type => type,
comparer: StringComparer.OrdinalIgnoreCase);
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
string commandName = request.GetRouteData().Values["command"].ToString();
if (request.Method != HttpMethod.Post)
{
return request.CreateErrorResponse(HttpStatusCode.MethodNotAllowed,
"The requested resource does not support HTTP method '" + request.Method + "'.");
}
if (!this.commandTypes.ContainsKey(commandName))
{
return new HttpResponseMessage { StatusCode = HttpStatusCode.NotFound, RequestMessage = request };
}
Type commandType = this.commandTypes[commandName];
string commandData = await request.Content.ReadAsStringAsync();
Type handlerType = typeof(ICommandHandler<>).MakeGenericType(commandType);
// GetDependencyScope() calls IDependencyResolver.BeginScope internally.
request.GetDependencyScope();
this.ApplyHeaders(request);
dynamic handler = this.handlerFactory.Invoke(handlerType);
try
{
dynamic command = DeserializeCommand(request, commandData, commandType);
handler.Handle(command);
return new HttpResponseMessage { StatusCode = HttpStatusCode.OK, RequestMessage = request };
}
catch (Exception ex)
{
var response = WebApiErrorResponseBuilder.CreateErrorResponseOrNull(ex, request);
if (response != null)
{
return response;
}
throw;
}
}
private void ApplyHeaders(HttpRequestMessage request)
{
// TODO: Here you read the relevant headers and and check them or apply them to the current scope
// so the values are accessible during execution of the command.
string sessionId = request.Headers.GetValueOrNull("sessionId");
string token = request.Headers.GetValueOrNull("CSRF-token");
}
private static object DeserializeCommand(HttpRequestMessage request, string json, Type commandType) =>
JsonConvert.DeserializeObject(json, commandType, GetJsonFormatter(request).SerializerSettings);
private static JsonMediaTypeFormatter GetJsonFormatter(HttpRequestMessage request) =>
request.GetConfiguration().Formatters.JsonFormatter;
}
}
================================================
FILE: src/WebApiService/Code/ExampleObjectCreator.cs
================================================
namespace WebApiService.Code
{
using System;
using AutoFixture;
using AutoFixture.Kernel;
public static class ExampleObjectCreator
{
public static object Create(Type type)
{
var fixture = new Fixture();
int index = 1;
fixture.Register(() => "sample text " + index++);
return new SpecimenContext(fixture).Resolve(type);
}
}
}
================================================
FILE: src/WebApiService/Code/QueryDelegatingHandler.cs
================================================
namespace WebApiService.Code
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Formatting;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using Contract;
using Newtonsoft.Json;
public sealed class QueryDelegatingHandler : DelegatingHandler
{
private readonly Func<Type, object> handlerFactory;
private readonly Dictionary<string, (Type QueryType, Type ResultType)> queryTypes;
public QueryDelegatingHandler(
Func<Type, object> handlerFactory, IEnumerable<(Type QueryType, Type ResultType)> queryTypes)
{
this.handlerFactory = handlerFactory;
this.queryTypes = queryTypes.ToDictionary(
keySelector: info => info.QueryType.Name.Replace("Query", string.Empty),
elementSelector: info => info,
comparer: StringComparer.OrdinalIgnoreCase);
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
string queryName = request.GetRouteData().Values["query"].ToString();
if (!this.queryTypes.ContainsKey(queryName))
{
return new HttpResponseMessage { StatusCode = HttpStatusCode.NotFound, RequestMessage = request };
}
// GET operations get their data through the query string, while POST operations expect a JSON
// object being put in the body.
string queryData = request.Method == HttpMethod.Get
? SerializationHelpers.ConvertQueryStringToJson(request.RequestUri.Query)
: await request.Content.ReadAsStringAsync();
var (queryType, resultType) = this.queryTypes[queryName];
Type handlerType = typeof(IQueryHandler<,>).MakeGenericType(queryType, resultType);
// GetDependencyScope() calls IDependencyResolver.BeginScope internally.
request.GetDependencyScope();
this.ApplyHeaders(request);
dynamic handler = this.handlerFactory.Invoke(handlerType);
try
{
dynamic query = DeserializeQuery(request, queryData, queryType);
object result = handler.Handle(query);
return CreateResponse(result, resultType, HttpStatusCode.OK, request);
}
catch (Exception ex)
{
var response = WebApiErrorResponseBuilder.CreateErrorResponseOrNull(ex, request);
if (response != null)
{
return response;
}
throw;
}
}
private void ApplyHeaders(HttpRequestMessage request)
{
// TODO: Here you read the relevant headers and check them or apply them to the current scope
// so the values are accessible during execution of the query.
string sessionId = request.Headers.GetValueOrNull("sessionId");
string token = request.Headers.GetValueOrNull("CSRF-token");
}
private static HttpResponseMessage CreateResponse(object data, Type dataType, HttpStatusCode code,
HttpRequestMessage request)
{
var configuration = request.GetConfiguration();
IContentNegotiator negotiator = configuration.Services.GetContentNegotiator();
ContentNegotiationResult result = negotiator.Negotiate(dataType, request, configuration.Formatters);
var bestMatchFormatter = result.Formatter;
var mediaType = result.MediaType.MediaType;
return new HttpResponseMessage
{
Content = new ObjectContent(dataType, data, bestMatchFormatter, mediaType),
StatusCode = code,
RequestMessage = request
};
}
private static dynamic DeserializeQuery(HttpRequestMessage request, string json, Type queryType) =>
JsonConvert.DeserializeObject(json, queryType, GetJsonFormatter(request).SerializerSettings);
private static JsonMediaTypeFormatter GetJsonFormatter(HttpRequestMessage request) =>
request.GetConfiguration().Formatters.JsonFormatter;
}
}
================================================
FILE: src/WebApiService/Code/SerializationHelpers.cs
================================================
namespace WebApiService.Code
{
using System.Collections.Generic;
using System.Linq;
using System.Web;
public static class SerializationHelpers
{
public static string ConvertQueryStringToJson(string query)
{
var collection = HttpUtility.ParseQueryString(query);
var dictionary = collection.AllKeys.ToDictionary(key => key, key => collection[key]);
return ConvertDictionaryToJson(dictionary);
}
private static string ConvertDictionaryToJson(Dictionary<string, string> dictionary)
{
var propertyNames =
from key in dictionary.Keys
let index = key.IndexOf('.')
select index < 0 ? key : key.Substring(0, index);
var data =
from propertyName in propertyNames.Distinct()
let json = dictionary.ContainsKey(propertyName)
? HttpUtility.JavaScriptStringEncode(dictionary[propertyName], true)
: ConvertDictionaryToJson(FilterByPropertyName(dictionary, propertyName))
select HttpUtility.JavaScriptStringEncode(propertyName, true) + ": " + json;
return "{ " + string.Join(", ", data) + " }";
}
private static Dictionary<string, string> FilterByPropertyName(Dictionary<string, string> dictionary,
string propertyName)
{
string prefix = propertyName + ".";
return dictionary.Keys
.Where(key => key.StartsWith(prefix))
.ToDictionary(key => key.Substring(prefix.Length), key => dictionary[key]);
}
}
}
================================================
FILE: src/WebApiService/Code/WebApiExceptionTranslator.cs
================================================
namespace WebApiService.Code
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Security;
using Newtonsoft.Json;
// Allows translating exceptions thrown by the business layer to HttpResponseExceptions.
// This allows returning useful error information to the client.
public static class WebApiErrorResponseBuilder
{
public static HttpResponseMessage CreateErrorResponseOrNull(Exception thrownException,
HttpRequestMessage request)
{
if (thrownException is JsonSerializationException)
{
// Return when the supplied model (command or query) can't be deserialized.
return request.CreateErrorResponse(HttpStatusCode.BadRequest, thrownException.Message);
}
// Here are some examples of how certain exceptions can be mapped to error responses.
if (thrownException is ValidationException)
{
// Return when the supplied model (command or query) isn't valid.
return request.CreateResponse<ValidationResult>(HttpStatusCode.BadRequest,
((ValidationException)thrownException).ValidationResult);
}
if (thrownException is OptimisticConcurrencyException)
{
// Return when there was a concurrency conflict in updating the model.
return request.CreateErrorResponse(HttpStatusCode.Conflict, thrownException);
}
if (thrownException is SecurityException)
{
// Return when the current user doesn't have the proper rights to execute the requested
// operation or to access the requested resource.
return request.CreateErrorResponse(HttpStatusCode.Unauthorized, thrownException);
}
if (thrownException is KeyNotFoundException)
{
// Return when the requested resource does not exist anymore. Catching a KeyNotFoundException
// is an example, but you probably shouldn't throw KeyNotFoundException in this case, since it
// could be thrown for other reasons (such as program errors) in which case this branch should
// of course not execute.
return request.CreateErrorResponse(HttpStatusCode.NotFound, thrownException);
}
// If the thrown exception can't be handled: return null.
return null;
}
public static string GetValueOrNull(this HttpRequestHeaders headers, string name)
{
IEnumerable<string> values;
return headers.TryGetValues(name, out values) ? values.FirstOrDefault() : null;
}
}
}
================================================
FILE: src/WebApiService/Global.asax
================================================
<%@ Application Codebehind="Global.asax.cs" Inherits="WebApiService.WebApiApplication" Language="C#" %>
================================================
FILE: src/WebApiService/Global.asax.cs
================================================
namespace WebApiService
{
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Routing;
using SimpleInjector.Integration.WebApi;
// Note: For instructions on enabling IIS6 or IIS7 classic mode,
// visit http://go.microsoft.com/?LinkId=9394801
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
var container = Bootstrapper.Bootstrap();
container.Verify();
WebApiConfig.Register(GlobalConfiguration.Configuration, container);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
GlobalConfiguration.Configuration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container);
}
}
}
================================================
FILE: src/WebApiService/Properties/AssemblyInfo.cs
================================================
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("WebApiService")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("WebApiService")]
[assembly: AssemblyCopyright("Copyright © 2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("c299c0a5-0aa0-4cf3-87c2-f3b68c5f63ce")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
================================================
FILE: src/WebApiService/Web.Debug.config
================================================
<?xml version="1.0"?>
<!-- For more information on using Web.config transformation visit http://go.microsoft.com/fwlink/?LinkId=125889 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!--
In the example below, the "SetAttributes" transform will change the value of
"connectionString" to use "ReleaseSQLServer" only when the "Match" locator
finds an atrribute "name" that has a value of "MyDB".
<connectionStrings>
<add name="MyDB"
connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
-->
<system.web>
<!--
In the example below, the "Replace" transform will replace the entire
<customErrors> section of your Web.config file.
Note that because there is only one customErrors section under the
<system.web> node, there is no need to use the "xdt:Locator" attribute.
<customErrors defaultRedirect="GenericError.htm"
mode="RemoteOnly" xdt:Transform="Replace">
<error statusCode="500" redirect="InternalError.htm"/>
</customErrors>
-->
</system.web>
</configuration>
================================================
FILE: src/WebApiService/Web.Release.config
================================================
<?xml version="1.0"?>
<!-- For more information on using Web.config transformation visit http://go.microsoft.com/fwlink/?LinkId=125889 -->
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!--
In the example below, the "SetAttributes" transform will change the value of
"connectionString" to use "ReleaseSQLServer" only when the "Match" locator
finds an atrribute "name" that has a value of "MyDB".
<connectionStrings>
<add name="MyDB"
connectionString="Data Source=ReleaseSQLServer;Initial Catalog=MyReleaseDB;Integrated Security=True"
xdt:Transform="SetAttributes" xdt:Locator="Match(name)"/>
</connectionStrings>
-->
<system.web>
<compilation xdt:Transform="RemoveAttributes(debug)" />
<!--
In the example below, the "Replace" transform will replace the entire
<customErrors> section of your Web.config file.
Note that because there is only one customErrors section under the
<system.web> node, there is no need to use the "xdt:Locator" attribute.
<customErrors defaultRedirect="GenericError.htm"
mode="RemoteOnly" xdt:Transform="Replace">
<error statusCode="500" redirect="InternalError.htm"/>
</customErrors>
-->
</system.web>
</configuration>
================================================
FILE: src/WebApiService/Web.config
================================================
<?xml version="1.0" encoding="utf-8"?>
<!--
For more information on how to configure your ASP.NET application, please visit
http://go.microsoft.com/fwlink/?LinkId=169433
-->
<configuration>
<!--
For a description of web.config changes see http://go.microsoft.com/fwlink/?LinkId=235367.
The following attributes can be set on the <httpRuntime> tag.
<system.Web>
<httpRuntime targetFramework="4.8" />
</system.Web>
-->
<system.web>
<compilation debug="true" targetFramework="4.8" />
<httpRuntime targetFramework="4.5" />
<authentication mode="None" />
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<handlers>
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<remove name="OPTIONSVerbHandler" />
<remove name="TRACEVerbHandler" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
<bindingRedirect oldVersion="1.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Web.Http" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.6.0" newVersion="5.2.6.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-13.0.0.0" newVersion="13.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Net.Http.Formatting" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-5.2.6.0" newVersion="5.2.6.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
================================================
FILE: src/WebApiService/WebApiService.csproj
================================================
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>
</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{B71A8419-1827-48A4-913E-467B52A7C2F1}</ProjectGuid>
<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>WebApiService</RootNamespace>
<AssemblyName>WebApiService</AssemblyName>
<TargetFrameworkVersion>v4.8</TargetFrameworkVersion>
<MvcBuildViews>true</MvcBuildViews>
<UseIISExpress>true</UseIISExpress>
<IISExpressSSLPort />
<IISExpressAnonymousAuthentication />
<IISExpressWindowsAuthentication />
<IISExpressUseClassicPipelineMode />
<SccProjectName>SAK</SccProjectName>
<SccLocalPath>SAK</SccLocalPath>
<SccAuxPath>SAK</SccAuxPath>
<SccProvider>SAK</SccProvider>
<MvcProjectUpgradeChecked>true</MvcProjectUpgradeChecked>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>4.0</OldToolsVersion>
<UseGlobalApplicationHostFile />
<Use64BitIISExpress />
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>
</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>
</DocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<Reference Include="AutoFixture, Version=4.14.0.0, Culture=neutral, PublicKeyToken=b24654c590009d4f, processorArchitecture=MSIL">
<HintPath>..\packages\AutoFixture.4.14.0\lib\net452\AutoFixture.dll</HintPath>
</Reference>
<Reference Include="Fare, Version=2.1.0.0, Culture=neutral, PublicKeyToken=ea68d375bf33a7c8, processorArchitecture=MSIL">
<HintPath>..\packages\Fare.2.1.1\lib\net35\Fare.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Bcl.AsyncInterfaces, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Bcl.AsyncInterfaces.1.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="SimpleInjector, Version=5.0.0.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
<HintPath>..\packages\SimpleInjector.5.3.2\lib\net461\SimpleInjector.dll</HintPath>
</Reference>
<Reference Include="SimpleInjector.Integration.WebApi, Version=5.0.0.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL">
<HintPath>..\packages\SimpleInjector.Integration.WebApi.5.0.0\lib\net45\SimpleInjector.Integration.WebApi.dll</HintPath>
</Reference>
<Reference Include="SolidServices.Controllerless.WebApi.Description, Version=0.1.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\SolidServices.Controllerless.WebApi.Description.0.1.2\lib\net45\SolidServices.Controllerless.WebApi.Description.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Swashbuckle.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cd1bb07a5ac7c7bc, processorArchitecture=MSIL">
<HintPath>..\packages\Swashbuckle.Core.5.6.0\lib\net40\Swashbuckle.Core.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data.Entity" />
<Reference Include="System.Net.Http.Extensions, Version=2.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Extensions.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Net.Http.Formatting, Version=5.2.6.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.Client.5.2.6\lib\net45\System.Net.Http.Formatting.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http.Primitives, Version=4.2.29.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Net.Http.2.2.29\lib\net45\System.Net.Http.Primitives.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
</Reference>
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.2\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll</HintPath>
</Reference>
<Reference Include="System.Web.Cors, Version=5.2.6.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.Cors.5.2.6\lib\net45\System.Web.Cors.dll</HintPath>
</Reference>
<Reference Include="System.Web.DynamicData" />
<Reference Include="System.Web.Entity" />
<Reference Include="System.Web.ApplicationServices" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Web" />
<Reference Include="System.Web.Abstractions" />
<Reference Include="System.Web.Extensions" />
<Reference Include="System.Web.Http, Version=5.2.6.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.Core.5.2.6\lib\net45\System.Web.Http.dll</HintPath>
</Reference>
<Reference Include="System.Web.Http.Cors, Version=5.2.6.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.AspNet.WebApi.Cors.5.2.6\lib\net45\System.Web.Http.Cors.dll</HintPath>
</Reference>
<Reference Include="System.Web.Http.WebHost">
<HintPath>..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Web.Routing" />
<Reference Include="System.Configuration" />
<Reference Include="Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.Web.Infrastructure.1.0.0.0\lib\net40\Microsoft.Web.Infrastructure.dll</HintPath>
</Reference>
<Reference Include="System.Net.Http">
</Reference>
<Reference Include="System.Net.Http.WebRequest">
</Reference>
<Reference Include="System.Web.Helpers, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.Helpers.dll</HintPath>
</Reference>
<Reference Include="System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.Mvc.4.0.20710.0\lib\net40\System.Web.Mvc.dll</HintPath>
</Reference>
<Reference Include="System.Web.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.Razor.2.0.20710.0\lib\net40\System.Web.Razor.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages.Deployment, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Deployment.dll</HintPath>
</Reference>
<Reference Include="System.Web.WebPages.Razor, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<Private>True</Private>
<HintPath>..\packages\Microsoft.AspNet.WebPages.2.0.20710.0\lib\net40\System.Web.WebPages.Razor.dll</HintPath>
</Reference>
<Reference Include="System.XML" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml.Serialization" />
<Reference Include="WebActivator, Version=1.4.4.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\WebActivator.1.4.4\lib\net40\WebActivator.dll</HintPath>
</Reference>
<Reference Include="WebActivatorEx, Version=2.0.0.0, Culture=neutral, PublicKeyToken=7b26dc2a43f6a0d4, processorArchitecture=MSIL">
<HintPath>..\packages\WebActivatorEx.2.2.0\lib\net40\WebActivatorEx.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="App_Start\FilterConfig.cs" />
<Compile Include="App_Start\RouteConfig.cs" />
<Compile Include="App_Start\SwaggerConfig.cs" />
<Compile Include="App_Start\WebApiConfig.cs" />
<Compile Include="Code\CommandDelegatingHandler.cs" />
<Compile Include="Code\ExampleObjectCreator.cs" />
<Compile Include="Code\QueryDelegatingHandler.cs" />
<Compile Include="Code\SerializationHelpers.cs" />
<Compile Include="Code\WebApiExceptionTranslator.cs" />
<Compile Include="Bootstrapper.cs" />
<Compile Include="Global.asax.cs">
<DependentUpon>Global.asax</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="Global.asax" />
<Content Include="Web.config" />
<Content Include="Web.Debug.config">
<DependentUpon>Web.config</DependentUpon>
</Content>
<Content Include="Web.Release.config">
<DependentUpon>Web.config</DependentUpon>
</Content>
</ItemGroup>
<ItemGroup>
<Folder Include="App_Data\" />
</ItemGroup>
<ItemGroup>
<Content Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\BusinessLayer\BusinessLayer.csproj">
<Project>{5e9b468d-fd1b-4ae5-8a10-c9b09ce2690c}</Project>
<Name>BusinessLayer</Name>
</ProjectReference>
<ProjectReference Include="..\Contract\Contract.csproj">
<Project>{ddd88351-9a73-4212-85dc-f769b37d5057}</Project>
<Name>Contract</Name>
</ProjectReference>
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
<Target Name="MvcBuildViews" AfterTargets="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<AspNetCompiler VirtualPath="temp" PhysicalPath="$(WebProjectOutputDir)" />
</Target>
<ProjectExtensions>
<VisualStudio>
<FlavorProperties GUID="{349c5851-65df-11da-9384-00065b846f21}">
<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>0</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:2591/</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
</CustomServerUrl>
<SaveServerSettingsInUserFile>False</SaveServerSettingsInUserFile>
</WebProjectProperties>
</FlavorProperties>
</VisualStudio>
</ProjectExtensions>
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" />
<Target Name="EnsureBclBuildImported" BeforeTargets="BeforeBuild" Condition="'$(BclBuildImported)' == ''">
<Error Condition="!Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=317567." HelpKeyword="BCLBUILD2001" />
<Error Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" Text="The build restored NuGet packages. Build the project again to include these packages in the build. For more information, see http://go.microsoft.com/fwlink/?LinkID=317568." HelpKeyword="BCLBUILD2002" />
</Target>
<PropertyGroup>
<PostBuildEvent>copy "$(ProjectDir)..\Contract\bin\$(Configuration)\Contract.xml" "$(ProjectDir)App_Data\Contract.xml"</PostBuildEvent>
</PropertyGroup>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target> -->
</Project>
================================================
FILE: src/WebApiService/packages.config
================================================
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="AutoFixture" version="4.14.0" targetFramework="net48" />
<package id="Fare" version="2.1.1" targetFramework="net48" />
<package id="Microsoft.AspNet.Cors" version="5.2.6" targetFramework="net45" />
<package id="Microsoft.AspNet.Mvc" version="4.0.20710.0" targetFramework="net45" />
<package id="Microsoft.AspNet.Razor" version="2.0.20710.0" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Client" version="5.2.6" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Core" version="5.2.6" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Cors" version="5.2.6" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.WebHost" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.WebPages" version="2.0.20710.0" targetFramework="net45" />
<package id="Microsoft.Bcl" version="1.1.10" targetFramework="net45" />
<package id="Microsoft.Bcl.AsyncInterfaces" version="1.0.0" targetFramework="net48" />
<package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="net45" />
<package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
<package id="Newtonsoft.Json" version="13.0.2" targetFramework="net48" />
<package id="SimpleInjector" version="5.3.2" targetFramework="net48" />
<package id="SimpleInjector.Integration.WebApi" version="5.0.0" targetFramework="net48" />
<package id="SolidServices.Controllerless.WebApi.Description" version="0.1.2" targetFramework="net45" />
<package id="Swashbuckle" version="5.6.0" targetFramework="net48" />
<package id="Swashbuckle.Core" version="5.6.0" targetFramework="net48" />
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net48" />
<package id="System.Threading.Tasks.Extensions" version="4.5.2" targetFramework="net48" />
<package id="WebActivator" version="1.4.4" targetFramework="net45" />
<package id="WebActivatorEx" version="2.2.0" targetFramework="net48" />
</packages>
================================================
FILE: src/WebCore3Service/Bootstrapper.cs
================================================
namespace WebCoreService
{
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Security.Principal;
using BusinessLayer;
using Microsoft.AspNetCore.Http;
using SimpleInjector;
public static class Bootstrapper
{
public static IEnumerable<Type> GetKnownCommandTypes() => BusinessLayerBootstrapper.GetCommandTypes();
public static IEnumerable<(Type QueryType, Type ResultType)> GetKnownQueryTypes() =>
BusinessLayerBootstrapper.GetQueryTypes();
public static Container Bootstrap(Container container)
{
BusinessLayerBootstrapper.Bootstrap(container);
container.RegisterSingleton<IPrincipal, HttpContextPrincipal>();
container.RegisterInstance<ILogger>(new DebugLogger());
return container;
}
private sealed class HttpContextPrincipal : IPrincipal
{
private readonly IHttpContextAccessor httpContextAccessor;
public HttpContextPrincipal(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public IIdentity Identity => this.Principal.Identity;
public bool IsInRole(string role) => this.Principal.IsInRole(role);
private IPrincipal Principal => this.httpContextAccessor.HttpContext.User;
}
private sealed class DebugLogger : ILogger
{
public void Log(string message)
{
Debug.WriteLine(message);
}
}
}
}
================================================
FILE: src/WebCore3Service/Code/CommandHandlerMiddleware.cs
================================================
namespace WebCoreService.Code
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BusinessLayer;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using SimpleInjector;
public sealed class CommandHandlerMiddleware : IMiddleware
{
private static readonly Dictionary<string, Type> CommandTypes;
private readonly Func<Type, object> handlerFactory;
private readonly JsonSerializerSettings jsonSettings;
static CommandHandlerMiddleware()
{
CommandTypes = Bootstrapper.GetKnownCommandTypes().ToDictionary(
keySelector: type => type.ToFriendlyName(),
elementSelector: type => type,
comparer: StringComparer.OrdinalIgnoreCase);
}
public CommandHandlerMiddleware(Container container, JsonSerializerSettings jsonSettings)
{
this.handlerFactory = container.GetInstance;
this.jsonSettings = jsonSettings;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
HttpRequest request = context.Request;
string commandName = GetCommandName(request);
if (request.Method == "POST" && CommandTypes.ContainsKey(commandName))
{
Type commandType = CommandTypes[commandName];
string commandData = request.Body.ReadToEnd();
Type handlerType = typeof(ICommandHandler<>).MakeGenericType(commandType);
this.ApplyHeaders(request);
dynamic handler = this.handlerFactory.Invoke(handlerType);
try
{
dynamic command = JsonConvert.DeserializeObject(
string.IsNullOrWhiteSpace(commandData) ? "{}" : commandData,
commandType,
this.jsonSettings);
handler.Handle(command);
var result = new ObjectResult(null);
await context.WriteResultAsync(result);
}
catch (Exception exception)
{
var response = WebApiErrorResponseBuilder.CreateErrorResponseOrNull(exception, context);
if (response != null)
{
await context.WriteResultAsync(response);
}
else
{
throw;
}
}
}
else
{
await context.WriteResultAsync(new NotFoundObjectResult(commandName));
}
}
private void ApplyHeaders(HttpRequest request)
{
// TODO: Here you read the relevant headers and and check them or apply them to the current scope
// so the values are accessible during execution of the command.
string sessionId = request.Headers.GetValueOrNull("sessionId");
string token = request.Headers.GetValueOrNull("CSRF-token");
}
private static string GetCommandName(HttpRequest request)
{
Uri requestUri = new Uri(request.GetEncodedUrl());
return requestUri.Segments.LastOrDefault();
}
}
}
================================================
FILE: src/WebCore3Service/Code/HeaderDictionaryExtensions.cs
================================================
namespace WebCoreService.Code
{
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Primitives;
public static class HeaderDictionaryExtensions
{
public static string GetValueOrNull(this IHeaderDictionary headers, string key)
{
if (!headers.TryGetValue(key, out StringValues value))
return null;
return value[0];
}
}
}
================================================
FILE: src/WebCore3Service/Code/HttpContextExtensions.cs
================================================
namespace WebCoreService.Code
{
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
// https://github.com/aspnet/Mvc/issues/7238#issuecomment-357391426
public static class HttpContextExtensions
{
private static readonly RouteData EmptyRouteData = new RouteData();
private static readonly ActionDescriptor EmptyActionDescriptor = new ActionDescriptor();
public static Task WriteResultAsync<TResult>(this HttpContext context, TResult result)
where TResult : ObjectResult
{
if (context == null)
throw new ArgumentNullException(nameof(context));
var executor = result is NotFoundObjectResult
? context.RequestServices.GetService<IActionResultExecutor<ObjectResult>>()
: context.RequestServices.GetService<IActionResultExecutor<TResult>>();
if (executor == null)
throw new InvalidOperationException(
$"No result executor for '{typeof(TResult).FullName}' has been registered.");
var routeData = context.GetRouteData() ?? EmptyRouteData;
var actionContext = new ActionContext(context, routeData, EmptyActionDescriptor);
return executor.ExecuteAsync(actionContext, result);
}
}
}
================================================
FILE: src/WebCore3Service/Code/QueryHandlerMiddleware.cs
================================================
namespace WebCoreService.Code
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Contract;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using SimpleInjector;
public sealed class QueryHandlerMiddleware : IMiddleware
{
private static readonly Dictionary<string, (Type QueryType, Type ResultType)> QueryTypes;
private readonly Func<Type, object> handlerFactory;
private readonly JsonSerializerSettings jsonSettings;
static QueryHandlerMiddleware()
{
QueryTypes = Bootstrapper.GetKnownQueryTypes().ToDictionary(
info => info.QueryType.ToFriendlyName(),
info => info,
StringComparer.OrdinalIgnoreCase);
}
public QueryHandlerMiddleware(Container container, JsonSerializerSettings jsonSettings)
{
this.handlerFactory = container.GetInstance;
this.jsonSettings = jsonSettings;
}
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
HttpRequest request = context.Request;
string queryName = GetQueryName(request);
if (QueryTypes.ContainsKey(queryName))
{
// GET operations get their data through the query string, while POST operations expect a JSON
// object being put in the body.
string queryData = request.Method.Equals("get", StringComparison.OrdinalIgnoreCase)
? SerializationHelpers.ConvertQueryStringToJson(request.QueryString.Value)
: request.Body.ReadToEnd();
var (queryType, resultType) = QueryTypes[queryName];
Type handlerType = typeof(IQueryHandler<,>).MakeGenericType(queryType, resultType);
this.ApplyHeaders(request);
dynamic handler = this.handlerFactory.Invoke(handlerType);
try
{
dynamic query = JsonConvert.DeserializeObject(
string.IsNullOrWhiteSpace(queryData) ? "{}" : queryData,
queryType,
this.jsonSettings);
object result = handler.Handle(query);
await context.WriteResultAsync(new ObjectResult(result));
}
catch (Exception exception)
{
ObjectResult response =
WebApiErrorResponseBuilder.CreateErrorResponseOrNull(exception, context);
if (response != null)
{
await context.WriteResultAsync(response);
}
else
{
throw;
}
}
}
else
{
var response = new ObjectResult(queryName)
{
StatusCode = StatusCodes.Status404NotFound
};
await context.WriteResultAsync(response);
}
}
private void ApplyHeaders(HttpRequest request)
{
// TODO: Here you read the relevant headers and check them or apply them to the current scope
// so the values are accessible during execution of the query.
string sessionId = request.Headers.GetValueOrNull("sessionId");
string token = request.Headers.GetValueOrNull("CSRF-token");
}
private static string GetQueryName(HttpRequest request)
{
Uri requestUri = new Uri(request.GetEncodedUrl());
return requestUri.Segments.LastOrDefault();
}
}
}
================================================
FILE: src/WebCore3Service/Code/SerializationHelpers.cs
================================================
namespace WebCoreService.Code
{
using System.Collections.Generic;
using System.Linq;
using System.Web;
public static class SerializationHelpers
{
// NOTE: arrays and dictionary formats are not supported. e.g. this won't work:
// ?values[0]=a&values[1]=b&x[A]=1&x[B]=5
public static string ConvertQueryStringToJson(string query)
{
var collection = HttpUtility.ParseQueryString(query);
var dictionary = collection.AllKeys.ToDictionary(key => key, key => collection[key]);
return ConvertDictionaryToJson(dictionary);
}
private static string ConvertDictionaryToJson(Dictionary<string, string> dictionary)
{
var propertyNames =
from key in dictionary.Keys
let index = key.IndexOf(value: '.')
select index < 0 ? key : key.Substring(0, index);
var data =
from propertyName in propertyNames.Distinct()
let json = dictionary.ContainsKey(propertyName)
? HttpUtility.JavaScriptStringEncode(dictionary[propertyName], addDoubleQuotes: true)
: ConvertDictionaryToJson(FilterByPropertyName(dictionary, propertyName))
select HttpUtility.JavaScriptStringEncode(propertyName, addDoubleQuotes: true) + ": " + json;
return "{ " + string.Join(", ", data) + " }";
}
private static Dictionary<string, string> FilterByPropertyName(Dictionary<string, string> dictionary,
string propertyName)
{
string prefix = propertyName + ".";
return dictionary.Keys
.Where(key => key.StartsWith(prefix))
.ToDictionary(key => key.Substring(prefix.Length), key => dictionary[key]);
}
}
}
================================================
FILE: src/WebCore3Service/Code/StreamExtensions.cs
================================================
namespace WebCoreService.Code
{
using System.IO;
public static class StreamExtensions
{
public static string ReadToEnd(this Stream stream)
{
string result;
using (var reader = new StreamReader(stream))
{
result = reader.ReadToEnd();
}
return result;
}
}
}
================================================
FILE: src/WebCore3Service/Code/WebApiErrorResponseBuilder.cs
================================================
namespace WebCoreService.Code
{
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Net;
using System.Security;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Formatters;
using Newtonsoft.Json;
public static class WebApiErrorResponseBuilder
{
// Allows translating exceptions thrown by the business layer to HttpResponseExceptions.
// This allows returning useful error information to the client.
public static ObjectResult CreateErrorResponseOrNull(Exception thrownException, HttpContext context)
{
// TODO: context.Response.ContentType is always null, not sure why.
var contentTypes = new MediaTypeCollection { context.Response.ContentType ?? "application/json" };
// Here are some examples of how certain exceptions can be mapped to error responses.
switch (thrownException)
{
case JsonException _:
// Return when the supplied model (command or query) can't be deserialized.
return new BadRequestObjectResult(thrownException.Message) { ContentTypes = contentTypes };
case ValidationException exception:
// Return when the supplied model (command or query) isn't valid.
return new BadRequestObjectResult(exception.ValidationResult) { ContentTypes = contentTypes };
// case OptimisticConcurrencyException _:
// // Return when there was a concurrency conflict in updating the model.
// return new ConflictObjectResult(thrownException.Message) { ContentTypes = contentTypes };
case SecurityException _:
// Return when the current user doesn't have the proper rights to execute the requested
// operation or to access the requested resource.
return new ObjectResult(null)
{
ContentTypes = contentTypes,
StatusCode = (int)HttpStatusCode.Unauthorized
};
case KeyNotFoundException _:
// Return when the requested resource does not exist anymore. Catching a KeyNotFoundException
// is an example, but you probably shouldn't throw KeyNotFoundException in this case, since it
// could be thrown for other reasons (such as program errors) in which case this branch should
// of course not execute.
return new NotFoundObjectResult(thrownException.Message) { ContentTypes = contentTypes };
}
// If the thrown exception can't be handled: return null.
return null;
}
}
}
================================================
FILE: src/WebCore3Service/Program.cs
================================================
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
namespace WebCoreService
{
// See the Startup class for two examples of query urls.
public static class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
}
================================================
FILE: src/WebCore3Service/Properties/launchSettings.json
================================================
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:49228",
"sslPort": 0
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "api/values",
"environmentVariables": {
"ASPNETCORE_ENVIR
gitextract_3fjw8qzp/
├── .gitignore
├── LICENSE
├── README.md
└── src/
├── .gitignore
├── BusinessLayer/
│ ├── BusinessLayer.csproj
│ ├── BusinessLayerBootstrapper.cs
│ ├── CommandHandlers/
│ │ ├── CreateOrderCommandHandler.cs
│ │ └── ShipOrderCommandHandler.cs
│ ├── CrossCuttingConcerns/
│ │ ├── AuthorizationCommandHandlerDecorator.cs
│ │ ├── AuthorizationQueryHandlerDecorator.cs
│ │ ├── DataAnnotationsValidator.cs
│ │ ├── StructuredLoggingCommandHandlerDecorator.cs
│ │ ├── StructuredLoggingQueryHandlerDecorator.cs
│ │ ├── StructuredMessageLogger.cs
│ │ ├── ValidationCommandHandlerDecorator.cs
│ │ └── ValidationQueryHandlerDecorator.cs
│ ├── Helpers/
│ │ └── PagingExtensions.cs
│ ├── ICommandHandler.cs
│ ├── ILogger.cs
│ ├── IQueryHandler.cs
│ ├── IValidator.cs
│ ├── Properties/
│ │ └── AssemblyInfo.cs
│ ├── QueryHandlers/
│ │ ├── GetOrderByIdQueryHandler.cs
│ │ └── GetUnshippedOrdersQueryHandler.cs
│ └── packages.config
├── Client/
│ ├── App.config
│ ├── Bootstrapper.cs
│ ├── Client.csproj
│ ├── Code/
│ │ ├── CommandServiceClient.cs
│ │ ├── DynamicQueryProcessor.cs
│ │ ├── QueryServiceClient.cs
│ │ ├── WcfServiceCommandHandlerProxy.cs
│ │ └── WcfServiceQueryHandlerProxy.cs
│ ├── Controllers/
│ │ ├── CommandExampleController.cs
│ │ └── QueryExampleController.cs
│ ├── CrossCuttingConcerns/
│ │ └── FromWcfFaultTranslatorCommandHandlerDecorator.cs
│ ├── ICommandHandler.cs
│ ├── IQueryHandler.cs
│ ├── Program.cs
│ ├── Properties/
│ │ └── AssemblyInfo.cs
│ ├── Wcf/
│ │ ├── KnownCommandTypesAttribute.cs
│ │ ├── KnownQueryAndResultTypesAttribute.cs
│ │ ├── KnownTypesAttribute.cs
│ │ └── KnownTypesDataContractResolver.cs
│ └── packages.config
├── Contract/
│ ├── Commands/
│ │ └── Orders/
│ │ ├── CreateOrder.cs
│ │ └── ShipOrder.cs
│ ├── Contract.csproj
│ ├── DTOs/
│ │ ├── Address.cs
│ │ └── OrderInfo.cs
│ ├── ICommand.cs
│ ├── IQuery.cs
│ ├── IQueryProcessor.cs
│ ├── Properties/
│ │ └── AssemblyInfo.cs
│ ├── Queries/
│ │ ├── Orders/
│ │ │ ├── GetOrderById.cs
│ │ │ └── GetUnshippedOrders.cs
│ │ ├── PageInfo.cs
│ │ └── Paged.cs
│ └── Validators/
│ ├── CompositeValidationResult.cs
│ ├── NonEmptyGuidAttribute.cs
│ └── ValidateObjectAttribute.cs
├── Settings.StyleCop
├── SolidServices.sln
├── WcfService/
│ ├── Bootstrapper.cs
│ ├── Code/
│ │ ├── DebugLogger.cs
│ │ └── WcfExceptionTranslator.cs
│ ├── CommandService.svc
│ ├── CommandService.svc.cs
│ ├── CrossCuttingConcerns/
│ │ └── ToWcfFaultTranslatorCommandHandlerDecorator.cs
│ ├── Global.asax
│ ├── Global.asax.cs
│ ├── NonDotNetQueryService.cs
│ ├── NonDotNetQueryService.svc
│ ├── NonDotNetQueryService.tt
│ ├── Properties/
│ │ └── AssemblyInfo.cs
│ ├── QueryService.svc
│ ├── QueryService.svc.cs
│ ├── ValidationError.cs
│ ├── WcfService.csproj
│ ├── Web.Debug.config
│ ├── Web.Release.config
│ ├── Web.config
│ └── packages.config
├── WebApiService/
│ ├── App_Data/
│ │ └── .gitignore
│ ├── App_Start/
│ │ ├── FilterConfig.cs
│ │ ├── RouteConfig.cs
│ │ ├── SwaggerConfig.cs
│ │ └── WebApiConfig.cs
│ ├── Bootstrapper.cs
│ ├── Code/
│ │ ├── CommandDelegatingHandler.cs
│ │ ├── ExampleObjectCreator.cs
│ │ ├── QueryDelegatingHandler.cs
│ │ ├── SerializationHelpers.cs
│ │ └── WebApiExceptionTranslator.cs
│ ├── Global.asax
│ ├── Global.asax.cs
│ ├── Properties/
│ │ └── AssemblyInfo.cs
│ ├── Web.Debug.config
│ ├── Web.Release.config
│ ├── Web.config
│ ├── WebApiService.csproj
│ └── packages.config
├── WebCore3Service/
│ ├── Bootstrapper.cs
│ ├── Code/
│ │ ├── CommandHandlerMiddleware.cs
│ │ ├── HeaderDictionaryExtensions.cs
│ │ ├── HttpContextExtensions.cs
│ │ ├── QueryHandlerMiddleware.cs
│ │ ├── SerializationHelpers.cs
│ │ ├── StreamExtensions.cs
│ │ └── WebApiErrorResponseBuilder.cs
│ ├── Program.cs
│ ├── Properties/
│ │ └── launchSettings.json
│ ├── Startup.cs
│ ├── WebCore3Service.csproj
│ ├── appsettings.Development.json
│ └── appsettings.json
└── WebCore6Service/
├── Bootstrapper.cs
├── Code/
│ ├── Commands.cs
│ ├── FlatApiMessageMappingBuilder.cs
│ ├── IMessageMappingBuilder.cs
│ ├── MessageMapping.cs
│ ├── MessageMappingExtensions.cs
│ ├── Queries.cs
│ └── WebApiErrorResponseBuilder.cs
├── Program.cs
├── Properties/
│ └── launchSettings.json
├── Swagger/
│ ├── SwaggerExtensions.cs
│ └── XmlDocumentationTypeDescriptionProvider.cs
├── WebCore6Service.csproj
├── appsettings.Development.json
└── appsettings.json
SYMBOL INDEX (290 symbols across 87 files)
FILE: src/BusinessLayer/BusinessLayerBootstrapper.cs
class BusinessLayerBootstrapper (line 15) | public static class BusinessLayerBootstrapper
method Bootstrap (line 20) | public static void Bootstrap(Container container)
method GetCommandTypes (line 38) | public static IEnumerable<Type> GetCommandTypes() =>
method CreateQueryHandlerType (line 45) | public static Type CreateQueryHandlerType(Type queryType) =>
method GetQueryTypes (line 48) | public static IEnumerable<(Type QueryType, Type ResultType)> GetQueryT...
method GetQueryResultType (line 54) | public static Type GetQueryResultType(Type queryType) => DetermineResu...
method IsQuery (line 56) | private static bool IsQuery(Type type) => DetermineResultTypes(type).A...
method DetermineResultTypes (line 58) | private static IEnumerable<Type> DetermineResultTypes(Type type) =>
FILE: src/BusinessLayer/CommandHandlers/CreateOrderCommandHandler.cs
class CreateOrderCommandHandler (line 6) | public class CreateOrderCommandHandler : ICommandHandler<CreateOrder>
method CreateOrderCommandHandler (line 10) | public CreateOrderCommandHandler(ILogger logger)
method Handle (line 15) | public void Handle(CreateOrder command)
FILE: src/BusinessLayer/CommandHandlers/ShipOrderCommandHandler.cs
class ShipOrderCommandHandler (line 6) | public class ShipOrderCommandHandler : ICommandHandler<ShipOrder>
method ShipOrderCommandHandler (line 10) | public ShipOrderCommandHandler(ILogger logger)
method Handle (line 15) | public void Handle(ShipOrder command)
FILE: src/BusinessLayer/CrossCuttingConcerns/AuthorizationCommandHandlerDecorator.cs
class AuthorizationCommandHandlerDecorator (line 8) | public class AuthorizationCommandHandlerDecorator<TCommand> : ICommandHa...
method AuthorizationCommandHandlerDecorator (line 14) | public AuthorizationCommandHandlerDecorator(ICommandHandler<TCommand> ...
method Handle (line 22) | public void Handle(TCommand query)
method Authorize (line 29) | private void Authorize()
FILE: src/BusinessLayer/CrossCuttingConcerns/AuthorizationQueryHandlerDecorator.cs
class AuthorizationQueryHandlerDecorator (line 7) | public class AuthorizationQueryHandlerDecorator<TQuery, TResult> : IQuer...
method AuthorizationQueryHandlerDecorator (line 14) | public AuthorizationQueryHandlerDecorator(IQueryHandler<TQuery, TResul...
method Handle (line 22) | public TResult Handle(TQuery query)
method Authorize (line 29) | private void Authorize()
FILE: src/BusinessLayer/CrossCuttingConcerns/DataAnnotationsValidator.cs
class DataAnnotationsValidator (line 6) | public class DataAnnotationsValidator : IValidator
method ValidateObject (line 8) | [DebuggerStepThrough]
FILE: src/BusinessLayer/CrossCuttingConcerns/StructuredLoggingCommandHandlerDecorator.cs
class StructuredLoggingCommandHandlerDecorator (line 6) | public sealed class StructuredLoggingCommandHandlerDecorator<TCommand> :...
method StructuredLoggingCommandHandlerDecorator (line 12) | public StructuredLoggingCommandHandlerDecorator(
method Handle (line 19) | public void Handle(TCommand command)
FILE: src/BusinessLayer/CrossCuttingConcerns/StructuredLoggingQueryHandlerDecorator.cs
class StructuredLoggingQueryHandlerDecorator (line 6) | public sealed class StructuredLoggingQueryHandlerDecorator<TQuery, TResult>
method StructuredLoggingQueryHandlerDecorator (line 12) | public StructuredLoggingQueryHandlerDecorator(
method Handle (line 19) | public TResult Handle(TQuery query)
FILE: src/BusinessLayer/CrossCuttingConcerns/StructuredMessageLogger.cs
class StructuredMessageLogger (line 19) | public sealed class StructuredMessageLogger<TMessage>
method StructuredMessageLogger (line 31) | static StructuredMessageLogger()
method StructuredMessageLogger (line 46) | public StructuredMessageLogger(ILogger logger)
method Log (line 51) | public void Log(TMessage message, TimeSpan elapsed)
method BuildParameters (line 58) | private object[] BuildParameters(TMessage message, TimeSpan elapsed)
method GetLoggableMessageProperties (line 77) | private static PropertyInfo[] GetLoggableMessageProperties()
FILE: src/BusinessLayer/CrossCuttingConcerns/ValidationCommandHandlerDecorator.cs
class ValidationCommandHandlerDecorator (line 6) | public class ValidationCommandHandlerDecorator<TCommand> : ICommandHandl...
method ValidationCommandHandlerDecorator (line 11) | public ValidationCommandHandlerDecorator(IValidator validator, IComman...
method Handle (line 17) | void ICommandHandler<TCommand>.Handle(TCommand command)
FILE: src/BusinessLayer/CrossCuttingConcerns/ValidationQueryHandlerDecorator.cs
class ValidationQueryHandlerDecorator (line 6) | public class ValidationQueryHandlerDecorator<TQuery, TResult> : IQueryHa...
method ValidationQueryHandlerDecorator (line 12) | public ValidationQueryHandlerDecorator(IValidator validator, IQueryHan...
method Handle (line 18) | TResult IQueryHandler<TQuery, TResult>.Handle(TQuery query)
FILE: src/BusinessLayer/Helpers/PagingExtensions.cs
class PagingExtensions (line 7) | public static class PagingExtensions
method Page (line 14) | public static Paged<T> Page<T>(this IEnumerable<T> collection, PageInf...
method Page (line 30) | public static Paged<T> Page<T>(this IQueryable<T> collection, PageInfo...
FILE: src/BusinessLayer/ICommandHandler.cs
type ICommandHandler (line 5) | public interface ICommandHandler<TCommand> where TCommand : ICommand
method Handle (line 7) | void Handle(TCommand command);
FILE: src/BusinessLayer/ILogger.cs
type ILogger (line 3) | public interface ILogger
method Log (line 5) | void Log(string message);
class LoggerExtensions (line 8) | public static class LoggerExtensions
method LogInformation (line 10) | public static void LogInformation(this ILogger logger, string messageT...
FILE: src/BusinessLayer/IQueryHandler.cs
type IQueryHandler (line 3) | public interface IQueryHandler<TQuery, TResult> where TQuery : IQuery<TR...
method Handle (line 5) | TResult Handle(TQuery query);
FILE: src/BusinessLayer/IValidator.cs
type IValidator (line 3) | public interface IValidator
method ValidateObject (line 9) | void ValidateObject(object instance);
FILE: src/BusinessLayer/QueryHandlers/GetOrderByIdQueryHandler.cs
class GetOrderByIdQueryHandler (line 9) | public class GetOrderByIdQueryHandler : IQueryHandler<GetOrderById, Orde...
method Handle (line 11) | public OrderInfo Handle(GetOrderById query)
FILE: src/BusinessLayer/QueryHandlers/GetUnshippedOrdersQueryHandler.cs
class GetUnshippedOrdersQueryHandler (line 11) | public class GetUnshippedOrdersQueryHandler : IQueryHandler<GetUnshipped...
method GetUnshippedOrdersQueryHandler (line 15) | public GetUnshippedOrdersQueryHandler(ILogger logger)
method Handle (line 20) | public Paged<OrderInfo> Handle(GetUnshippedOrders query)
method GetAllOrders (line 28) | private static IEnumerable<OrderInfo> GetAllOrders()
FILE: src/Client/Bootstrapper.cs
class Bootstrapper (line 9) | public static class Bootstrapper
method Bootstrap (line 13) | public static void Bootstrap()
method GetInstance (line 31) | public static TService GetInstance<TService>() where TService : class
FILE: src/Client/Code/CommandServiceClient.cs
type CommandService (line 10) | [KnownCommandTypes]
method Execute (line 16) | [OperationContract(
class CommandServiceClient (line 22) | public class CommandServiceClient : ClientBase<CommandService>, CommandS...
method Execute (line 24) | [DebuggerStepThrough]
FILE: src/Client/Code/DynamicQueryProcessor.cs
class DynamicQueryProcessor (line 7) | public sealed class DynamicQueryProcessor : IQueryProcessor
method DynamicQueryProcessor (line 11) | public DynamicQueryProcessor(Container container)
method Execute (line 16) | [DebuggerStepThrough]
FILE: src/Client/Code/QueryServiceClient.cs
type QueryService (line 10) | [KnownQueryAndResultTypes]
method Execute (line 16) | [OperationContract(
class QueryServiceClient (line 22) | public class QueryServiceClient : ClientBase<QueryService>, QueryService
method Execute (line 24) | [DebuggerStepThrough]
FILE: src/Client/Code/WcfServiceCommandHandlerProxy.cs
class WcfServiceCommandHandlerProxy (line 6) | public sealed class WcfServiceCommandHandlerProxy<TCommand> : ICommandHa...
method Handle (line 8) | [DebuggerStepThrough]
FILE: src/Client/Code/WcfServiceQueryHandlerProxy.cs
class WcfServiceQueryHandlerProxy (line 7) | public sealed class WcfServiceQueryHandlerProxy<TQuery, TResult> : IQuer...
method Handle (line 10) | [DebuggerStepThrough]
FILE: src/Client/Controllers/CommandExampleController.cs
class CommandExampleController (line 7) | public class CommandExampleController
method CommandExampleController (line 12) | public CommandExampleController(
method CreateOrder (line 20) | public Guid CreateOrder()
method ShipOrder (line 40) | public void ShipOrder(Guid orderId)
FILE: src/Client/Controllers/QueryExampleController.cs
class QueryExampleController (line 11) | public class QueryExampleController
method QueryExampleController (line 15) | public QueryExampleController(IQueryProcessor queryProcessor)
method ShowOrders (line 20) | public void ShowOrders(int pageIndex, int pageSize)
FILE: src/Client/CrossCuttingConcerns/FromWcfFaultTranslatorCommandHandlerDecorator.cs
class FromWcfFaultTranslatorCommandHandlerDecorator (line 6) | public class FromWcfFaultTranslatorCommandHandlerDecorator<TCommand> : I...
method FromWcfFaultTranslatorCommandHandlerDecorator (line 10) | public FromWcfFaultTranslatorCommandHandlerDecorator(ICommandHandler<T...
method Handle (line 15) | public void Handle(TCommand command)
FILE: src/Client/ICommandHandler.cs
type ICommandHandler (line 3) | public interface ICommandHandler<TCommand>
method Handle (line 5) | void Handle(TCommand command);
FILE: src/Client/IQueryHandler.cs
type IQueryHandler (line 5) | public interface IQueryHandler<TQuery, TResult> where TQuery : IQuery<TR...
method Handle (line 7) | TResult Handle(TQuery query);
FILE: src/Client/Program.cs
class Program (line 6) | public class Program
method Main (line 8) | public static void Main(string[] args)
FILE: src/Client/Wcf/KnownCommandTypesAttribute.cs
class KnownCommandTypesAttribute (line 8) | public class KnownCommandTypesAttribute : KnownTypesAttribute
method KnownCommandTypesAttribute (line 10) | public KnownCommandTypesAttribute() : base(new KnownTypesDataContractR...
FILE: src/Client/Wcf/KnownQueryAndResultTypesAttribute.cs
class KnownQueryAndResultTypesAttribute (line 8) | public class KnownQueryAndResultTypesAttribute : KnownTypesAttribute
method KnownQueryAndResultTypesAttribute (line 10) | public KnownQueryAndResultTypesAttribute()
method IsQueryType (line 20) | private static bool IsQueryType(Type type) => GetResultType(type) != n...
method GetResultType (line 22) | private static Type GetResultType(Type queryType) => (
FILE: src/Client/Wcf/KnownTypesAttribute.cs
class KnownTypesAttribute (line 8) | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, All...
method KnownTypesAttribute (line 13) | public KnownTypesAttribute(KnownTypesDataContractResolver resolver)
method AddBindingParameters (line 18) | public void AddBindingParameters(ContractDescription contractDescripti...
method ApplyClientBehavior (line 23) | public void ApplyClientBehavior(ContractDescription contractDescriptio...
method ApplyDispatchBehavior (line 29) | public void ApplyDispatchBehavior(ContractDescription contractDescript...
method Validate (line 35) | public void Validate(ContractDescription contractDescription, ServiceE...
method CreateMyDataContractSerializerOperationBehaviors (line 39) | private void CreateMyDataContractSerializerOperationBehaviors(Contract...
method CreateMyDataContractSerializerOperationBehavior (line 47) | private void CreateMyDataContractSerializerOperationBehavior(Operation...
FILE: src/Client/Wcf/KnownTypesDataContractResolver.cs
class KnownTypesDataContractResolver (line 11) | public sealed class KnownTypesDataContractResolver : DataContractResolver
method KnownTypesDataContractResolver (line 15) | public KnownTypesDataContractResolver(IEnumerable<Type> types)
method ResolveName (line 20) | public override Type ResolveName(
method TryResolveType (line 44) | [DebuggerStepThrough]
method GetName (line 57) | private static string GetName(Type type) =>
method GetGenericName (line 62) | private static string GetGenericName(Type type)
FILE: src/Contract/Commands/Orders/CreateOrder.cs
class CreateOrder (line 9) | public class CreateOrder : ICommand
FILE: src/Contract/Commands/Orders/ShipOrder.cs
class ShipOrder (line 7) | public class ShipOrder : ICommand
FILE: src/Contract/DTOs/Address.cs
class Address (line 5) | public class Address
FILE: src/Contract/DTOs/OrderInfo.cs
class OrderInfo (line 5) | public class OrderInfo
FILE: src/Contract/ICommand.cs
type ICommand (line 3) | public interface ICommand { }
FILE: src/Contract/IQuery.cs
type IQuery (line 5) | public interface IQuery<TResult>
FILE: src/Contract/IQueryProcessor.cs
type IQueryProcessor (line 3) | public interface IQueryProcessor
method Execute (line 5) | TResult Execute<TResult>(IQuery<TResult> query);
FILE: src/Contract/Queries/Orders/GetOrderById.cs
class GetOrderById (line 8) | public class GetOrderById : IQuery<OrderInfo>
FILE: src/Contract/Queries/Orders/GetUnshippedOrders.cs
class GetUnshippedOrders (line 8) | public class GetUnshippedOrders : IQuery<Paged<OrderInfo>>
FILE: src/Contract/Queries/PageInfo.cs
class PageInfo (line 4) | public class PageInfo
method SinglePage (line 7) | public static PageInfo SinglePage() => new PageInfo { PageIndex = 0, P...
method IsSinglePage (line 16) | public bool IsSinglePage() => this.PageIndex == 0 && this.PageSize == -1;
FILE: src/Contract/Queries/Paged.cs
class Paged (line 9) | [DataContract(Name = nameof(Paged<T>) + "Of{0}")]
FILE: src/Contract/Validators/CompositeValidationResult.cs
class CompositeValidationResult (line 8) | public class CompositeValidationResult : ValidationResult, IEnumerable<V...
method CompositeValidationResult (line 10) | public CompositeValidationResult(string errorMessage, IEnumerable<Vali...
method GetEnumerator (line 17) | public IEnumerator<ValidationResult> GetEnumerator() => this.Results.G...
method GetEnumerator (line 18) | IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator();
FILE: src/Contract/Validators/NonEmptyGuidAttribute.cs
class NonEmptyGuidAttribute (line 7) | [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | At...
method IsValid (line 11) | public override bool IsValid(object value)
FILE: src/Contract/Validators/ValidateObjectAttribute.cs
class ValidateObjectAttribute (line 7) | public class ValidateObjectAttribute : ValidationAttribute
method IsValid (line 10) | protected override ValidationResult IsValid(object value, ValidationCo...
FILE: src/WcfService/Bootstrapper.cs
class Bootstrapper (line 15) | public static class Bootstrapper
method GetCommandHandler (line 19) | public static object GetCommandHandler(Type commandType) =>
method GetQueryHandler (line 22) | public static object GetQueryHandler(Type queryType) =>
method GetCommandTypes (line 25) | public static IEnumerable<Type> GetCommandTypes() => BusinessLayerBoot...
method GetQueryAndResultTypes (line 27) | public static IEnumerable<Type> GetQueryAndResultTypes()
method Bootstrap (line 34) | public static void Bootstrap()
method Log (line 50) | public static void Log(Exception ex)
method RegisterWcfSpecificDependencies (line 55) | private static void RegisterWcfSpecificDependencies()
FILE: src/WcfService/Code/DebugLogger.cs
class DebugLogger (line 6) | public sealed class DebugLogger : ILogger
method Log (line 8) | public void Log(string message)
FILE: src/WcfService/Code/WcfExceptionTranslator.cs
class WcfExceptionTranslator (line 7) | public static class WcfExceptionTranslator
method CreateFaultExceptionOrNull (line 9) | public static FaultException CreateFaultExceptionOrNull(Exception exce...
FILE: src/WcfService/CommandService.svc.cs
class CommandService (line 9) | [ServiceContract(Namespace = "http://www.solid.net/commandservice/v1.0")]
method GetKnownTypes (line 13) | public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider...
method Execute (line 16) | [OperationContract]
FILE: src/WcfService/CrossCuttingConcerns/ToWcfFaultTranslatorCommandHandlerDecorator.cs
class ToWcfFaultTranslatorCommandHandlerDecorator (line 8) | public class ToWcfFaultTranslatorCommandHandlerDecorator<TCommand> : ICo...
method ToWcfFaultTranslatorCommandHandlerDecorator (line 13) | public ToWcfFaultTranslatorCommandHandlerDecorator(ICommandHandler<TCo...
method Handle (line 18) | public void Handle(TCommand command)
FILE: src/WcfService/Global.asax.cs
class Global (line 5) | public class Global : System.Web.HttpApplication
method Application_Start (line 7) | protected void Application_Start(object sender, EventArgs e)
FILE: src/WcfService/QueryService.svc.cs
class QueryService (line 9) | [ServiceContract(Namespace = "http://www.cuttingedge.it/solid/queryservi...
method GetKnownTypes (line 13) | public static IEnumerable<Type> GetKnownTypes(ICustomAttributeProvider...
method Execute (line 16) | [OperationContract]
method ExecuteQuery (line 20) | internal static object ExecuteQuery(dynamic query)
FILE: src/WcfService/ValidationError.cs
class ValidationError (line 3) | public class ValidationError
FILE: src/WebApiService/App_Start/FilterConfig.cs
class FilterConfig (line 5) | public class FilterConfig
method RegisterGlobalFilters (line 7) | public static void RegisterGlobalFilters(GlobalFilterCollection filters)
FILE: src/WebApiService/App_Start/RouteConfig.cs
class RouteConfig (line 6) | public class RouteConfig
method RegisterRoutes (line 8) | public static void RegisterRoutes(RouteCollection routes)
FILE: src/WebApiService/App_Start/SwaggerConfig.cs
class SwaggerConfig (line 18) | public static class SwaggerConfig
method Register (line 20) | public static void Register()
method IncludeXmlCommentsFromAppDataFolder (line 34) | private static void IncludeXmlCommentsFromAppDataFolder(SwaggerDocsCon...
class ControllerlessActionOperationFilter (line 55) | private sealed class ControllerlessActionOperationFilter : IOperationF...
method ControllerlessActionOperationFilter (line 59) | public ControllerlessActionOperationFilter(params string[] xmlCommen...
method Apply (line 64) | public void Apply(Operation operation, SchemaRegistry schemaRegistry...
method GetSummaries (line 74) | private IEnumerable<string> GetSummaries(Type type) =>
FILE: src/WebApiService/App_Start/WebApiConfig.cs
class WebApiConfig (line 14) | public static class WebApiConfig
method Register (line 16) | public static void Register(HttpConfiguration config, Container contai...
method MapRoutes (line 33) | private static void MapRoutes(HttpConfiguration config, Container cont...
method UseControllerlessApiDocumentation (line 59) | private static void UseControllerlessApiDocumentation(HttpConfiguratio...
method UseCamelCaseJsonSerialization (line 87) | private static void UseCamelCaseJsonSerialization(HttpConfiguration co...
method UseIndentJsonSerialization (line 93) | private static void UseIndentJsonSerialization(HttpConfiguration config)
FILE: src/WebApiService/Bootstrapper.cs
class Bootstrapper (line 16) | public static class Bootstrapper
method GetKnownCommandTypes (line 18) | public static IEnumerable<Type> GetKnownCommandTypes() => BusinessLaye...
method GetKnownQueryTypes (line 20) | public static IEnumerable<(Type QueryType, Type ResultType)> GetKnownQ...
method Bootstrap (line 23) | public static Container Bootstrap()
class HttpContextPrincipal (line 37) | private sealed class HttpContextPrincipal : IPrincipal
method IsInRole (line 41) | public bool IsInRole(string role) => this.Principal.IsInRole(role);
class DebugLogger (line 44) | private sealed class DebugLogger : ILogger
method Log (line 46) | public void Log(string message)
FILE: src/WebApiService/Code/CommandDelegatingHandler.cs
class CommandDelegatingHandler (line 15) | public sealed class CommandDelegatingHandler : DelegatingHandler
method CommandDelegatingHandler (line 20) | public CommandDelegatingHandler(Func<Type, object> handlerFactory, IEn...
method SendAsync (line 29) | protected override async Task<HttpResponseMessage> SendAsync(HttpReque...
method ApplyHeaders (line 79) | private void ApplyHeaders(HttpRequestMessage request)
method DeserializeCommand (line 87) | private static object DeserializeCommand(HttpRequestMessage request, s...
method GetJsonFormatter (line 90) | private static JsonMediaTypeFormatter GetJsonFormatter(HttpRequestMess...
FILE: src/WebApiService/Code/ExampleObjectCreator.cs
class ExampleObjectCreator (line 7) | public static class ExampleObjectCreator
method Create (line 9) | public static object Create(Type type)
FILE: src/WebApiService/Code/QueryDelegatingHandler.cs
class QueryDelegatingHandler (line 15) | public sealed class QueryDelegatingHandler : DelegatingHandler
method QueryDelegatingHandler (line 20) | public QueryDelegatingHandler(
method SendAsync (line 30) | protected override async Task<HttpResponseMessage> SendAsync(HttpReque...
method ApplyHeaders (line 78) | private void ApplyHeaders(HttpRequestMessage request)
method CreateResponse (line 86) | private static HttpResponseMessage CreateResponse(object data, Type da...
method DeserializeQuery (line 105) | private static dynamic DeserializeQuery(HttpRequestMessage request, st...
method GetJsonFormatter (line 108) | private static JsonMediaTypeFormatter GetJsonFormatter(HttpRequestMess...
FILE: src/WebApiService/Code/SerializationHelpers.cs
class SerializationHelpers (line 7) | public static class SerializationHelpers
method ConvertQueryStringToJson (line 9) | public static string ConvertQueryStringToJson(string query)
method ConvertDictionaryToJson (line 16) | private static string ConvertDictionaryToJson(Dictionary<string, strin...
method FilterByPropertyName (line 33) | private static Dictionary<string, string> FilterByPropertyName(Diction...
FILE: src/WebApiService/Code/WebApiExceptionTranslator.cs
class WebApiErrorResponseBuilder (line 16) | public static class WebApiErrorResponseBuilder
method CreateErrorResponseOrNull (line 18) | public static HttpResponseMessage CreateErrorResponseOrNull(Exception ...
method GetValueOrNull (line 61) | public static string GetValueOrNull(this HttpRequestHeaders headers, s...
FILE: src/WebApiService/Global.asax.cs
class WebApiApplication (line 10) | public class WebApiApplication : System.Web.HttpApplication
method Application_Start (line 12) | protected void Application_Start()
FILE: src/WebCore3Service/Bootstrapper.cs
class Bootstrapper (line 11) | public static class Bootstrapper
method GetKnownCommandTypes (line 13) | public static IEnumerable<Type> GetKnownCommandTypes() => BusinessLaye...
method GetKnownQueryTypes (line 15) | public static IEnumerable<(Type QueryType, Type ResultType)> GetKnownQ...
method Bootstrap (line 18) | public static Container Bootstrap(Container container)
class HttpContextPrincipal (line 28) | private sealed class HttpContextPrincipal : IPrincipal
method HttpContextPrincipal (line 32) | public HttpContextPrincipal(IHttpContextAccessor httpContextAccessor)
method IsInRole (line 38) | public bool IsInRole(string role) => this.Principal.IsInRole(role);
class DebugLogger (line 42) | private sealed class DebugLogger : ILogger
method Log (line 44) | public void Log(string message)
FILE: src/WebCore3Service/Code/CommandHandlerMiddleware.cs
class CommandHandlerMiddleware (line 14) | public sealed class CommandHandlerMiddleware : IMiddleware
method CommandHandlerMiddleware (line 21) | static CommandHandlerMiddleware()
method CommandHandlerMiddleware (line 29) | public CommandHandlerMiddleware(Container container, JsonSerializerSet...
method InvokeAsync (line 35) | public async Task InvokeAsync(HttpContext context, RequestDelegate next)
method ApplyHeaders (line 86) | private void ApplyHeaders(HttpRequest request)
method GetCommandName (line 94) | private static string GetCommandName(HttpRequest request)
FILE: src/WebCore3Service/Code/HeaderDictionaryExtensions.cs
class HeaderDictionaryExtensions (line 6) | public static class HeaderDictionaryExtensions
method GetValueOrNull (line 8) | public static string GetValueOrNull(this IHeaderDictionary headers, st...
FILE: src/WebCore3Service/Code/HttpContextExtensions.cs
class HttpContextExtensions (line 13) | public static class HttpContextExtensions
method WriteResultAsync (line 18) | public static Task WriteResultAsync<TResult>(this HttpContext context,...
FILE: src/WebCore3Service/Code/QueryHandlerMiddleware.cs
class QueryHandlerMiddleware (line 14) | public sealed class QueryHandlerMiddleware : IMiddleware
method QueryHandlerMiddleware (line 20) | static QueryHandlerMiddleware()
method QueryHandlerMiddleware (line 28) | public QueryHandlerMiddleware(Container container, JsonSerializerSetti...
method InvokeAsync (line 34) | public async Task InvokeAsync(HttpContext context, RequestDelegate next)
method ApplyHeaders (line 93) | private void ApplyHeaders(HttpRequest request)
method GetQueryName (line 102) | private static string GetQueryName(HttpRequest request)
FILE: src/WebCore3Service/Code/SerializationHelpers.cs
class SerializationHelpers (line 7) | public static class SerializationHelpers
method ConvertQueryStringToJson (line 11) | public static string ConvertQueryStringToJson(string query)
method ConvertDictionaryToJson (line 18) | private static string ConvertDictionaryToJson(Dictionary<string, strin...
method FilterByPropertyName (line 35) | private static Dictionary<string, string> FilterByPropertyName(Diction...
FILE: src/WebCore3Service/Code/StreamExtensions.cs
class StreamExtensions (line 4) | public static class StreamExtensions
method ReadToEnd (line 6) | public static string ReadToEnd(this Stream stream)
FILE: src/WebCore3Service/Code/WebApiErrorResponseBuilder.cs
class WebApiErrorResponseBuilder (line 13) | public static class WebApiErrorResponseBuilder
method CreateErrorResponseOrNull (line 17) | public static ObjectResult CreateErrorResponseOrNull(Exception thrownE...
FILE: src/WebCore3Service/Program.cs
class Program (line 7) | public static class Program
method Main (line 9) | public static void Main(string[] args)
method CreateWebHostBuilder (line 14) | public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
FILE: src/WebCore3Service/Startup.cs
class Startup (line 18) | public class Startup
method Startup (line 31) | public Startup(IConfiguration configuration)
method ConfigureServices (line 40) | public void ConfigureServices(IServiceCollection services)
method Configure (line 56) | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
method UseMiddleware (line 77) | private static void UseMiddleware(IApplicationBuilder app, IMiddleware...
FILE: src/WebCore6Service/Bootstrapper.cs
class Bootstrapper (line 8) | public static class Bootstrapper
method GetKnownCommandTypes (line 10) | public static IEnumerable<Type> GetKnownCommandTypes() => BusinessLaye...
method GetKnownQueryTypes (line 12) | public static IEnumerable<(Type QueryType, Type ResultType)> GetKnownQ...
method Bootstrap (line 15) | public static Container Bootstrap(Container container)
class HttpContextPrincipal (line 25) | private sealed class HttpContextPrincipal : IPrincipal
method HttpContextPrincipal (line 29) | public HttpContextPrincipal(IHttpContextAccessor httpContextAccessor)
method IsInRole (line 35) | public bool IsInRole(string role) => this.Principal.IsInRole(role);
class DebugLogger (line 39) | private sealed class DebugLogger : BusinessLayer.ILogger
method Log (line 41) | public void Log(string message)
FILE: src/WebCore6Service/Code/Commands.cs
type Commands (line 8) | public sealed record Commands(Container Container)
FILE: src/WebCore6Service/Code/FlatApiMessageMappingBuilder.cs
class FlatApiMessageMappingBuilder (line 13) | public sealed class FlatApiMessageMappingBuilder : IMessageMappingBuilder
method FlatApiMessageMappingBuilder (line 19) | public FlatApiMessageMappingBuilder(object dispatcher, string patternF...
method BuildMapping (line 27) | public (string, string[], Delegate) BuildMapping(Type messageType, Typ...
method GetMessageRoute (line 61) | private static string GetMessageRoute(Type messageType) =>
FILE: src/WebCore6Service/Code/IMessageMappingBuilder.cs
type IMessageMappingBuilder (line 3) | public interface IMessageMappingBuilder
method BuildMapping (line 5) | (string Pattern, string[] HttpMethods, Delegate Handler) BuildMapping(...
FILE: src/WebCore6Service/Code/MessageMapping.cs
class MessageMapping (line 3) | public static class MessageMapping
method FlatApi (line 5) | public static IMessageMappingBuilder FlatApi(object dispatcher, string...
FILE: src/WebCore6Service/Code/MessageMappingExtensions.cs
class MessageMappingExtensions (line 3) | public static class MessageMappingExtensions
method MapCommands (line 5) | public static void MapCommands(
method MapQueries (line 16) | public static void MapQueries(
method MapMessage (line 27) | public static void MapMessage(
FILE: src/WebCore6Service/Code/Queries.cs
type Queries (line 7) | public sealed record Queries(Container Container)
FILE: src/WebCore6Service/Code/WebApiErrorResponseBuilder.cs
class WebApiErrorResponseBuilder (line 7) | public static class WebApiErrorResponseBuilder
method CreateErrorResponseOrNull (line 11) | public static IResult? CreateErrorResponseOrNull(Exception thrownExcep...
FILE: src/WebCore6Service/Swagger/SwaggerExtensions.cs
class SwaggerExtensions (line 6) | public static class SwaggerExtensions
method IncludeXmlDocumentationFromDirectory (line 8) | public static void IncludeXmlDocumentationFromDirectory(this SwaggerGe...
method IncludeMessageSummariesFromXmlDocs (line 25) | public static void IncludeMessageSummariesFromXmlDocs(this SwaggerGenO...
class AddMessageSummaryOperationFilter (line 32) | public sealed class AddMessageSummaryOperationFilter : IOperationFilter
method AddMessageSummaryOperationFilter (line 36) | public AddMessageSummaryOperationFilter(string[] xmlCommentsPaths)
method Apply (line 41) | public void Apply(OpenApiOperation operation, OperationFilterContext...
method GetSummaries (line 52) | private IEnumerable<string> GetSummaries(Type type) =>
FILE: src/WebCore6Service/Swagger/XmlDocumentationTypeDescriptionProvider.cs
class XmlDocumentationTypeDescriptionProvider (line 11) | public sealed class XmlDocumentationTypeDescriptionProvider
method XmlDocumentationTypeDescriptionProvider (line 21) | public XmlDocumentationTypeDescriptionProvider(string documentPath)
method GetDescription (line 31) | public string? GetDescription(Type type)
method GetTypeNode (line 37) | private XPathNavigator GetTypeNode(Type type)
method GetTagValue (line 44) | private static string? GetTagValue(XPathNavigator parentNode, string t...
method GetTypeName (line 58) | private static string GetTypeName(Type type)
Condensed preview — 131 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (224K chars).
[
{
"path": ".gitignore",
"chars": 249,
"preview": "################################################################################\n# This .gitignore file was automatical"
},
{
"path": "LICENSE",
"chars": 1086,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2019 Steven van Deursen\n\nPermission is hereby granted, free of charge, to any pers"
},
{
"path": "README.md",
"chars": 4124,
"preview": "# Highly Maintainable Web Services\n\nThe Highly Maintainable Web Services project is a reference architecture application"
},
{
"path": "src/.gitignore",
"chars": 2942,
"preview": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User"
},
{
"path": "src/BusinessLayer/BusinessLayer.csproj",
"chars": 5406,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"12.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.micros"
},
{
"path": "src/BusinessLayer/BusinessLayerBootstrapper.cs",
"chars": 3082,
"preview": "namespace BusinessLayer\n{\n using BusinessLayer.CrossCuttingConcerns;\n using Contract;\n using SimpleInjector;\n "
},
{
"path": "src/BusinessLayer/CommandHandlers/CreateOrderCommandHandler.cs",
"chars": 537,
"preview": "namespace BusinessLayer.CommandHandlers\n{\n using Contract;\n using Contract.Commands.Orders;\n\n public class Cre"
},
{
"path": "src/BusinessLayer/CommandHandlers/ShipOrderCommandHandler.cs",
"chars": 467,
"preview": "namespace BusinessLayer.CommandHandlers\n{\n using Contract;\n using Contract.Commands.Orders;\n\n public class Shi"
},
{
"path": "src/BusinessLayer/CrossCuttingConcerns/AuthorizationCommandHandlerDecorator.cs",
"chars": 1302,
"preview": "namespace BusinessLayer.CrossCuttingConcerns\n{\n using System.Security;\n using System.Security.Principal;\n\n usi"
},
{
"path": "src/BusinessLayer/CrossCuttingConcerns/AuthorizationQueryHandlerDecorator.cs",
"chars": 1336,
"preview": "namespace BusinessLayer.CrossCuttingConcerns\n{\n using System.Security;\n using System.Security.Principal;\n usin"
},
{
"path": "src/BusinessLayer/CrossCuttingConcerns/DataAnnotationsValidator.cs",
"chars": 522,
"preview": "namespace BusinessLayer.CrossCuttingConcerns\n{\n using System.ComponentModel.DataAnnotations;\n using System.Diagno"
},
{
"path": "src/BusinessLayer/CrossCuttingConcerns/StructuredLoggingCommandHandlerDecorator.cs",
"chars": 832,
"preview": "namespace BusinessLayer.CrossCuttingConcerns\n{\n using Contract;\n using System.Diagnostics;\n\n public sealed cla"
},
{
"path": "src/BusinessLayer/CrossCuttingConcerns/StructuredLoggingQueryHandlerDecorator.cs",
"chars": 899,
"preview": "namespace BusinessLayer.CrossCuttingConcerns\n{\n using Contract;\n using System.Diagnostics;\n\n public sealed cla"
},
{
"path": "src/BusinessLayer/CrossCuttingConcerns/StructuredMessageLogger.cs",
"chars": 3575,
"preview": "namespace BusinessLayer.CrossCuttingConcerns\n{\n using System;\n using System.Linq;\n using System.Reflection;\n\n "
},
{
"path": "src/BusinessLayer/CrossCuttingConcerns/ValidationCommandHandlerDecorator.cs",
"chars": 907,
"preview": "namespace BusinessLayer.CrossCuttingConcerns\n{\n using Contract;\n using System;\n\n public class ValidationComman"
},
{
"path": "src/BusinessLayer/CrossCuttingConcerns/ValidationQueryHandlerDecorator.cs",
"chars": 941,
"preview": "namespace BusinessLayer.CrossCuttingConcerns\n{\n using System;\n using Contract;\n\n public class ValidationQueryH"
},
{
"path": "src/BusinessLayer/Helpers/PagingExtensions.cs",
"chars": 1765,
"preview": "namespace BusinessLayer\n{\n using System.Collections.Generic;\n using System.Linq;\n using Contract.Queries;\n\n "
},
{
"path": "src/BusinessLayer/ICommandHandler.cs",
"chars": 169,
"preview": "using Contract;\n\nnamespace BusinessLayer\n{\n public interface ICommandHandler<TCommand> where TCommand : ICommand\n "
},
{
"path": "src/BusinessLayer/ILogger.cs",
"chars": 388,
"preview": "namespace BusinessLayer\n{\n public interface ILogger\n {\n void Log(string message);\n }\n\n public static"
},
{
"path": "src/BusinessLayer/IQueryHandler.cs",
"chars": 156,
"preview": "namespace Contract\n{\n public interface IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResult>\n {\n T"
},
{
"path": "src/BusinessLayer/IValidator.cs",
"chars": 457,
"preview": "namespace BusinessLayer\n{\n public interface IValidator\n {\n /// <summary>Validates the given instance.</sum"
},
{
"path": "src/BusinessLayer/Properties/AssemblyInfo.cs",
"chars": 1399,
"preview": "using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Infor"
},
{
"path": "src/BusinessLayer/QueryHandlers/GetOrderByIdQueryHandler.cs",
"chars": 649,
"preview": "namespace BusinessLayer.QueryHandlers\n{\n using System;\n using System.Collections.Generic;\n using Contract;\n "
},
{
"path": "src/BusinessLayer/QueryHandlers/GetUnshippedOrdersQueryHandler.cs",
"chars": 1274,
"preview": "namespace BusinessLayer.QueryHandlers\n{\n using System;\n using System.Collections.Generic;\n using System.Linq;\n"
},
{
"path": "src/BusinessLayer/packages.config",
"chars": 416,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n <package id=\"Microsoft.Bcl.AsyncInterfaces\" version=\"1.0.0\" targetF"
},
{
"path": "src/Client/App.config",
"chars": 2719,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<configuration>\n <system.serviceModel>\n <bindings>\n <basicHttpBinding>\n "
},
{
"path": "src/Client/Bootstrapper.cs",
"chars": 1096,
"preview": "namespace Client\n{\n using Client.Code;\n using Client.Controllers;\n using Client.CrossCuttingConcerns;\n usin"
},
{
"path": "src/Client/Client.csproj",
"chars": 5196,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"12.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.micros"
},
{
"path": "src/Client/Code/CommandServiceClient.cs",
"chars": 1077,
"preview": "namespace Client.Code\n{\n using System.Diagnostics;\n using System.ServiceModel;\n using Client.Wcf;\n\n // This"
},
{
"path": "src/Client/Code/DynamicQueryProcessor.cs",
"chars": 688,
"preview": "namespace Client.Code\n{\n using System.Diagnostics;\n using SimpleInjector;\n using Contract;\n\n public sealed "
},
{
"path": "src/Client/Code/QueryServiceClient.cs",
"chars": 1089,
"preview": "namespace Client.Code\n{\n using System.Diagnostics;\n using System.ServiceModel;\n using Client.Wcf;\n\n // This"
},
{
"path": "src/Client/Code/WcfServiceCommandHandlerProxy.cs",
"chars": 1016,
"preview": "namespace Client.Code\n{\n using System;\n using System.Diagnostics;\n\n public sealed class WcfServiceCommandHandl"
},
{
"path": "src/Client/Code/WcfServiceQueryHandlerProxy.cs",
"chars": 1096,
"preview": "namespace Client.Code\n{\n using System;\n using System.Diagnostics;\n using Contract;\n\n public sealed class Wc"
},
{
"path": "src/Client/Controllers/CommandExampleController.cs",
"chars": 1434,
"preview": "namespace Client.Controllers\n{\n using System;\n using Contract.Commands.Orders;\n using Contract.DTOs;\n\n publ"
},
{
"path": "src/Client/Controllers/QueryExampleController.cs",
"chars": 1141,
"preview": "namespace Client.Controllers\n{\n using System;\n using System.Linq;\n\n using Contract;\n using Contract.DTOs;\n "
},
{
"path": "src/Client/CrossCuttingConcerns/FromWcfFaultTranslatorCommandHandlerDecorator.cs",
"chars": 977,
"preview": "namespace Client.CrossCuttingConcerns\n{\n using System.ComponentModel.DataAnnotations;\n using System.ServiceModel;"
},
{
"path": "src/Client/ICommandHandler.cs",
"chars": 119,
"preview": "namespace Client\n{\n public interface ICommandHandler<TCommand>\n {\n void Handle(TCommand command);\n }\n}"
},
{
"path": "src/Client/IQueryHandler.cs",
"chars": 175,
"preview": "namespace Client\n{\n using Contract;\n\n public interface IQueryHandler<TQuery, TResult> where TQuery : IQuery<TResu"
},
{
"path": "src/Client/Program.cs",
"chars": 625,
"preview": "namespace Client\n{\n using System;\n using Client.Controllers;\n\n public class Program\n {\n public stati"
},
{
"path": "src/Client/Properties/AssemblyInfo.cs",
"chars": 1385,
"preview": "using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Infor"
},
{
"path": "src/Client/Wcf/KnownCommandTypesAttribute.cs",
"chars": 598,
"preview": "namespace Client.Wcf\n{\n using System;\n using System.Collections.Generic;\n using System.Linq;\n using Contrac"
},
{
"path": "src/Client/Wcf/KnownQueryAndResultTypesAttribute.cs",
"chars": 996,
"preview": "namespace Client.Wcf\n{\n using System;\n using System.Collections.Generic;\n using System.Linq;\n using Contrac"
},
{
"path": "src/Client/Wcf/KnownTypesAttribute.cs",
"chars": 2128,
"preview": "namespace Client.Wcf\n{\n using System;\n using System.ServiceModel.Channels;\n using System.ServiceModel.Descript"
},
{
"path": "src/Client/Wcf/KnownTypesDataContractResolver.cs",
"chars": 2956,
"preview": "namespace Client.Wcf\n{\n using System;\n using System.Collections.Generic;\n using System.Diagnostics;\n using "
},
{
"path": "src/Client/packages.config",
"chars": 416,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n <package id=\"Microsoft.Bcl.AsyncInterfaces\" version=\"1.0.0\" targetF"
},
{
"path": "src/Contract/Commands/Orders/CreateOrder.cs",
"chars": 543,
"preview": "namespace Contract.Commands.Orders\n{\n using System;\n using System.ComponentModel.DataAnnotations;\n using Contr"
},
{
"path": "src/Contract/Commands/Orders/ShipOrder.cs",
"chars": 316,
"preview": "namespace Contract.Commands.Orders\n{\n using System;\n using Contract.Validators;\n\n /// <summary>Commands an ord"
},
{
"path": "src/Contract/Contract.csproj",
"chars": 3411,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"12.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.micros"
},
{
"path": "src/Contract/DTOs/Address.cs",
"chars": 617,
"preview": "namespace Contract.DTOs\n{\n using System.ComponentModel.DataAnnotations;\n\n public class Address\n {\n /// "
},
{
"path": "src/Contract/DTOs/OrderInfo.cs",
"chars": 225,
"preview": "namespace Contract.DTOs\n{\n using System;\n\n public class OrderInfo\n {\n public Guid Id { get; set; }\n\n "
},
{
"path": "src/Contract/ICommand.cs",
"chars": 57,
"preview": "namespace Contract\n{\n public interface ICommand { }\n}"
},
{
"path": "src/Contract/IQuery.cs",
"chars": 171,
"preview": "namespace Contract\n{\n /// <summary>Defines a query message.</summary>\n /// <typeparam name=\"TResult\"></typeparam>"
},
{
"path": "src/Contract/IQueryProcessor.cs",
"chars": 129,
"preview": "namespace Contract\n{\n public interface IQueryProcessor\n {\n TResult Execute<TResult>(IQuery<TResult> query)"
},
{
"path": "src/Contract/Properties/AssemblyInfo.cs",
"chars": 1389,
"preview": "using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Infor"
},
{
"path": "src/Contract/Queries/Orders/GetOrderById.cs",
"chars": 369,
"preview": "namespace Contract.Queries.Orders\n{\n using System;\n using Contract.DTOs;\n using Validators;\n\n /// <summary>"
},
{
"path": "src/Contract/Queries/Orders/GetUnshippedOrders.cs",
"chars": 358,
"preview": "namespace Contract.Queries.Orders\n{\n using Contract.DTOs;\n\n /// <summary>\n /// Gets a paged list of all unship"
},
{
"path": "src/Contract/Queries/PageInfo.cs",
"chars": 745,
"preview": "namespace Contract.Queries\n{\n /// <summary>Object containing information about paging.</summary>\n public class Pa"
},
{
"path": "src/Contract/Queries/Paged.cs",
"chars": 711,
"preview": "namespace Contract.Queries\n{\n using System.Runtime.Serialization;\n\n // Applying the DataContract attribute to gen"
},
{
"path": "src/Contract/Validators/CompositeValidationResult.cs",
"chars": 700,
"preview": "namespace Contract.Validators\n{\n using System.Collections;\n using System.Collections.Generic;\n using System.Co"
},
{
"path": "src/Contract/Validators/NonEmptyGuidAttribute.cs",
"chars": 650,
"preview": "namespace Contract.Validators\n{\n using System;\n using System.ComponentModel.DataAnnotations;\n\n /// <inheritdoc"
},
{
"path": "src/Contract/Validators/ValidateObjectAttribute.cs",
"chars": 873,
"preview": "namespace Contract.Validators\n{\n using System.Collections.Generic;\n using System.ComponentModel.DataAnnotations;\n"
},
{
"path": "src/Settings.StyleCop",
"chars": 10129,
"preview": "<StyleCopSettings Version=\"105\">\n <GlobalSettings>\n <StringProperty Name=\"MergeSettingsFiles\">NoMerge</StringPropert"
},
{
"path": "src/SolidServices.sln",
"chars": 8438,
"preview": "\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 17\nVisualStudioVersion = 17.0.3191"
},
{
"path": "src/WcfService/Bootstrapper.cs",
"chars": 2012,
"preview": "namespace WcfService\n{\n using System;\n using System.Collections.Generic;\n using System.Diagnostics;\n using "
},
{
"path": "src/WcfService/Code/DebugLogger.cs",
"chars": 242,
"preview": "namespace WcfService.Code\n{\n using System.Diagnostics;\n using BusinessLayer;\n\n public sealed class DebugLogger"
},
{
"path": "src/WcfService/Code/WcfExceptionTranslator.cs",
"chars": 639,
"preview": "namespace WcfService.Code\n{\n using System;\n using System.ComponentModel.DataAnnotations;\n using System.Service"
},
{
"path": "src/WcfService/CommandService.svc",
"chars": 138,
"preview": "<%@ ServiceHost \n Language=\"C#\" \n Debug=\"true\" \n Service=\"WcfService.CommandService\" \n CodeBehind=\"CommandS"
},
{
"path": "src/WcfService/CommandService.svc.cs",
"chars": 1129,
"preview": "namespace WcfService\n{\n using System;\n using System.Collections.Generic;\n using System.Reflection;\n using S"
},
{
"path": "src/WcfService/CrossCuttingConcerns/ToWcfFaultTranslatorCommandHandlerDecorator.cs",
"chars": 1037,
"preview": "namespace WcfService.CrossCuttingConcerns\n{\n using System.ComponentModel.DataAnnotations;\n using System.ServiceMo"
},
{
"path": "src/WcfService/Global.asax",
"chars": 91,
"preview": "<%@ Application Codebehind=\"Global.asax.cs\" Inherits=\"WcfService.Global\" Language=\"C#\" %>\n"
},
{
"path": "src/WcfService/Global.asax.cs",
"chars": 236,
"preview": "namespace WcfService\n{\n using System;\n\n public class Global : System.Web.HttpApplication\n {\n protected "
},
{
"path": "src/WcfService/NonDotNetQueryService.cs",
"chars": 0,
"preview": ""
},
{
"path": "src/WcfService/NonDotNetQueryService.svc",
"chars": 128,
"preview": "<%@ ServiceHost Language=\"C#\" Debug=\"true\" Service=\"WcfService.NonDotNetQueryService\" CodeBehind=\"NonDotNetQueryService"
},
{
"path": "src/WcfService/NonDotNetQueryService.tt",
"chars": 6140,
"preview": "<#\n/*\n\tGenerates a service class for queries to be consumed by non-.NET clients.\n*/\n#>\n<#@ template language=\"C#\" debug"
},
{
"path": "src/WcfService/Properties/AssemblyInfo.cs",
"chars": 1393,
"preview": "using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Infor"
},
{
"path": "src/WcfService/QueryService.svc",
"chars": 114,
"preview": "<%@ ServiceHost Language=\"C#\" Debug=\"true\" Service=\"WcfService.QueryService\" CodeBehind=\"QueryService.svc.cs\" %>\n"
},
{
"path": "src/WcfService/QueryService.svc.cs",
"chars": 1267,
"preview": "namespace WcfService\n{\n using System;\n using System.Collections.Generic;\n using System.Reflection;\n using S"
},
{
"path": "src/WcfService/ValidationError.cs",
"chars": 119,
"preview": "namespace WcfService\n{\n public class ValidationError\n {\n public string ErrorMessage { get; set; }\n }\n}"
},
{
"path": "src/WcfService/WcfService.csproj",
"chars": 9133,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"12.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.micros"
},
{
"path": "src/WcfService/Web.Debug.config",
"chars": 239,
"preview": "<?xml version=\"1.0\"?>\n\n<!-- For more information on using web.config transformation visit http://go.microsoft.com/fwlin"
},
{
"path": "src/WcfService/Web.Release.config",
"chars": 331,
"preview": "<?xml version=\"1.0\"?>\n\n<!-- For more information on using web.config transformation visit http://go.microsoft.com/fwlin"
},
{
"path": "src/WcfService/Web.config",
"chars": 2224,
"preview": "<?xml version=\"1.0\"?>\n<configuration>\n <!--\n For a description of web.config changes see http://go.microsoft.com/fw"
},
{
"path": "src/WcfService/packages.config",
"chars": 506,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n <package id=\"Microsoft.Bcl.AsyncInterfaces\" version=\"1.0.0\" targetF"
},
{
"path": "src/WebApiService/App_Data/.gitignore",
"chars": 6,
"preview": "*.xml\n"
},
{
"path": "src/WebApiService/App_Start/FilterConfig.cs",
"chars": 251,
"preview": "namespace WebApiService\n{\n using System.Web.Mvc;\n\n public class FilterConfig\n {\n public static void Reg"
},
{
"path": "src/WebApiService/App_Start/RouteConfig.cs",
"chars": 490,
"preview": "namespace WebApiService\n{\n using System.Web.Mvc;\n using System.Web.Routing;\n\n public class RouteConfig\n {\n "
},
{
"path": "src/WebApiService/App_Start/SwaggerConfig.cs",
"chars": 3046,
"preview": "[assembly: System.Web.PreApplicationStartMethod(typeof(WebApiService.SwaggerConfig), \"Register\")]\nnamespace WebApiServic"
},
{
"path": "src/WebApiService/App_Start/WebApiConfig.cs",
"chars": 3930,
"preview": "namespace WebApiService\n{\n using System.Linq;\n using System.Net.Http;\n using System.Web.Http;\n using System"
},
{
"path": "src/WebApiService/Bootstrapper.cs",
"chars": 1818,
"preview": "namespace WebApiService\n{\n using System;\n using System.Collections.Generic;\n using System.Diagnostics;\n usi"
},
{
"path": "src/WebApiService/Code/CommandDelegatingHandler.cs",
"chars": 3533,
"preview": "namespace WebApiService.Code\n{\n using System;\n using System.Collections.Generic;\n using System.Linq;\n using"
},
{
"path": "src/WebApiService/Code/ExampleObjectCreator.cs",
"chars": 424,
"preview": "namespace WebApiService.Code\n{\n using System;\n using AutoFixture;\n using AutoFixture.Kernel;\n\n public stati"
},
{
"path": "src/WebApiService/Code/QueryDelegatingHandler.cs",
"chars": 4510,
"preview": "namespace WebApiService.Code\r\n{\r\n using System;\r\n using System.Collections.Generic;\r\n using System.Linq;\r\n "
},
{
"path": "src/WebApiService/Code/SerializationHelpers.cs",
"chars": 1661,
"preview": "namespace WebApiService.Code\n{\n using System.Collections.Generic;\n using System.Linq;\n using System.Web;\n\n "
},
{
"path": "src/WebApiService/Code/WebApiExceptionTranslator.cs",
"chars": 2947,
"preview": "namespace WebApiService.Code\n{\n using System;\n using System.Collections.Generic;\n using System.ComponentModel."
},
{
"path": "src/WebApiService/Global.asax",
"chars": 105,
"preview": "<%@ Application Codebehind=\"Global.asax.cs\" Inherits=\"WebApiService.WebApiApplication\" Language=\"C#\" %>\n"
},
{
"path": "src/WebApiService/Global.asax.cs",
"chars": 907,
"preview": "namespace WebApiService\n{\n using System.Web.Http;\n using System.Web.Mvc;\n using System.Web.Routing;\n using "
},
{
"path": "src/WebApiService/Properties/AssemblyInfo.cs",
"chars": 1359,
"preview": "using System.Reflection;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\n\n// General Infor"
},
{
"path": "src/WebApiService/Web.Debug.config",
"chars": 1254,
"preview": "<?xml version=\"1.0\"?>\n\n<!-- For more information on using Web.config transformation visit http://go.microsoft.com/fwlin"
},
{
"path": "src/WebApiService/Web.Release.config",
"chars": 1314,
"preview": "<?xml version=\"1.0\"?>\n\n<!-- For more information on using Web.config transformation visit http://go.microsoft.com/fwlin"
},
{
"path": "src/WebApiService/Web.config",
"chars": 2584,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\n For more information on how to configure your ASP.NET application, please"
},
{
"path": "src/WebApiService/WebApiService.csproj",
"chars": 15329,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Project ToolsVersion=\"12.0\" DefaultTargets=\"Build\" xmlns=\"http://schemas.micros"
},
{
"path": "src/WebApiService/packages.config",
"chars": 2230,
"preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<packages>\n <package id=\"AutoFixture\" version=\"4.14.0\" targetFramework=\"net48\" "
},
{
"path": "src/WebCore3Service/Bootstrapper.cs",
"chars": 1621,
"preview": "namespace WebCoreService\n{\n using System;\n using System.Collections.Generic;\n using System.Diagnostics;\n us"
},
{
"path": "src/WebCore3Service/Code/CommandHandlerMiddleware.cs",
"chars": 3458,
"preview": "namespace WebCoreService.Code\n{\n using System;\n using System.Collections.Generic;\n using System.Linq;\n usin"
},
{
"path": "src/WebCore3Service/Code/HeaderDictionaryExtensions.cs",
"chars": 412,
"preview": "namespace WebCoreService.Code\n{\n using Microsoft.AspNetCore.Http;\n using Microsoft.Extensions.Primitives;\n\n pu"
},
{
"path": "src/WebCore3Service/Code/HttpContextExtensions.cs",
"chars": 1568,
"preview": "namespace WebCoreService.Code\n{\n using System;\n using System.Threading.Tasks;\n using Microsoft.AspNetCore.Http"
},
{
"path": "src/WebCore3Service/Code/QueryHandlerMiddleware.cs",
"chars": 3882,
"preview": "namespace WebCoreService.Code\n{\n using System;\n using System.Collections.Generic;\n using System.Linq;\n usin"
},
{
"path": "src/WebCore3Service/Code/SerializationHelpers.cs",
"chars": 1841,
"preview": "namespace WebCoreService.Code\n{\n using System.Collections.Generic;\n using System.Linq;\n using System.Web;\n\n "
},
{
"path": "src/WebCore3Service/Code/StreamExtensions.cs",
"chars": 373,
"preview": "namespace WebCoreService.Code\n{\n using System.IO;\n public static class StreamExtensions\n {\n public stat"
},
{
"path": "src/WebCore3Service/Code/WebApiErrorResponseBuilder.cs",
"chars": 2902,
"preview": "namespace WebCoreService.Code\n{\n using System;\n using System.Collections.Generic;\n using System.ComponentModel"
},
{
"path": "src/WebCore3Service/Program.cs",
"chars": 485,
"preview": "using Microsoft.AspNetCore;\nusing Microsoft.AspNetCore.Hosting;\n\nnamespace WebCoreService\n{\n // See the Startup clas"
},
{
"path": "src/WebCore3Service/Properties/launchSettings.json",
"chars": 754,
"preview": "{\n \"$schema\": \"http://json.schemastore.org/launchsettings.json\",\n \"iisSettings\": {\n \"windowsAuthentication\": false"
},
{
"path": "src/WebCore3Service/Startup.cs",
"chars": 2904,
"preview": "using WebCoreService.Code;\n\nnamespace WebCoreService\n{\n using Microsoft.AspNetCore.Builder;\n using Microsoft.AspN"
},
{
"path": "src/WebCore3Service/WebCore3Service.csproj",
"chars": 850,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n\n <PropertyGroup>\n <TargetFramework>netcoreapp3.1</TargetFramework>\n <RootN"
},
{
"path": "src/WebCore3Service/appsettings.Development.json",
"chars": 137,
"preview": "{\n \"Logging\": {\n \"LogLevel\": {\n \"Default\": \"Debug\",\n \"System\": \"Information\",\n \"Microsoft\": \"Informat"
},
{
"path": "src/WebCore3Service/appsettings.json",
"chars": 97,
"preview": "{\n \"Logging\": {\n \"LogLevel\": {\n \"Default\": \"Warning\"\n }\n },\n \"AllowedHosts\": \"*\"\n}\n"
},
{
"path": "src/WebCore6Service/Bootstrapper.cs",
"chars": 1417,
"preview": "namespace WebCoreService;\n\nusing BusinessLayer;\nusing SimpleInjector;\nusing System.Diagnostics;\nusing System.Security.P"
},
{
"path": "src/WebCore6Service/Code/Commands.cs",
"chars": 853,
"preview": "namespace WebCoreService;\n\nusing BusinessLayer;\nusing Contract;\nusing SimpleInjector;\n\n// This class is named \"Commands"
},
{
"path": "src/WebCore6Service/Code/FlatApiMessageMappingBuilder.cs",
"chars": 3817,
"preview": "namespace WebCoreService;\n\nusing System.Reflection;\n\n/// <summary>\n/// Builds mappings based on a flat model where:\n///"
},
{
"path": "src/WebCore6Service/Code/IMessageMappingBuilder.cs",
"chars": 190,
"preview": "namespace WebCoreService;\n\npublic interface IMessageMappingBuilder\n{\n (string Pattern, string[] HttpMethods, Delegat"
},
{
"path": "src/WebCore6Service/Code/MessageMapping.cs",
"chars": 241,
"preview": "namespace WebCoreService;\n\npublic static class MessageMapping\n{\n public static IMessageMappingBuilder FlatApi(object"
},
{
"path": "src/WebCore6Service/Code/MessageMappingExtensions.cs",
"chars": 1027,
"preview": "namespace WebCoreService;\n\npublic static class MessageMappingExtensions\n{\n public static void MapCommands(\n t"
},
{
"path": "src/WebCore6Service/Code/Queries.cs",
"chars": 905,
"preview": "namespace WebCoreService;\n\nusing Contract;\nusing SimpleInjector;\n\n// This class is named \"Queries\" to allow Swagger to "
},
{
"path": "src/WebCore6Service/Code/WebApiErrorResponseBuilder.cs",
"chars": 1826,
"preview": "namespace WebCoreService;\n\nusing Newtonsoft.Json;\nusing System.ComponentModel.DataAnnotations;\nusing System.Security;\n\n"
},
{
"path": "src/WebCore6Service/Program.cs",
"chars": 1497,
"preview": "using Microsoft.OpenApi.Models;\nusing SimpleInjector;\nusing WebCoreService;\n\nvar builder = WebApplication.CreateBuilder("
},
{
"path": "src/WebCore6Service/Properties/launchSettings.json",
"chars": 793,
"preview": "{\n \"$schema\": \"https://json.schemastore.org/launchsettings.json\",\n \"iisSettings\": {\n \"windowsAuthentication\": fals"
},
{
"path": "src/WebCore6Service/Swagger/SwaggerExtensions.cs",
"chars": 2151,
"preview": "namespace WebCoreService;\n\nusing Microsoft.OpenApi.Models;\nusing Swashbuckle.AspNetCore.SwaggerGen;\n\npublic static clas"
},
{
"path": "src/WebCore6Service/Swagger/XmlDocumentationTypeDescriptionProvider.cs",
"chars": 3103,
"preview": "namespace WebCoreService;\n\nusing System.Globalization;\nusing System.Xml.XPath;\n\n// NOTE: The code in this file is copy-"
},
{
"path": "src/WebCore6Service/WebCore6Service.csproj",
"chars": 753,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk.Web\">\n\n <PropertyGroup>\n <TargetFramework>net6.0</TargetFramework>\n <Nullable>ena"
},
{
"path": "src/WebCore6Service/appsettings.Development.json",
"chars": 119,
"preview": "{\n \"Logging\": {\n \"LogLevel\": {\n \"Default\": \"Information\",\n \"Microsoft.AspNetCore\": \"Warning\"\n }\n }\n}\n"
},
{
"path": "src/WebCore6Service/appsettings.json",
"chars": 142,
"preview": "{\n \"Logging\": {\n \"LogLevel\": {\n \"Default\": \"Information\",\n \"Microsoft.AspNetCore\": \"Warning\"\n }\n },\n "
}
]
About this extraction
This page contains the full source code of the dotnetjunkie/solidservices GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 131 files (201.7 KB), approximately 50.7k tokens, and a symbol index with 290 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.