Skip to content

Commit 1f09ecd

Browse files
Implement a hook to intercept CompileAccessorExpression (#273)
1 parent b62c258 commit 1f09ecd

7 files changed

Lines changed: 147 additions & 6 deletions

File tree

net/DevExtreme.AspNet.Data.Tests/CustomAggregatorsBarrier.cs renamed to net/DevExtreme.AspNet.Data.Tests.Common/StaticBarrier.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
using DevExtreme.AspNet.Data.Aggregation;
2+
using DevExtreme.AspNet.Data.Helpers;
23
using System;
34
using System.Linq;
45

56
namespace DevExtreme.AspNet.Data.Tests {
67

7-
static class CustomAggregatorsBarrier {
8+
public class StaticBarrier {
89
static readonly object SYNC = new object();
910

1011
public static void Run(Action action) {
@@ -13,6 +14,7 @@ public static void Run(Action action) {
1314
action();
1415
} finally {
1516
CustomAggregators.Clear();
17+
CustomAccessorCompilers.Clear();
1618
}
1719
}
1820

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
using DevExpress.Xpo;
2+
using DevExtreme.AspNet.Data.Helpers;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Linq.Expressions;
7+
using Xunit;
8+
9+
namespace DevExtreme.AspNet.Data.Tests.Xpo {
10+
11+
public class XafDomainComponents {
12+
13+
public abstract class DCBaseObject : XPCustomObject {
14+
public DCBaseObject(Session session)
15+
: base(session) {
16+
}
17+
18+
[Key]
19+
public Guid Oid { get; set; }
20+
}
21+
22+
interface IMyComponent {
23+
int Value { get; set; }
24+
}
25+
26+
[Persistent(nameof(XafDomainComponents) + "_" + nameof(MyComponentImpl))]
27+
public class MyComponentImpl : DCBaseObject, IMyComponent {
28+
29+
public MyComponentImpl(Session session)
30+
: base(session) {
31+
}
32+
33+
public int Value { get; set; }
34+
}
35+
36+
const string
37+
OID = "Oid",
38+
LOCK_FILED = "OptimisticLockFieldInDataLayer";
39+
40+
[Fact]
41+
public void Scenario() {
42+
StaticBarrier.Run(delegate {
43+
CustomAccessorCompilers.Register((target, accessorText) => {
44+
if(accessorText == OID) {
45+
return Expression.Property(
46+
Expression.Convert(target, typeof(DCBaseObject)),
47+
OID
48+
);
49+
}
50+
51+
if(accessorText == LOCK_FILED) {
52+
return Expression.Call(
53+
Expression.Convert(target, typeof(PersistentBase)),
54+
"GetPropertyValue",
55+
null,
56+
Expression.Constant(LOCK_FILED)
57+
);
58+
}
59+
60+
return null;
61+
});
62+
63+
var key = Guid.NewGuid();
64+
65+
UnitOfWorkHelper.Exec(uow => {
66+
uow.Save(new MyComponentImpl(uow) {
67+
Oid = key,
68+
Value = 123
69+
});
70+
71+
uow.CommitChanges();
72+
73+
IQueryable<IMyComponent> interfaceQuery = uow.Query<MyComponentImpl>();
74+
75+
var loadResult = DataSourceLoader.Load(interfaceQuery, new SampleLoadOptions {
76+
PrimaryKey = new[] { OID },
77+
RemoteSelect = false,
78+
PreSelect = new[] { OID, "Value", LOCK_FILED }
79+
});
80+
81+
var item = loadResult.data.Cast<IDictionary<string, object>>().First();
82+
83+
Assert.Equal(key, item[OID]);
84+
Assert.Equal(123, item["Value"]);
85+
Assert.Equal(0, item[LOCK_FILED]);
86+
});
87+
});
88+
}
89+
90+
}
91+
92+
}

net/DevExtreme.AspNet.Data.Tests/AggregateCalculatorTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ class SumFixItem {
281281

282282
[Fact]
283283
public void CustomAggregator() {
284-
CustomAggregatorsBarrier.Run(delegate {
284+
StaticBarrier.Run(delegate {
285285
CustomAggregators.RegisterAggregator("comma", typeof(CommaAggregator<>));
286286

287287
var data = new[] {

net/DevExtreme.AspNet.Data.Tests/CustomAggregatorsTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ public class CustomAggregatorsTests {
88

99
[Fact]
1010
public void ShouldNotReturnUnexistingAggregator() {
11-
CustomAggregatorsBarrier.Run(delegate {
11+
StaticBarrier.Run(delegate {
1212
Assert.Null(CustomAggregators.CreateAggregator("custom", new DefaultAccessor<int>()));
1313
});
1414
}
1515

1616
[Fact]
1717
public void ShouldCreateRegisteredAggregator() {
18-
CustomAggregatorsBarrier.Run(delegate {
18+
StaticBarrier.Run(delegate {
1919
CustomAggregators.RegisterAggregator(AggregateName.SUM, typeof(SumAggregator<>));
2020
var aggregator = CustomAggregators.CreateAggregator(AggregateName.SUM, new DefaultAccessor<int>());
2121
Assert.NotNull(aggregator);
@@ -25,7 +25,7 @@ public void ShouldCreateRegisteredAggregator() {
2525

2626
[Fact]
2727
public void ShouldSupportMultipleAggregatorRegistrations() {
28-
CustomAggregatorsBarrier.Run(delegate {
28+
StaticBarrier.Run(delegate {
2929
CustomAggregators.RegisterAggregator("any", typeof(SumAggregator<>));
3030
CustomAggregators.RegisterAggregator("any", typeof(MinAggregator<>));
3131
var aggregator = CustomAggregators.CreateAggregator("any", new DefaultAccessor<int>());

net/DevExtreme.AspNet.Data/ExpressionCompiler.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System;
1+
using DevExtreme.AspNet.Data.Helpers;
2+
using System;
23
using System.Collections.Generic;
34
using System.Dynamic;
45
using System.Linq;
@@ -20,6 +21,10 @@ protected bool GuardNulls {
2021
}
2122

2223
protected internal Expression CompileAccessorExpression(Expression target, string clientExpr, Action<List<Expression>> customizeProgression = null, bool liftToNullable = false) {
24+
var customResult = CustomAccessorCompilers.TryCompile(target, clientExpr);
25+
if(customResult != null)
26+
return customResult;
27+
2328
var progression = new List<Expression> { target };
2429

2530
var clientExprItems = clientExpr.Split('.');
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.ComponentModel;
4+
using System.Linq;
5+
using System.Linq.Expressions;
6+
7+
namespace DevExtreme.AspNet.Data.Helpers {
8+
9+
[EditorBrowsable(EditorBrowsableState.Never)]
10+
public static class CustomAccessorCompilers {
11+
public delegate Expression CompilerFunc(Expression target, string accessorText);
12+
13+
static readonly ICollection<CompilerFunc> _compilers = new List<CompilerFunc>();
14+
15+
public static void Register(CompilerFunc compilerFunc) {
16+
_compilers.Add(compilerFunc);
17+
}
18+
19+
public static Expression TryCompile(Expression target, string accessorText) {
20+
if(_compilers.Count < 1)
21+
return null;
22+
23+
foreach(var compiler in _compilers) {
24+
var result = compiler(target, accessorText);
25+
if(result != null)
26+
return result;
27+
}
28+
29+
return null;
30+
}
31+
32+
#if DEBUG
33+
internal static void Clear() {
34+
_compilers.Clear();
35+
}
36+
#endif
37+
38+
}
39+
40+
}

net/docfx/filterConfig.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
apiRules:
22
- exclude:
33
uidRegex: ^DevExtreme\.AspNet\.Data\.Helpers\.Compat$
4+
- exclude:
5+
uidRegex: ^DevExtreme\.AspNet\.Data\.Helpers\.CustomAccessorCompilers(\.|$)
46
- exclude:
57
uidRegex: ^DevExtreme\.AspNet\.Data\.DataSourceLoadOptionsBase\.(Pre|Remote)Select$
68
- exclude:

0 commit comments

Comments
 (0)