Skip to content

Commit 9585cc6

Browse files
committed
Merge branch 'release/v5.2.0'
2 parents 2dbc524 + 39224ba commit 9585cc6

28 files changed

Lines changed: 616 additions & 169 deletions

.github/workflows/ci.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Wissance.WebApiToolkit CI
2+
3+
on:
4+
pull_request:
5+
branches: [develop, master]
6+
push:
7+
branches: [develop, master]
8+
9+
env:
10+
DOTNET_VERSION: '6.0.x'
11+
12+
jobs:
13+
build-and-test:
14+
name: Building Wissance.WebApiToolkit and running Tests
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v3
18+
- name: Set up .Net
19+
uses: actions/setup-dotnet@v3
20+
with:
21+
dotnet-version: ${{ env.DOTNET_VERSION }}
22+
- name: Install dependencies
23+
run: dotnet restore ./Wissance.WebApiToolkit
24+
- name: Build Solution
25+
run: dotnet build --configuration Release ./Wissance.WebApiToolkit
26+
- name: Run Wissance.WebApiToolkit.Core.Tests
27+
run: dotnet test -c Release ./Wissance.WebApiToolkit/Wissance.WebApiToolkit.Core.Tests/Wissance.WebApiToolkit.Core.Tests.csproj
28+
- name: Run Wissance.WebApiToolkit.Ef.Tests
29+
run: dotnet test -c Release ./Wissance.WebApiToolkit/Wissance.WebApiToolkit.Ef.Tests/Wissance.WebApiToolkit.Ef.Tests.csproj
30+
- name: Run Wissance.WebApiToolkit.Tests
31+
run: dotnet test -c Release ./Wissance.WebApiToolkit/Wissance.WebApiToolkit.Tests/Wissance.WebApiToolkit.Tests.csproj

Wissance.WebApiToolkit/Wissance.WebApiToolkit.Ef.Tests/Factories/TestPassFactory.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,10 @@ public void TestCreateWithCyclingDepsRemove()
6868

6969
org.Users = new List<User>() {user1, user2};
7070

71-
User user1Repr = PassFactory.Create(user1);
71+
User user1Repr = PassFactory.CreateRes(user1);
7272
Assert.Null(user1Repr.Profiles);
7373
Assert.Null(user1Repr.Organization);
74-
User user2Repr = PassFactory.Create(user2);
74+
User user2Repr = PassFactory.CreateRes(user2);
7575
Assert.Null(user2Repr.Profiles);
7676
Assert.Null(user2Repr.Organization);
7777
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Microsoft.EntityFrameworkCore;
4+
using Wissance.WebApiToolkit.Data.Entity;
5+
6+
namespace Wissance.WebApiToolkit.Ef.Configuration
7+
{
8+
/// <summary>
9+
/// ManagerConfiguration contains a set of Delegates that are using for building DTO (TRes) from Data (TObj)
10+
/// objects, for creating and updating Data objects and for the filtering Data object from DB
11+
/// </summary>
12+
/// <typeparam name="TCtx">Entity Framework Context</typeparam>
13+
/// <typeparam name="TRes">T Resource = DTO</typeparam>
14+
/// <typeparam name="TObj">T Object = Data/Entity</typeparam>
15+
/// <typeparam name="TId"></typeparam>
16+
public class ManagerConfiguration<TCtx, TRes, TObj, TId>
17+
where TCtx: DbContext
18+
where TObj: class, IModelIdentifiable<TId>
19+
where TRes: class
20+
where TId: IComparable
21+
{
22+
public Func<TObj, TRes> CreateResFunc { get; set; }
23+
public Func<TObj, IDictionary<string, string>, bool> FilterFunc { get; set; }
24+
public Func<TRes, TCtx, TObj> CreateObjFunc { get; set; }
25+
public Action<TRes, TId, TCtx, TObj> UpdateObjFunc { get; set; }
26+
}
27+
}

Wissance.WebApiToolkit/Wissance.WebApiToolkit.Ef/Extensions/ServiceCollectionExtensions.cs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,28 +6,51 @@
66
using Microsoft.Extensions.Logging;
77
using Wissance.WebApiToolkit.Core.Data;
88
using Wissance.WebApiToolkit.Data.Entity;
9+
using Wissance.WebApiToolkit.Ef.Configuration;
910
using Wissance.WebApiToolkit.Ef.Factories;
1011
using Wissance.WebApiToolkit.Ef.Generators;
1112

1213
namespace Wissance.WebApiToolkit.Ef.Extensions
1314
{
1415
public static class ServiceCollectionExtensions
1516
{
16-
public static Assembly AddSimplifiedAutoController<TObj, TId, TFilter>(this IServiceCollection services,
17-
DbContext context, string resourceName, ControllerType controllerType,
17+
public static Assembly AddSimplifiedAutoController<TCtx, TObj, TId, TFilter>(this IServiceCollection services,
18+
TCtx context, string resourceName, ControllerType controllerType,
1819
Func<TObj, IDictionary<string, string>, bool> filterFunc,
1920
ILoggerFactory loggerFactory)
21+
where TCtx: DbContext
2022
where TObj: class, IModelIdentifiable<TId>
2123
where TId: IComparable
2224
where TFilter: class, IReadFilterable
2325
{
2426
string assemblyName = $"AutoGenerated{resourceName}Controller";
2527
services.AddScoped(sp =>
2628
{
27-
return SimplifiedEfBasedManagerFactory.Create<TObj, TId>(context, filterFunc, loggerFactory);
29+
return EfBasedManagerFactory.CreateSimplifiedManager<TCtx, TObj, TId>(context, filterFunc, loggerFactory);
2830
});
2931

30-
Tuple<Assembly, string> controllerAssembly = OnTheFlyServicesGenerator.GenerateController<TObj, TId, TFilter>(assemblyName, resourceName, false, controllerType);
32+
Tuple<Assembly, string> controllerAssembly = OnTheFlyServicesGenerator.GenerateController<TObj, TObj, TId, TFilter>(assemblyName, resourceName, false, controllerType);
33+
34+
return controllerAssembly.Item1;
35+
}
36+
37+
public static Assembly AddFullyConfiguredAutoController<TCtx, TRes, TObj, TId, TFilter>(this IServiceCollection services,
38+
TCtx context, string resourceName, ControllerType controllerType,
39+
ManagerConfiguration<TCtx, TRes, TObj, TId> managerConfiguration,
40+
ILoggerFactory loggerFactory)
41+
where TCtx: DbContext
42+
where TObj : class, IModelIdentifiable<TId>
43+
where TRes : class
44+
where TId : IComparable
45+
where TFilter : class, IReadFilterable
46+
{
47+
string assemblyName = $"AutoGenerated{resourceName}Controller";
48+
services.AddScoped(sp =>
49+
{
50+
return EfBasedManagerFactory.CreateFullyDefinedManager(context, managerConfiguration, loggerFactory);
51+
});
52+
53+
Tuple<Assembly, string> controllerAssembly = OnTheFlyServicesGenerator.GenerateController<TRes, TObj, TId, TFilter>(assemblyName, resourceName, false, controllerType);
3154

3255
return controllerAssembly.Item1;
3356
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using Microsoft.CodeAnalysis.CSharp.Syntax;
4+
using Microsoft.EntityFrameworkCore;
5+
using Microsoft.Extensions.Logging;
6+
using Wissance.WebApiToolkit.Core.Managers;
7+
using Wissance.WebApiToolkit.Data.Entity;
8+
using Wissance.WebApiToolkit.Ef.Configuration;
9+
using Wissance.WebApiToolkit.Ef.Managers;
10+
11+
namespace Wissance.WebApiToolkit.Ef.Factories
12+
{
13+
public static class EfBasedManagerFactory
14+
{
15+
public static IModelManager<TE, TE, TId> CreateSimplifiedManager<TCtx,TE, TId>(TCtx dbContext, Func<TE, IDictionary<string, string>, bool> filterFunc,
16+
ILoggerFactory loggerFactory)
17+
where TId : IComparable
18+
where TE : class, IModelIdentifiable<TId>
19+
where TCtx: DbContext
20+
{
21+
return new SimplifiedEfModelManager<TCtx, TE, TId>(dbContext, filterFunc, PassFactory.CreateRes,
22+
PassFactory.CreateObj, PassFactory.UpdateAll, loggerFactory);
23+
}
24+
25+
public static IModelManager<TR, TE, TId> CreateFullyDefinedManager<TCtx, TR, TE, TId>(TCtx dbContext,
26+
ManagerConfiguration<TCtx, TR, TE, TId> configuration, ILoggerFactory loggerFactory)
27+
where TId : IComparable
28+
where TR : class
29+
where TE : class, IModelIdentifiable<TId>
30+
where TCtx: DbContext
31+
{
32+
return new FullEfModelManager<TCtx, TR, TE, TId>(dbContext, configuration, loggerFactory);
33+
}
34+
35+
}
36+
}

Wissance.WebApiToolkit/Wissance.WebApiToolkit.Ef/Factories/PassFactory.cs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
using System.Linq;
44
using System.Reflection;
55
using System.Security.Permissions;
6+
using Microsoft.EntityFrameworkCore;
67

78
namespace Wissance.WebApiToolkit.Ef.Factories
89
{
910
public static class PassFactory
1011
{
11-
public static T Create<T>(T item)
12+
public static T CreateRes<T>(T item)
1213
{
1314
T res = item;
1415
PropertyInfo[] properties = item.GetType().GetProperties(BindingFlags.DeclaredOnly |
@@ -26,9 +27,30 @@ public static T Create<T>(T item)
2627
}
2728
return res;
2829
}
30+
31+
public static T CreateObj<T, TCtx>(T item, TCtx dbContext)
32+
where TCtx: DbContext
33+
{
34+
T res = item;
35+
PropertyInfo[] properties = item.GetType().GetProperties(BindingFlags.DeclaredOnly |
36+
BindingFlags.Public |
37+
BindingFlags.Instance);
38+
IList<PropertyInfo> virtProperties = properties.Where(p => p.GetAccessors()[0].IsVirtual &&
39+
!p.PropertyType.IsPrimitive &&
40+
p.PropertyType.IsClass || p.PropertyType.IsInterface).ToList();
41+
42+
foreach (PropertyInfo virtProperty in virtProperties)
43+
{
44+
virtProperty.SetValue(res, null);
45+
// object propertyValue = virtProperty.GetValue(res);
46+
// SetNullForVirtual(propertyValue);
47+
}
48+
return res;
49+
}
2950

3051
// todo(UMV): move Generic on a class level and init properties via static constructor
31-
public static void UpdateAll<T, TId>(T item, TId id, T updatingItem)
52+
public static void UpdateAll<T, TId, TCtx>(T item, TId id, TCtx dbContext, T updatingItem)
53+
where TCtx: DbContext
3254
where T : class
3355
{
3456
Type objType = typeof(T);

Wissance.WebApiToolkit/Wissance.WebApiToolkit.Ef/Factories/SimplifiedEfBasedManagerFactory.cs

Lines changed: 0 additions & 24 deletions
This file was deleted.

Wissance.WebApiToolkit/Wissance.WebApiToolkit.Ef/Generators/OnTheFlyServicesGenerator.cs

Lines changed: 50 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,24 +8,25 @@
88

99
namespace Wissance.WebApiToolkit.Ef.Generators
1010
{
11+
// todo(UMV): combine controllers in one assembly
1112
internal static class OnTheFlyServicesGenerator
1213
{
13-
public static Tuple<Assembly, string> GenerateController<TObj, TId, TFilter>(string assemblyName, string controllerName,
14+
/*public static Tuple<Assembly, string> GenerateController<TObj, TId, TFilter>(string assemblyName, string controllerName,
1415
bool saveOnDisk, ControllerType controllerType)
1516
where TObj: class, IModelIdentifiable<TId>
1617
where TId: IComparable
1718
where TFilter: class, IReadFilterable
1819
{
1920
try
2021
{
21-
// todo(UMV): combine controllers in one assembly
22+
2223
AssemblyName name = new AssemblyName(assemblyName);
2324
AssemblyBuilderAccess access = saveOnDisk ? AssemblyBuilderAccess.RunAndCollect : AssemblyBuilderAccess.Run;
2425
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(name, access);
2526
2627
string fullControllerName = $"{controllerName}Controller";
2728
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule($"AutoGenerated{fullControllerName}");
28-
Type basicControllerType = GetAppropriateBaseControllerType<TObj, TId, TFilter>(controllerType);
29+
Type basicControllerType = GetAppropriateBaseControllerType<TObj, TObj, TId, TFilter>(controllerType);
2930
TypeBuilder typeBuilder = moduleBuilder.DefineType(fullControllerName,
3031
TypeAttributes.Public | TypeAttributes.Class, basicControllerType);
3132
ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(
@@ -48,23 +49,64 @@ public static Tuple<Assembly, string> GenerateController<TObj, TId, TFilter>(str
4849
{
4950
return new Tuple<Assembly, string>(null, e.Message);
5051
}
52+
}*/
53+
54+
public static Tuple<Assembly, string> GenerateController<TRes, TObj, TId, TFilter>(string assemblyName, string controllerName,
55+
bool saveOnDisk, ControllerType controllerType)
56+
where TRes: class
57+
where TObj: class, IModelIdentifiable<TId>
58+
where TId: IComparable
59+
where TFilter: class, IReadFilterable
60+
{
61+
try
62+
{
63+
AssemblyName name = new AssemblyName(assemblyName);
64+
AssemblyBuilderAccess access = saveOnDisk ? AssemblyBuilderAccess.RunAndCollect : AssemblyBuilderAccess.Run;
65+
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(name, access);
66+
67+
string fullControllerName = $"{controllerName}Controller";
68+
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule($"AutoGenerated{fullControllerName}");
69+
Type basicControllerType = GetAppropriateBaseControllerType<TRes, TObj, TId, TFilter>(controllerType);
70+
TypeBuilder typeBuilder = moduleBuilder.DefineType(fullControllerName,
71+
TypeAttributes.Public | TypeAttributes.Class, basicControllerType);
72+
ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor(
73+
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
74+
CallingConventions.Standard,
75+
new Type[] {typeof(IModelManager<TRes, TObj, TId>)});
76+
ILGenerator ilGenerator = constructorBuilder.GetILGenerator();
77+
// load this
78+
ilGenerator.Emit(OpCodes.Ldarg_0);
79+
// load arg0 -> manager
80+
ilGenerator.Emit(OpCodes.Ldarg_1);
81+
ConstructorInfo baseCtor = basicControllerType.GetConstructor(new []{typeof(IModelManager<TRes, TObj, TId>)});
82+
ilGenerator.Emit(OpCodes.Call, baseCtor);
83+
ilGenerator.Emit(OpCodes.Ret); // Return from the constructor
84+
Type t = typeBuilder.CreateType();
85+
Assembly controllerAssembly = Assembly.GetAssembly(t);
86+
return new Tuple<Assembly, string>(controllerAssembly, String.Empty);
87+
}
88+
catch (Exception e)
89+
{
90+
return new Tuple<Assembly, string>(null, e.Message);
91+
}
5192
}
5293

53-
private static Type GetAppropriateBaseControllerType<TObj, TId, TFilter>(ControllerType controllerType)
94+
private static Type GetAppropriateBaseControllerType<TRes, TObj, TId, TFilter>(ControllerType controllerType)
95+
where TRes : class
5496
where TObj: class, IModelIdentifiable<TId>
5597
where TId: IComparable
5698
where TFilter: class, IReadFilterable
5799
{
58100
switch (controllerType)
59101
{
60102
case ControllerType.ReadOnly:
61-
return typeof(GenericReadOnlyController<TObj, TObj, TId, TFilter>);
103+
return typeof(GenericReadOnlyController<TRes, TObj, TId, TFilter>);
62104
case ControllerType.FullCrud:
63-
return typeof(GenericCrudController<TObj, TObj, TId, TFilter>);
105+
return typeof(GenericCrudController<TRes, TObj, TId, TFilter>);
64106
case ControllerType.Bulk:
65-
return typeof(GenericBulkController<TObj, TObj, TId, TFilter>);
107+
return typeof(GenericBulkController<TRes, TObj, TId, TFilter>);
66108
default:
67-
return typeof(GenericCrudController<TObj, TObj, TId, TFilter>);
109+
return typeof(GenericCrudController<TRes, TObj, TId, TFilter>);
68110
}
69111
}
70112
}

0 commit comments

Comments
 (0)