void DisableAndInvalidate() { CacheLogic.DisableAllConnectedTypesInTransaction(this.cachedTable.controller.Type); Transaction.PostRealCommit -= Transaction_PostRealCommit; Transaction.PostRealCommit += Transaction_PostRealCommit; }
public override void AttachInvalidations(SchemaBuilder sb, InvalidateWith invalidateWith, EventHandler invalidate) { if (CacheLogic.GloballyDisabled) { base.AttachInvalidations(sb, invalidateWith, invalidate); } else { EventHandler <CacheEventArgs> onInvalidation = (sender, args) => { if (args == CacheEventArgs.Invalidated) { invalidate(sender, args); } else if (args == CacheEventArgs.Disabled) { if (Transaction.InTestTransaction) { invalidate(sender, args); Transaction.Rolledback += dic => invalidate(sender, args); } Transaction.PostRealCommit += dic => invalidate(sender, args); } }; foreach (var t in invalidateWith.Types) { CacheLogic.TryCacheTable(sb, t); GetController(t).Invalidated += onInvalidation; } } }
void DisableAndInvalidateMassive() { if (CacheLogic.IsAssumedMassiveChangeAsInvalidation <T>()) { DisableAndInvalidate(); } }
static Color GetColor(Type type, Func <Type, bool> cacheHint) { if (type.IsEnumEntityOrSymbol()) { return(Color.Red); } switch (CacheLogic.GetCacheType(type)) { case CacheType.Cached: return(Color.Purple); case CacheType.Semi: return(Color.Pink); } if (typeof(Symbol).IsAssignableFrom(type)) { return(Color.Orange); } if (cacheHint != null && cacheHint(type)) { return(Color.Yellow); } return(Color.Green); }
private Expression GetEntity(bool isLite, IColumn column, Type entityType, CachedTableConstructor constructor) { Expression id = constructor.GetTupleProperty(column); var pk = CachedTableConstructor.WrapPrimaryKey(id); CachedTableConstructor typeConstructor = CacheLogic.GetCacheType(entityType) == CacheType.Cached ? CacheLogic.GetCachedTable(entityType).Constructor : constructor.cachedTable.SubTables !.SingleEx(a => a.ParentColumn == column).Constructor; return(new CachedEntityExpression(pk, entityType, typeConstructor, null !)); }
private Expression GetEntity(bool isLite, IColumn column, Type type) { Expression id = GetTupleProperty(column); if (isLite) { Expression lite; switch (CacheLogic.GetCacheType(type)) { case CacheType.Cached: { lite = Expression.Call(retriever, miRequestLite.MakeGenericMethod(type), Lite.NewExpression(type, NewPrimaryKey(id.UnNullify()), Expression.Constant(null, typeof(string)))); lite = Expression.Call(retriever, miModifiablePostRetrieving.MakeGenericMethod(typeof(LiteImp)), lite.TryConvert(typeof(LiteImp))).TryConvert(lite.Type); break; } case CacheType.Semi: { string lastPartialJoin = CreatePartialInnerJoin(column); CachedTableBase ctb = ciCachedSemiTable.GetInvoker(type)(cachedTable.controller, aliasGenerator !, lastPartialJoin, remainingJoins); if (cachedTable.subTables == null) { cachedTable.subTables = new List <CachedTableBase>(); } cachedTable.subTables.Add(ctb); ctb.ParentColumn = column; lite = Expression.Call(Expression.Constant(ctb), ctb.GetType().GetMethod("GetLite"), NewPrimaryKey(id.UnNullify()), retriever); break; } default: throw new InvalidOperationException("{0} should be cached at this stage".FormatWith(type)); } if (!id.Type.IsNullable()) { return(lite); } return(Expression.Condition(Expression.Equal(id, NullId), Expression.Constant(null, Lite.Generate(type)), lite)); } else { switch (CacheLogic.GetCacheType(type)) { case CacheType.Cached: return(Expression.Call(retriever, miRequest.MakeGenericMethod(type), WrapPrimaryKey(id.Nullify()))); case CacheType.Semi: { string lastPartialJoin = CreatePartialInnerJoin(column); CachedTableBase ctb = ciCachedTable.GetInvoker(type)(cachedTable.controller, aliasGenerator, lastPartialJoin, remainingJoins); if (cachedTable.subTables == null) { cachedTable.subTables = new List <CachedTableBase>(); } cachedTable.subTables.Add(ctb); ctb.ParentColumn = column; var entity = Expression.Parameter(type); LambdaExpression lambda = Expression.Lambda(typeof(Action <>).MakeGenericType(type), Expression.Call(Expression.Constant(ctb), ctb.GetType().GetMethod("Complete"), entity, retriever), entity); return(Expression.Call(retriever, miComplete.MakeGenericMethod(type), WrapPrimaryKey(id.Nullify()), lambda)); } default: throw new InvalidOperationException("{0} should be cached at this stage".FormatWith(type)); } } }
void Transaction_PostRealCommit(Dictionary <string, object> obj) { cachedTable.ResetAll(forceReset: false); CacheLogic.NotifyInvalidateAllConnectedTypes(this.cachedTable.controller.Type); }
public override void SchemaCompleted() { List <IColumn> columns = new List <IColumn> { table.PrimaryKey }; ParameterExpression reader = Expression.Parameter(typeof(FieldReader)); var expression = ToStringExpressionVisitor.GetToString(table, reader, columns); //Query using (ObjectName.OverrideOptions(new ObjectNameOptions { AvoidDatabaseName = true })) { string select = "SELECT {0}\r\nFROM {1} {2}\r\n".FormatWith( columns.ToString(c => currentAlias + "." + c.Name.SqlEscape(), ", "), table.Name.ToString(), currentAlias.ToString()); select += this.lastPartialJoin + currentAlias + "." + table.PrimaryKey.Name.SqlEscape() + "\r\n" + this.remainingJoins; query = new SqlPreCommandSimple(select); } //Reader { var kvpConstructor = Expression.New(CachedTableConstructor.ciKVPIntString, CachedTableConstructor.NewPrimaryKey(FieldReader.GetExpression(reader, 0, this.table.PrimaryKey.Type)), expression); rowReader = Expression.Lambda <Func <FieldReader, KeyValuePair <PrimaryKey, string> > >(kvpConstructor, reader).Compile(); } toStrings = new ResetLazy <Dictionary <PrimaryKey, string> >(() => { CacheLogic.AssertSqlDependencyStarted(); var connector = (SqlConnector)Connector.Current; var subConnector = connector.ForDatabase(table.Name.Schema?.Database); Dictionary <PrimaryKey, string> result = new Dictionary <PrimaryKey, string>(); using (MeasureLoad()) using (Connector.Override(subConnector)) using (Transaction tr = Transaction.ForceNew(IsolationLevel.ReadCommitted)) { if (CacheLogic.LogWriter != null) { CacheLogic.LogWriter.WriteLine("Load {0}".FormatWith(GetType().TypeName())); } ((SqlConnector)Connector.Current).ExecuteDataReaderOptionalDependency(query, OnChange, fr => { var kvp = rowReader(fr); result[kvp.Key] = kvp.Value; }); tr.Commit(); } return(result); }, mode: LazyThreadSafetyMode.ExecutionAndPublication); if (this.subTables != null) { foreach (var item in this.subTables) { item.SchemaCompleted(); } } }
public CachedTableMList(ICacheLogicController controller, TableMList table, AliasGenerator aliasGenerator, string lastPartialJoin, string remainingJoins) : base(controller) { this.table = table; CachedTableConstructor ctr = this.Constructor = new CachedTableConstructor(this, aliasGenerator); //Query using (ObjectName.OverrideOptions(new ObjectNameOptions { AvoidDatabaseName = true })) { string select = "SELECT\r\n{0}\r\nFROM {1} {2}\r\n".FormatWith( ctr.table.Columns.Values.ToString(c => ctr.currentAlias + "." + c.Name.SqlEscape(), ",\r\n"), table.Name.ToString(), ctr.currentAlias.ToString()); ctr.remainingJoins = lastPartialJoin + ctr.currentAlias + "." + table.BackReference.Name.SqlEscape() + "\r\n" + remainingJoins; query = new SqlPreCommandSimple(select); } //Reader { rowReader = ctr.GetRowReader(); } //Completer { List <Expression> instructions = new List <Expression> { Expression.Assign(ctr.origin, Expression.Convert(CachedTableConstructor.originObject, ctr.tupleType)), Expression.Assign(result, ctr.MaterializeField(table.Field)) }; var ci = typeof(MList <T> .RowIdElement).GetConstructor(new [] { typeof(T), typeof(PrimaryKey), typeof(int?) }); var order = table.Order == null?Expression.Constant(null, typeof(int?)) : ctr.GetTupleProperty(table.Order).Nullify(); instructions.Add(Expression.New(ci, result, CachedTableConstructor.NewPrimaryKey(ctr.GetTupleProperty(table.PrimaryKey)), order)); var block = Expression.Block(typeof(MList <T> .RowIdElement), new[] { ctr.origin, result }, instructions); activatorExpression = Expression.Lambda <Func <object, IRetriever, MList <T> .RowIdElement> >(block, CachedTableConstructor.originObject, CachedTableConstructor.retriever); activator = activatorExpression.Compile(); parentIdGetter = ctr.GetPrimaryKeyGetter(table.BackReference); rowIdGetter = ctr.GetPrimaryKeyGetter(table.PrimaryKey); } relationalRows = new ResetLazy <Dictionary <PrimaryKey, Dictionary <PrimaryKey, object> > >(() => { CacheLogic.AssertSqlDependencyStarted(); var connector = (SqlConnector)Connector.Current; var subConnector = connector.ForDatabase(table.Name.Schema?.Database); Dictionary <PrimaryKey, Dictionary <PrimaryKey, object> > result = new Dictionary <PrimaryKey, Dictionary <PrimaryKey, object> >(); using (MeasureLoad()) using (Connector.Override(subConnector)) using (Transaction tr = Transaction.ForceNew(IsolationLevel.ReadCommitted)) { if (CacheLogic.LogWriter != null) { CacheLogic.LogWriter.WriteLine("Load {0}".FormatWith(GetType().TypeName())); } ((SqlConnector)Connector.Current).ExecuteDataReaderOptionalDependency(query, OnChange, fr => { object obj = rowReader(fr); PrimaryKey parentId = parentIdGetter(obj); var dic = result.TryGetC(parentId); if (dic == null) { result[parentId] = dic = new Dictionary <PrimaryKey, object>(); } dic[rowIdGetter(obj)] = obj; }); tr.Commit(); } return(result); }, mode: LazyThreadSafetyMode.ExecutionAndPublication); }
public CachedTable(ICacheLogicController controller, AliasGenerator aliasGenerator, string lastPartialJoin, string remainingJoins) : base(controller) { this.table = Schema.Current.Table(typeof(T)); CachedTableConstructor ctr = this.Constructor = new CachedTableConstructor(this, aliasGenerator); //Query using (ObjectName.OverrideOptions(new ObjectNameOptions { AvoidDatabaseName = true })) { string select = "SELECT\r\n{0}\r\nFROM {1} {2}\r\n".FormatWith( Table.Columns.Values.ToString(c => ctr.currentAlias + "." + c.Name.SqlEscape(), ",\r\n"), table.Name.ToString(), ctr.currentAlias.ToString()); ctr.remainingJoins = lastPartialJoin == null ? null : lastPartialJoin + ctr.currentAlias + ".Id\r\n" + remainingJoins; if (ctr.remainingJoins != null) { select += ctr.remainingJoins; } query = new SqlPreCommandSimple(select); } //Reader { rowReader = ctr.GetRowReader(); } //Completer { ParameterExpression me = Expression.Parameter(typeof(T), "me"); var block = ctr.MaterializeEntity(me, table); completerExpression = Expression.Lambda <Action <object, IRetriever, T> >(block, CachedTableConstructor.originObject, CachedTableConstructor.retriever, me); completer = completerExpression.Compile(); idGetter = ctr.GetPrimaryKeyGetter((IColumn)table.PrimaryKey); } rows = new ResetLazy <Dictionary <PrimaryKey, object> >(() => { CacheLogic.AssertSqlDependencyStarted(); var connector = (SqlConnector)Connector.Current; Table table = connector.Schema.Table(typeof(T)); var subConnector = connector.ForDatabase(table.Name.Schema?.Database); Dictionary <PrimaryKey, object> result = new Dictionary <PrimaryKey, object>(); using (MeasureLoad()) using (Connector.Override(subConnector)) using (Transaction tr = Transaction.ForceNew(IsolationLevel.ReadCommitted)) { if (CacheLogic.LogWriter != null) { CacheLogic.LogWriter.WriteLine("Load {0}".FormatWith(GetType().TypeName())); } ((SqlConnector)Connector.Current).ExecuteDataReaderOptionalDependency(query, OnChange, fr => { object obj = rowReader(fr); result[idGetter(obj)] = obj; //Could be repeated joins }); tr.Commit(); } return(result); }, mode: LazyThreadSafetyMode.ExecutionAndPublication); if (!CacheLogic.WithSqlDependency && lastPartialJoin.HasText()) //Is semi { semiCachedController = new SemiCachedController <T>(this); } }
public static FluentInclude <T> WithCache <T>(this FluentInclude <T> fi) where T : Entity { CacheLogic.TryCacheTable(fi.SchemaBuilder, typeof(T)); return(fi); }
public static List <T> ToListWithInvalidation <T>(this IQueryable <T> simpleQuery, Type type, string exceptionContext, Action <SqlNotificationEventArgs> invalidation) { if (!WithSqlDependency) { throw new InvalidOperationException("ToListWithInvalidation requires SqlDependency"); } ITranslateResult tr; using (ObjectName.OverrideOptions(new ObjectNameOptions { AvoidDatabaseName = true })) tr = ((DbQueryProvider)simpleQuery.Provider).GetRawTranslateResult(simpleQuery.Expression); OnChangeEventHandler onChange = (object sender, SqlNotificationEventArgs args) => { try { if (args.Type != SqlNotificationType.Change) { throw new InvalidOperationException( "Problems with SqlDependency (Type : {0} Source : {1} Info : {2}) on query: \r\n{3}" .FormatWith(args.Type, args.Source, args.Info, tr.MainCommand.PlainSql())); } if (args.Info == SqlNotificationInfo.PreviousFire) { throw new InvalidOperationException("The same transaction that loaded the data is invalidating it!") { Data = { { "query", tr.MainCommand.PlainSql() } } } } ; if (CacheLogic.LogWriter != null) { CacheLogic.LogWriter.WriteLine("Change ToListWithInvalidations {0} {1}".FormatWith(typeof(T).TypeName()), exceptionContext); } invalidation(args); } catch (Exception e) { e.LogException(c => c.ControllerName = exceptionContext); } }; SimpleReader reader = null; Expression <Func <IProjectionRow, T> > projectorExpression = (Expression <Func <IProjectionRow, T> >)tr.GetMainProjector(); Func <IProjectionRow, T> projector = projectorExpression.Compile(); List <T> list = new List <T>(); CacheLogic.AssertSqlDependencyStarted(); Table table = Schema.Current.Table(type); DatabaseName db = table.Name.Schema?.Database; SqlConnector subConnector = ((SqlConnector)Connector.Current).ForDatabase(db); if (CacheLogic.LogWriter != null) { CacheLogic.LogWriter.WriteLine("Load ToListWithInvalidations {0} {1}".FormatWith(typeof(T).TypeName()), exceptionContext); } using (new EntityCache()) using (var r = EntityCache.NewRetriever()) { subConnector.ExecuteDataReaderDependency(tr.MainCommand, onChange, StartSqlDependencyAndEnableBrocker, fr => { if (reader == null) { reader = new SimpleReader(fr, r); } list.Add(projector(reader)); }, CommandType.Text); r.CompleteAll(); } return(list); }