Skip to content

Commit 644151a

Browse files
committed
add support for IsNew and GetType in LINQ provider and LambdaToJavascriptConverter (useful for ToString)
1 parent 3410e83 commit 644151a

12 files changed

Lines changed: 327 additions & 28 deletions

File tree

Signum.Engine.Extensions/Cache/ToStringExpressionVisitor.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ protected override Expression VisitMember(MemberExpression node)
4242

4343
if (exp is CachedEntityExpression cee)
4444
{
45+
if (node.Member.Name == "IsNew")
46+
return Expression.Constant(false);
47+
4548
Field field =
4649
cee.FieldEmbedded != null ? cee.FieldEmbedded.GetField(node.Member) :
4750
cee.FieldMixin != null ? cee.FieldMixin.GetField(node.Member) :
@@ -53,6 +56,26 @@ protected override Expression VisitMember(MemberExpression node)
5356
return node.Update(exp);
5457
}
5558

59+
protected override Expression VisitConditional(ConditionalExpression c) // a.IsNew
60+
{
61+
Expression test = this.Visit(c.Test);
62+
if (test is ConstantExpression co)
63+
{
64+
if ((bool)co.Value!)
65+
return this.Visit(c.IfTrue);
66+
else
67+
return this.Visit(c.IfFalse);
68+
}
69+
70+
Expression ifTrue = this.Visit(c.IfTrue);
71+
Expression ifFalse = this.Visit(c.IfFalse);
72+
if (test != c.Test || ifTrue != c.IfTrue || ifFalse != c.IfFalse)
73+
{
74+
return Expression.Condition(test, ifTrue, ifFalse);
75+
}
76+
return c;
77+
}
78+
5679
private Expression BindMember(CachedEntityExpression n, Field field, Expression? prevPrimaryKey)
5780
{
5881
Expression body = GetField(field, n.Constructor, prevPrimaryKey);
@@ -192,6 +215,7 @@ protected override Expression VisitMethodCall(MethodCallExpression node)
192215
var obj = base.Visit(node.Object);
193216
var args = base.Visit(node.Arguments);
194217

218+
195219
if (node.Method.Name == "ToString" && node.Arguments.IsEmpty() && obj is CachedEntityExpression ce && ce.Type.IsEntity())
196220
{
197221
var table = (Table)ce.Constructor.table;
@@ -212,6 +236,11 @@ protected override Expression VisitMethodCall(MethodCallExpression node)
212236
}
213237
}
214238

239+
if(node.Method.Name == "GetType" && obj is CachedEntityExpression ce2)
240+
{
241+
return Expression.Constant(ce2.Type);
242+
}
243+
215244
LambdaExpression? lambda = ExpressionCleaner.GetFieldExpansion(obj?.Type, node.Method);
216245

217246
if (lambda != null)

Signum.Engine/Database.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public static T Save<T>(this T entity)
5858
}
5959
catch (Exception e)
6060
{
61-
e.Data["entity"] = ((Entity)(IEntity)entity).BaseToString();
61+
e.Data["entity"] = entity;
6262

6363
throw;
6464
}

Signum.Engine/Linq/ExpressionVisitor/QueryBinder.cs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1487,6 +1487,26 @@ protected override MemberAssignment VisitMemberAssignment(MemberAssignment assig
14871487
return assignment;
14881488
}
14891489

1490+
protected override Expression VisitConditional(ConditionalExpression c) // a.IsNew
1491+
{
1492+
Expression test = this.Visit(c.Test);
1493+
if (test is ConstantExpression co)
1494+
{
1495+
if ((bool)co.Value!)
1496+
return this.Visit(c.IfTrue);
1497+
else
1498+
return this.Visit(c.IfFalse);
1499+
}
1500+
1501+
Expression ifTrue = this.Visit(c.IfTrue);
1502+
Expression ifFalse = this.Visit(c.IfFalse);
1503+
if (test != c.Test || ifTrue != c.IfTrue || ifFalse != c.IfFalse)
1504+
{
1505+
return Expression.Condition(test, ifTrue, ifFalse);
1506+
}
1507+
return c;
1508+
}
1509+
14901510
protected override Expression VisitMember(MemberExpression m)
14911511
{
14921512
Expression ex = base.VisitMember(m);
@@ -1539,6 +1559,7 @@ public Expression BindMethodCall(MethodCallExpression m)
15391559
return result;
15401560
}
15411561

1562+
15421563
if (source is ImplementedByExpression ib)
15431564
{
15441565
if (m.Method.IsExtensionMethod())
@@ -1625,9 +1646,39 @@ public Expression BindMethodCall(MethodCallExpression m)
16251646
return tablePeriod;
16261647
}
16271648

1649+
Expression PartialEval(Expression ee)
1650+
{
1651+
if (m.Method.IsExtensionMethod())
1652+
{
1653+
return ExpressionEvaluator.PartialEval(Expression.Call(null, m.Method, m.Arguments.Skip(1).PreAnd(ee)));
1654+
}
1655+
else
1656+
{
1657+
return ExpressionEvaluator.PartialEval(Expression.Call(ee, m.Method, m.Arguments));
1658+
}
1659+
}
1660+
1661+
if (source is TypeEntityExpression type)
1662+
{
1663+
return Condition(Expression.NotEqual(type.ExternalId, NullId(type.ExternalId.ValueType)),
1664+
ifTrue: PartialEval(Expression.Constant(type.TypeValue)),
1665+
ifFalse: Expression.Constant(null, m.Type));
1666+
}
1667+
1668+
if(source is TypeImplementedByExpression typeIB)
1669+
{
1670+
return typeIB.TypeImplementations.Aggregate(
1671+
(Expression)Expression.Constant(null, m.Type),
1672+
(acum, kvp) => Condition(Expression.NotEqual(kvp.Value, NullId(kvp.Value.Value.Type)),
1673+
ifTrue: PartialEval(Expression.Constant(kvp.Key)),
1674+
ifFalse: acum));
1675+
}
1676+
16281677
return m;
16291678
}
16301679

1680+
1681+
16311682

16321683
private ConditionalExpression DispatchConditional(MethodCallExpression m, Expression test, Expression ifTrue, Expression ifFalse)
16331684
{
@@ -1748,6 +1799,9 @@ public Expression BindMemberAccess(MemberExpression m)
17481799
if (pi != null && ReflectionTools.PropertyEquals(pi, EntityExpression.IdOrNullProperty))
17491800
return ee.ExternalId;
17501801

1802+
if (m.Member.Name == nameof(Entity.IsNew))
1803+
return Expression.Constant(false);
1804+
17511805
FieldInfo? fi = m.Member as FieldInfo ?? Reflector.TryFindFieldInfo(ee.Type, pi!);
17521806

17531807
if (fi == null)
@@ -1862,6 +1916,25 @@ public Expression BindMemberAccess(MemberExpression m)
18621916
_ => throw new InvalidOperationException("The member {0} of MListElement is not accesible on queries".FormatWith(m.Member)),
18631917
};
18641918
}
1919+
case DbExpressionType.TypeEntity:
1920+
{
1921+
TypeEntityExpression type = (TypeEntityExpression)source;
1922+
1923+
return Condition(Expression.NotEqual(type.ExternalId, NullId(type.ExternalId.ValueType)),
1924+
ifTrue: ExpressionEvaluator.PartialEval(Expression.MakeMemberAccess(Expression.Constant(type.TypeValue), m.Member)),
1925+
ifFalse: Expression.Constant(null, m.Type));
1926+
}
1927+
1928+
case DbExpressionType.TypeImplementedBy:
1929+
{
1930+
TypeImplementedByExpression typeIB = (TypeImplementedByExpression)source;
1931+
1932+
return typeIB.TypeImplementations.Aggregate(
1933+
(Expression)Expression.Constant(null, m.Type),
1934+
(acum, kvp) => Condition(Expression.NotEqual(kvp.Value, NullId(kvp.Value.Value.Type)),
1935+
ifTrue: ExpressionEvaluator.PartialEval(Expression.MakeMemberAccess(Expression.Constant(kvp.Key), m.Member)),
1936+
ifFalse: acum));
1937+
}
18651938
}
18661939
}
18671940
break;
@@ -3477,6 +3550,7 @@ protected override Expression VisitConditional(ConditionalExpression c)
34773550
var ifTrue = Visit(c.IfTrue);
34783551
var ifFalse = Visit(c.IfFalse);
34793552

3553+
34803554
if (colExpression is LiteReferenceExpression)
34813555
{
34823556
return Combiner<LiteReferenceExpression>(ifTrue, ifFalse, (col, l, r) =>

Signum.Engine/Operations/OperationLogic.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ public static Dictionary<OperationSymbol, string> ServiceCanExecute(Entity entit
329329
}
330330
catch(Exception e)
331331
{
332-
e.Data["entity"] = entity.BaseToString();
332+
e.Data["entity"] = entity;
333333
throw;
334334
}
335335
}

Signum.Entities/Entity.cs

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,12 @@ protected bool SetIfNew<T>(ref T field, T value, [CallerMemberName]string? autom
6666
return base.Set<T>(ref field, value, automaticPropertyName!);
6767
}
6868

69-
public override string ToString()
70-
{
71-
return BaseToString();
72-
}
7369

74-
public string BaseToString()
75-
{
76-
return "{0} ({1})".FormatWith(GetType().NiceName(), id.HasValue ? id.ToString() : LiteMessage.New_G.NiceToString().ForGenderAndNumber(this.GetType().GetGender()));
77-
}
70+
[AutoExpressionField]
71+
public override string ToString() => As.Expression(() => BaseToString());
72+
73+
[AutoExpressionField]
74+
public string BaseToString() => As.Expression(() => IsNew ? GetType().NewNiceName() : GetType().NiceName() + " " + Id);
7875

7976
public override bool Equals(object? obj)
8077
{

Signum.Entities/Lite.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -369,8 +369,6 @@ public enum LiteMessage
369369
IdNotValid,
370370
[Description("Invalid Format")]
371371
InvalidFormat,
372-
[Description("New")]
373-
New_G,
374372
[Description("Type {0} not found")]
375373
Type0NotFound,
376374
[Description("Text")]

Signum.Entities/Reflection/Reflector.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,4 +480,17 @@ public static int NumDecimals(string format)
480480

481481
return str.Length;
482482
}
483+
484+
public static string NiceCount(this Type type, int count)
485+
{
486+
if (count == 1)
487+
return "1 " + type.NiceName();
488+
489+
return count + " " + type.NicePluralName();
490+
}
491+
492+
public static string NewNiceName(this Type type)
493+
{
494+
return NormalWindowMessage.New0_G.NiceToString().ForGenderAndNumber(type.GetGender()).FormatWith(type.NiceName());
495+
}
483496
}

Signum.React/Facades/LambdaToJavascriptConverter.cs

Lines changed: 86 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ internal class LambdaToJavascriptConverter
1010
if (lambdaExpression == null)
1111
return null;
1212

13-
var body = ToJavascript(lambdaExpression.Parameters.Single(), lambdaExpression.Body);
13+
var newLambda = (LambdaExpression)ExpressionCleaner.Clean(lambdaExpression)!;
14+
15+
var body = ToJavascript(newLambda.Parameters.Single(), newLambda.Body);
1416

1517
if (body == null)
1618
return null;
@@ -30,14 +32,22 @@ internal class LambdaToJavascriptConverter
3032
if (param == expr)
3133
return "e";
3234

33-
if (expr is ConstantExpression ce && expr.Type == typeof(string))
35+
if (expr is ConstantExpression ce)
3436
{
35-
var str = (string)ce.Value!;
37+
if (expr.Type == typeof(string))
38+
{
39+
var str = (string)ce.Value!;
3640

37-
if (!str.HasText())
38-
return "\"\"";
41+
if (!str.HasText())
42+
return "\"\"";
3943

40-
return "\"" + str.Replace(replacements) + "\"";
44+
return "\"" + str.Replace(replacements) + "\"";
45+
}
46+
47+
if(ce.Value == null)
48+
{
49+
return "null";
50+
}
4151
}
4252

4353
if (expr is MemberExpression me)
@@ -58,22 +68,69 @@ internal class LambdaToJavascriptConverter
5868
return a + "." + me.Member.Name.FirstLower();
5969
}
6070

61-
if (expr is BinaryExpression be && be.NodeType == ExpressionType.Add)
71+
if (expr is BinaryExpression be)
6272
{
63-
var a = ToJavascriptToString(param, be.Left);
64-
var b = ToJavascriptToString(param, be.Right);
73+
if (be.NodeType == ExpressionType.Add && be.Type == typeof(string))
74+
{
6575

66-
if (a != null && b != null)
67-
return "(" + a + " + " + b + ")";
76+
var a = ToJavascriptToString(param, be.Left);
77+
var b = ToJavascriptToString(param, be.Right);
6878

69-
return null;
79+
if (a != null && b != null)
80+
return "(" + a + " + " + b + ")";
81+
82+
return null;
83+
}
84+
else
85+
{
86+
var a = ToJavascript(param, be.Left);
87+
var b = ToJavascript(param, be.Right);
88+
89+
var op = ToJsOperator(be.NodeType);
90+
91+
if (a != null && op != null && b != null)
92+
{
93+
return a + op + b;
94+
}
95+
96+
return null;
97+
}
7098
}
7199

72100
if (expr is MethodCallExpression mc)
73101
{
74102
if (mc.Method.Name == "ToString")
75103
return ToJavascriptToString(param, mc.Object!, mc.TryGetArgument("format") is ConstantExpression format ? (string)format.Value! : null);
76104

105+
if (mc.Method.Name == "GetType" && mc.Object != null && mc.Object.Type.IsIEntity())
106+
{
107+
var obj = ToJavascript(param, mc.Object!);
108+
if (obj == null)
109+
return null;
110+
111+
return "getTypeInfo(" + obj + ")";
112+
}
113+
114+
if (mc.Method.IsExtensionMethod() && mc.Arguments.Only()?.Type == typeof(Type))
115+
{
116+
var obj = ToJavascript(param, mc.Arguments.SingleEx());
117+
if (obj == null)
118+
return null;
119+
120+
if (mc.Method.Name == nameof(DescriptionManager.NiceName))
121+
return obj + ".niceName";
122+
123+
124+
if (mc.Method.Name == nameof(DescriptionManager.NicePluralName))
125+
return obj + ".nicePluralName";
126+
127+
if (mc.Method.Name == nameof(Reflector.NewNiceName))
128+
return "newNiceName(" + obj + ")";
129+
130+
return null;
131+
}
132+
133+
77134
if (mc.Method.DeclaringType == typeof(DateTime))
78135
{
79136
switch (mc.Method.Name)
@@ -129,6 +186,23 @@ internal class LambdaToJavascriptConverter
129186
return null;
130187
}
131188

189+
private static string? ToJsOperator(ExpressionType nodeType)
190+
{
191+
return nodeType switch
192+
{
193+
ExpressionType.Add => "+",
194+
ExpressionType.Subtract => "-",
195+
ExpressionType.Equal => "==",
196+
ExpressionType.NotEqual => "!=",
197+
ExpressionType.LessThan => "<",
198+
ExpressionType.LessThanOrEqual => "<",
199+
ExpressionType.GreaterThan => ">",
200+
ExpressionType.GreaterThanOrEqual => ">=",
201+
ExpressionType.Coalesce => "??",
202+
_ => null
203+
};
204+
}
205+
132206
private static string? ToJavascriptToString(ParameterExpression param, Expression expr, string? format = null)
133207
{
134208
var r = ToJavascript(param, expr);

Signum.React/Scripts/Reflection.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { DateTime, DateTimeFormatOptions, Duration, DurationObjectUnits, Settings } from 'luxon';
22
import { Dic } from './Globals';
3-
import { ModifiableEntity, Entity, Lite, MListElement, ModelState, MixinEntity } from './Signum.Entities'; //ONLY TYPES or Cyclic problems in Webpack!
3+
import type { ModifiableEntity, Entity, Lite, MListElement, ModelState, MixinEntity } from './Signum.Entities'; //ONLY TYPES or Cyclic problems in Webpack!
44
import { ajaxGet } from './Services';
55
import { MList } from "./Signum.Entities";
66
import * as AppContext from './AppContext';
@@ -1506,8 +1506,6 @@ export interface ISymbol {
15061506

15071507
let missingSymbols: ISymbol[] = [];
15081508

1509-
1510-
15111509
function getMember(key: string): MemberInfo | undefined {
15121510

15131511
if (!key.contains("."))

0 commit comments

Comments
 (0)