Skip to content

Commit e31b496

Browse files
committed
added fluent validation and helper method for unit testing
1 parent 2cdf4ca commit e31b496

16 files changed

+213
-62
lines changed

src/App/App.Api/App.Api.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@
7777
<Reference Include="EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
7878
<HintPath>..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll</HintPath>
7979
</Reference>
80+
<Reference Include="FluentValidation, Version=6.2.1.0, Culture=neutral, processorArchitecture=MSIL">
81+
<HintPath>..\packages\FluentValidation.6.2.1.0\lib\Net45\FluentValidation.dll</HintPath>
82+
</Reference>
8083
<Reference Include="LinqKit, Version=1.1.9.0, Culture=neutral, PublicKeyToken=bc217f8844052a91, processorArchitecture=MSIL">
8184
<HintPath>..\packages\LinqKit.1.1.9.0\lib\net45\LinqKit.dll</HintPath>
8285
</Reference>
@@ -281,6 +284,7 @@
281284
</ItemGroup>
282285
<ItemGroup>
283286
<Compile Include="ApiModule.cs" />
287+
<Compile Include="App_Start\AutofacModules\FluentValidationModule.cs" />
284288
<Compile Include="App_Start\BundleConfig.cs" />
285289
<Compile Include="App_Start\FilterConfig.cs" />
286290
<Compile Include="App_Start\RouteConfig.cs" />
@@ -313,6 +317,7 @@
313317
<Compile Include="Areas\HelpPage\SampleGeneration\SampleDirection.cs" />
314318
<Compile Include="Areas\HelpPage\SampleGeneration\TextSample.cs" />
315319
<Compile Include="Areas\HelpPage\XmlDocumentationProvider.cs" />
320+
<Compile Include="AutofacConfig.cs" />
316321
<Compile Include="Controllers\AccountsController.cs" />
317322
<Compile Include="Controllers\AudienceController.cs" />
318323
<Compile Include="Controllers\BaseApiController.cs" />
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using App.Core.Utils;
2+
using Autofac;
3+
using FluentValidation;
4+
5+
namespace App.Api.App_Start.AutofacModules
6+
{
7+
public class FluentValidationModule : Module
8+
{
9+
protected override void Load(ContainerBuilder builder)
10+
{
11+
// assemblies
12+
var assemblies = AutofacAssemblyStore.GetAssemblies();
13+
14+
// validators: types derived from AbstractValidator<>
15+
builder.RegisterAssemblyTypes(assemblies)
16+
.Where(type => type.BaseType != null && type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition() == typeof(AbstractValidator<>))
17+
.Keyed(type => type.BaseType?.GenericTypeArguments[0], typeof(IValidator))
18+
.SingleInstance();
19+
}
20+
}
21+
}

src/App/App.Api/AutofacConfig.cs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using System.Linq;
2+
using System.Reflection;
3+
using System.Web.Compilation;
4+
using System.Web.Http;
5+
using System.Web.Mvc;
6+
using App.Core.Contracts;
7+
using App.Core.Implementations;
8+
using App.Infrastructure.Di;
9+
using Autofac;
10+
using Autofac.Integration.Mvc;
11+
using Autofac.Integration.WebApi;
12+
13+
namespace App.Api
14+
{
15+
public class AutofacConfig
16+
{
17+
private class Inner { }
18+
19+
private static IContainer Container { get; set; }
20+
21+
public static IContainer ConfigureContainer()
22+
{
23+
var builder = new ContainerBuilder();
24+
RegisterAllModules(builder);
25+
26+
builder.RegisterApiControllers(typeof(AutofacConfig).Assembly);
27+
AutowireProperties(builder);
28+
builder.RegisterControllers(Assembly.GetExecutingAssembly());
29+
30+
RegisterDependencies(builder);
31+
Container = builder.Build();
32+
33+
DependencyResolver.SetResolver(new AutofacResolver(Container));
34+
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(Container);
35+
36+
return Container;
37+
}
38+
39+
private static void AutowireProperties(ContainerBuilder builder)
40+
{
41+
builder.RegisterApiControllers(typeof(Inner).Assembly).PropertiesAutowired();
42+
43+
builder.RegisterType<Global>().PropertiesAutowired();
44+
}
45+
46+
private static void RegisterAllModules(ContainerBuilder builder)
47+
{
48+
var assemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToArray();
49+
// register modules from assemblies
50+
builder.RegisterAssemblyModules(assemblies);
51+
}
52+
53+
private static void RegisterDependencies(ContainerBuilder builder)
54+
{
55+
builder.Register<IResolver>(x => new Resolver(Container));
56+
}
57+
}
58+
}

src/App/App.Api/Startup.cs

Lines changed: 2 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,7 @@
11
using System;
2-
using System.Linq;
3-
using System.Reflection;
4-
using System.Web.Compilation;
5-
using System.Web.Http;
6-
using System.Web.Mvc;
72
using App.Api;
8-
using App.Core.Contracts;
9-
using App.Core.Implementations;
10-
using App.Infrastructure.Di;
113
using App.Infrastructure.Logging.Owin;
124
using Autofac;
13-
using Autofac.Integration.Mvc;
14-
using Autofac.Integration.WebApi;
155
using Microsoft.Owin;
166
using Microsoft.Owin.Security;
177
using Microsoft.Owin.Security.Infrastructure;
@@ -44,11 +34,10 @@ public void Configuration(IAppBuilder app)
4434
app.UseAutofacMiddleware(container);
4535
ConfigureOAuth(app, container);
4636
ConfigureOAuthTokenConsumption(app, container);
47-
4837
app.UseCommonLogging();
4938
}
5039

51-
private void ConfigureOAuth(IAppBuilder app, IComponentContext componentContext)
40+
private static void ConfigureOAuth(IAppBuilder app, IComponentContext componentContext)
5241
{
5342
//var issuer = ConfigurationManager.AppSettings["tokenIssuer"];
5443

@@ -78,57 +67,13 @@ private void ConfigureOAuth(IAppBuilder app, IComponentContext componentContext)
7867
// });
7968
}
8069

81-
private void ConfigureOAuthTokenConsumption(IAppBuilder app, IComponentContext componentContext)
70+
private static void ConfigureOAuthTokenConsumption(IAppBuilder app, IComponentContext componentContext)
8271
{
8372
app.UseOAuthBearerAuthentication(
8473
new OAuthBearerAuthenticationOptions
8574
{
8675
AccessTokenFormat = componentContext.Resolve<ISecureDataFormat<AuthenticationTicket>>()
8776
});
8877
}
89-
90-
private class AutofacConfig
91-
{
92-
private class Inner { }
93-
94-
private static IContainer Container { get; set; }
95-
96-
public static IContainer ConfigureContainer()
97-
{
98-
var builder = new ContainerBuilder();
99-
RegisterAllModules(builder);
100-
101-
builder.RegisterApiControllers(typeof(AutofacConfig).Assembly);
102-
AutowireProperties(builder);
103-
builder.RegisterControllers(Assembly.GetExecutingAssembly());
104-
105-
RegisterDependencies(builder);
106-
Container = builder.Build();
107-
108-
DependencyResolver.SetResolver(new AutofacResolver(Container));
109-
GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(Container);
110-
111-
return Container;
112-
}
113-
114-
private static void AutowireProperties(ContainerBuilder builder)
115-
{
116-
builder.RegisterApiControllers(typeof(Inner).Assembly).PropertiesAutowired();
117-
118-
builder.RegisterType<Global>().PropertiesAutowired();
119-
}
120-
121-
private static void RegisterAllModules(ContainerBuilder builder)
122-
{
123-
var assemblies = BuildManager.GetReferencedAssemblies().Cast<Assembly>().ToArray();
124-
// register modules from assemblies
125-
builder.RegisterAssemblyModules(assemblies);
126-
}
127-
128-
private static void RegisterDependencies(ContainerBuilder builder)
129-
{
130-
builder.Register<IResolver>(x => new Resolver(Container));
131-
}
132-
}
13378
}
13479
}

src/App/App.Api/packages.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<package id="Common.Logging" version="3.4.0" targetFramework="net461" />
1111
<package id="Common.Logging.Core" version="3.4.0" targetFramework="net461" />
1212
<package id="EntityFramework" version="6.1.3" targetFramework="net452" />
13+
<package id="FluentValidation" version="6.2.1.0" targetFramework="net461" />
1314
<package id="LinqKit" version="1.1.9.0" targetFramework="net461" />
1415
<package id="LinqKit.Core" version="1.1.9.0" targetFramework="net452" />
1516
<package id="Microsoft.AspNet.Identity.Core" version="2.2.1" targetFramework="net461" />

src/App/App.Core/App.Core.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@
4949
<Reference Include="EntityFramework.SqlServer, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL">
5050
<HintPath>..\packages\EntityFramework.6.1.3\lib\net45\EntityFramework.SqlServer.dll</HintPath>
5151
</Reference>
52+
<Reference Include="FluentValidation, Version=6.2.1.0, Culture=neutral, processorArchitecture=MSIL">
53+
<HintPath>..\packages\FluentValidation.6.2.1.0\lib\Net45\FluentValidation.dll</HintPath>
54+
</Reference>
5255
<Reference Include="LinqKit, Version=1.1.9.0, Culture=neutral, PublicKeyToken=bc217f8844052a91, processorArchitecture=MSIL">
5356
<HintPath>..\packages\LinqKit.1.1.9.0\lib\net45\LinqKit.dll</HintPath>
5457
</Reference>
@@ -85,8 +88,10 @@
8588
<Compile Include="Implementations\TraceStepUtil.cs" />
8689
<Compile Include="Properties\AssemblyInfo.cs" />
8790
<Compile Include="Utils\AsyncHelper.cs" />
91+
<Compile Include="Utils\AutofacAssemblyStore.cs" />
8892
<Compile Include="Utils\ConcurrentList.cs" />
8993
<Compile Include="Utils\EventLogger.cs" />
94+
<Compile Include="Utils\FluentValidationValidatorFactory.cs" />
9095
<Compile Include="Utils\JsonUtils.cs" />
9196
<Compile Include="Utils\LockUtil.cs" />
9297
<Compile Include="Utils\SaveUtil.cs" />
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
using System.Collections.Concurrent;
2+
using System.Linq;
3+
using System.Reflection;
4+
5+
namespace App.Core.Utils
6+
{
7+
public static class AutofacAssemblyStore
8+
{
9+
private static readonly ConcurrentDictionary<string, Assembly> Assemblies;
10+
11+
static AutofacAssemblyStore()
12+
{
13+
Assemblies = new ConcurrentDictionary<string, Assembly>();
14+
}
15+
16+
public static Assembly LoadAssembly(Assembly assembly)
17+
{
18+
return Assemblies.GetOrAdd(ExtractAssemblyShortName(assembly.FullName), shortName => assembly);
19+
}
20+
21+
public static Assembly LoadAssembly(string assemblyName)
22+
{
23+
return Assemblies.GetOrAdd(ExtractAssemblyShortName(assemblyName), shortName => LoadWorker(shortName, assemblyName));
24+
}
25+
26+
public static Assembly[] GetAssemblies()
27+
{
28+
return Assemblies.Values.ToArray();
29+
}
30+
31+
public static string ExtractAssemblyShortName(string fullName)
32+
{
33+
var index = fullName.IndexOf(',');
34+
return index < 0 ? fullName : fullName.Substring(0, index);
35+
}
36+
37+
public static void ClearAssemblies()
38+
{
39+
Assemblies.Clear();
40+
}
41+
42+
private static Assembly LoadWorker(string shortName, string fullName)
43+
{
44+
Assembly result;
45+
46+
// Try loading with full name first (if there is a full name)
47+
if (fullName != shortName)
48+
{
49+
result = TryAssemblyLoad(fullName);
50+
if (result != null) return result;
51+
}
52+
53+
// Try loading with short name
54+
result = TryAssemblyLoad(shortName);
55+
if (result != null) return result;
56+
57+
// Try again so that we get the exception this time
58+
return Assembly.Load(fullName);
59+
}
60+
61+
private static Assembly TryAssemblyLoad(string name)
62+
{
63+
try
64+
{
65+
return Assembly.Load(name);
66+
}
67+
catch
68+
{
69+
return null;
70+
}
71+
}
72+
}
73+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
using Autofac;
3+
using FluentValidation;
4+
5+
namespace App.Core.Utils
6+
{
7+
/// <summary>
8+
/// Validator factory provides the validator instance by resoving from autofac container.
9+
/// </summary>
10+
/// <remarks>
11+
/// Validator should only directly depend on singleton instance. For other lifetime instance should use self-created child scope from ILifetimeScope.
12+
/// </remarks>
13+
public class FluentValidationValidatorFactory : IValidatorFactory
14+
{
15+
private readonly ILifetimeScope _container;
16+
17+
public FluentValidationValidatorFactory(ILifetimeScope container)
18+
{
19+
_container = container;
20+
}
21+
22+
public IValidator<T> GetValidator<T>()
23+
{
24+
return (IValidator<T>)GetValidator(typeof(T));
25+
}
26+
27+
public IValidator GetValidator(Type type)
28+
{
29+
object retVal;
30+
if (_container.TryResolveKeyed(type, typeof(IValidator), out retVal))
31+
{
32+
return (IValidator)retVal;
33+
}
34+
return null;
35+
}
36+
}
37+
}

src/App/App.Core/packages.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
<package id="Common.Logging" version="3.4.0" targetFramework="net461" />
66
<package id="Common.Logging.Core" version="3.4.0" targetFramework="net461" />
77
<package id="EntityFramework" version="6.1.3" targetFramework="net452" />
8+
<package id="FluentValidation" version="6.2.1.0" targetFramework="net461" />
89
<package id="LinqKit" version="1.1.9.0" targetFramework="net461" />
910
<package id="Microsoft.CSharp" version="4.3.0" targetFramework="net461" />
1011
<package id="Newtonsoft.Json" version="10.0.3" targetFramework="net461" />

src/App/App.Database/Migrations/201708072228136_InitialMigration.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
namespace App.Database
22
{
3-
using System;
43
using System.Data.Entity.Migrations;
54

65
public partial class InitialMigration : DbMigration

0 commit comments

Comments
 (0)