1
+
using Signum.Utilities.Reflection;
2
+
using Signum.Engine.Linq;
3
+
using System.Data;
4
+
using Signum.Entities.Internal;
5
+
using Signum.Engine.Connection;
6
+
7
+
namespace Signum.Engine.Cache;
8
+
9
+
class CachedLiteTable<T> : CachedTableBase where T : Entity
10
+
{
11
+
public override IColumn? ParentColumn { get; set; }
12
+
13
+
Table table;
14
+
15
+
Alias currentAlias;
16
+
string lastPartialJoin;
17
+
string? remainingJoins;
18
+
19
+
Func<FieldReader, object> rowReader = null!;
20
+
ResetLazy<Dictionary<PrimaryKey, object>> rows = null!;
21
+
Func<object, PrimaryKey> idGetter;
22
+
Dictionary<Type, ICachedLiteModelConstructor> liteModelConstructors = null!;
23
+
24
+
SemiCachedController<T>? semiCachedController;
25
+
26
+
public Dictionary<PrimaryKey, object> GetRows()
27
+
{
28
+
return rows.Value;
29
+
}
30
+
31
+
public CachedLiteTable(ICacheLogicController controller, AliasGenerator aliasGenerator, string lastPartialJoin, string? remainingJoins)
32
+
: base(controller)
33
+
{
34
+
this.table = Schema.Current.Table(typeof(T));
35
+
36
+
HashSet<IColumn> columns = new HashSet<IColumn> { table.PrimaryKey };
37
+
38
+
foreach (var modelType in Lite.GetAllLiteModelTypes(typeof(T)))
39
+
{
40
+
var modelConstructor = Lite.GetModelConstructorExpression(typeof(T), modelType);
41
+
42
+
ToStringColumnsFinderVisitor.GatherColumns(modelConstructor, this.table, columns);
43
+
}
44
+
45
+
var ctr = this.Constructor = new CachedTableConstructor(this, aliasGenerator, columns.ToList());
46
+
47
+
this.lastPartialJoin = lastPartialJoin;
48
+
this.remainingJoins = remainingJoins;
49
+
this.currentAlias = aliasGenerator.NextTableAlias(table.Name.Name);
50
+
var isPostgres = Schema.Current.Settings.IsPostgres;
51
+
52
+
//Query
53
+
using (ObjectName.OverrideOptions(new ObjectNameOptions { AvoidDatabaseName = true }))
54
+
{
55
+
string select = "SELECT {0}\r\nFROM {1} {2}\r\n".FormatWith(
56
+
ctr.columns.ToString(c => currentAlias + "." + c.Name.SqlEscape(isPostgres), ", "),
57
+
table.Name.ToString(),
58
+
currentAlias.ToString());
59
+
60
+
select += this.lastPartialJoin + currentAlias + "." + table.PrimaryKey.Name.SqlEscape(isPostgres) + "\r\n" + this.remainingJoins;
61
+
62
+
query = new SqlPreCommandSimple(select);
63
+
}
64
+
65
+
//Reader
66
+
{
67
+
rowReader = ctr.GetRowReader();
68
+
69
+
idGetter = ctr.GetPrimaryKeyGetter((IColumn)table.PrimaryKey);
70
+
}
71
+
72
+
rows = new ResetLazy<Dictionary<PrimaryKey, object>>(() =>
73
+
{
74
+
return SqlServerRetry.Retry(() =>
75
+
{
76
+
CacheLogic.AssertSqlDependencyStarted();
77
+
78
+
Dictionary<PrimaryKey, object> result = new Dictionary<PrimaryKey, object>();
79
+
80
+
using (MeasureLoad())
81
+
using (Connector.Override(Connector.Current.ForDatabase(table.Name.Schema?.Database)))
82
+
using (var tr = Transaction.ForceNew(IsolationLevel.ReadCommitted))
83
+
{
84
+
if (CacheLogic.LogWriter != null)
85
+
CacheLogic.LogWriter.WriteLine("Load {0}".FormatWith(GetType().TypeName()));
86
+
87
+
Connector.Current.ExecuteDataReaderOptionalDependency(query, OnChange, fr =>
88
+
{
89
+
var obj = rowReader(fr);
90
+
result[idGetter(obj)] = obj; //Could be repeated joins
91
+
});
92
+
tr.Commit();
93
+
}
94
+
95
+
return result;
96
+
});
97
+
}, mode: LazyThreadSafetyMode.ExecutionAndPublication);
98
+
99
+
if (!CacheLogic.WithSqlDependency) //Always semi
100
+
{
101
+
semiCachedController = new SemiCachedController<T>(this);
102
+
}
103
+
}
104
+
105
+
public override void SchemaCompleted()
106
+
{
107
+
this.liteModelConstructors = Lite.GetAllLiteModelTypes(typeof(T))
108
+
.ToDictionary(modelType => modelType, modelType =>
109
+
{
110
+
var modelConstructor = Lite.GetModelConstructorExpression(typeof(T), modelType);
111
+
var cachedModelConstructor = LiteModelExpressionVisitor.giGetCachedLiteModelConstructor.GetInvoker(typeof(T), modelType)(this.Constructor, modelConstructor);
112
+
return cachedModelConstructor;
113
+
});
114
+
115
+
if (this.subTables != null)
116
+
foreach (var item in this.subTables)
117
+
item.SchemaCompleted();
118
+
}
119
+
120
+
protected override void Reset()
121
+
{
122
+
if (rows == null)
123
+
return;
124
+
125
+
if (CacheLogic.LogWriter != null )
126
+
CacheLogic.LogWriter.WriteLine((rows.IsValueCreated ? "RESET {0}" : "Reset {0}").FormatWith(GetType().TypeName()));
127
+
128
+
rows.Reset();
129
+
}
130
+
131
+
protected override void Load()
132
+
{
133
+
if (rows == null)
134
+
return;
135
+
136
+
rows.Load();
137
+
}
138
+
139
+
140
+
public Lite<T> GetLite(PrimaryKey id, IRetriever retriever, Type modelType)
141
+
{
142
+
Interlocked.Increment(ref hits);
143
+
144
+
var model = liteModelConstructors.GetOrThrow(modelType).GetModel(id, retriever);
145
+
146
+
var lite = Lite.Create<T>(id, model);
147
+
retriever.ModifiablePostRetrieving((LiteImp)lite);
148
+
return lite;
149
+
}
150
+
151
+
public override int? Count
152
+
{
153
+
get { return rows.IsValueCreated ? rows.Value.Count : (int?)null; }
154
+
}
155
+
156
+
public override Type Type
157
+
{
158
+
get { return typeof(Lite<T>); }
159
+
}
160
+
161
+
public override ITable Table
162
+
{
163
+
get { return table; }
164
+
}
165
+
166
+
class ToStringColumnsFinderVisitor : ExpressionVisitor
167
+
{
168
+
ParameterExpression param;
169
+
170
+
HashSet<IColumn> columns;
171
+
172
+
Table table;
173
+
174
+
public ToStringColumnsFinderVisitor(ParameterExpression param, HashSet<IColumn> columns, Table table)
175
+
{
176
+
this.param = param;
177
+
this.columns = columns;
178
+
this.table = table;
179
+
}
180
+
181
+
public static Expression GatherColumns(LambdaExpression lambda, Table table, HashSet<IColumn> columns)
182
+
{
183
+
ToStringColumnsFinderVisitor toStr = new ToStringColumnsFinderVisitor(
184
+
lambda.Parameters.SingleEx(),
185
+
columns,
186
+
table
187
+
);
188
+
189
+
var result = toStr.Visit(lambda.Body);
190
+
191
+
return result;
192
+
}
193
+
194
+
195
+
196
+
static MethodInfo miMixin = ReflectionTools.GetMethodInfo((Entity e) => e.Mixin<MixinEntity>()).GetGenericMethodDefinition();
197
+
198
+
protected override Expression VisitMember(MemberExpression node)
199
+
{
200
+
if (node.Expression == param)
201
+
{
202
+
var field = table.GetField(node.Member);
203
+
var column = GetColumn(field);
204
+
columns.Add(column);
205
+
return node;
206
+
}
207
+
208
+
if (node.Expression is MethodCallExpression me && me.Method.IsInstantiationOf(miMixin))
209
+
{
210
+
var type = me.Method.GetGenericArguments()[0];
211
+
var mixin = table.Mixins!.GetOrThrow(type);
212
+
var field = mixin.GetField(node.Member);
213
+
var column = GetColumn(field);
214
+
columns.Add(column);
215
+
return node;
216
+
}
217
+
218
+
return base.VisitMember(node);
219
+
}
220
+
221
+
protected override Expression VisitMethodCall(MethodCallExpression node)
222
+
{
223
+
var obj = base.Visit(node.Object);
224
+
var args = base.Visit(node.Arguments);
225
+
226
+
LambdaExpression? lambda = ExpressionCleaner.GetFieldExpansion(obj?.Type, node.Method);
227
+
228
+
if (lambda != null)
229
+
{
230
+
var replace = ExpressionReplacer.Replace(Expression.Invoke(lambda, obj == null ? args : args.PreAnd(obj)));
231
+
232
+
return this.Visit(replace);
233
+
}
234
+
235
+
if (node.Object == param && node.Method.Name == nameof(node.ToString))
236
+
{
237
+
columns.Add(this.table.ToStrColumn!);
238
+
return node;
239
+
}
240
+
241
+
return base.VisitMethodCall(node);
242
+
}
243
+
244
+
private IColumn GetColumn(Field field)
245
+
{
246
+
if (field is FieldPrimaryKey || field is FieldValue || field is FieldTicks)
247
+
return (IColumn)field;
248
+
249
+
throw new InvalidOperationException("{0} not supported when caching the ToString for a Lite of a transacional entity ({1})".FormatWith(field.GetType().TypeName(), this.table.Type.TypeName()));
250
+
}
251
+
}
252
+
253
+
internal override bool Contains(PrimaryKey primaryKey)
254
+
{
255
+
return this.rows.Value.ContainsKey(primaryKey);
256
+
}
257
+
}
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4