/// <summary> /// Constructs a DbDataJoiner instance /// </summary> /// <param name="joinOn">Property path from the root object down to the lookup key</param> /// <param name="dimTableTarget">Table information of the remote dimension table</param> /// <param name="option">Option to use for this dataflow</param> /// <param name="batchSize">The batch size for a batched remote look up</param> /// <param name="cacheSize">The local cache item count (part of the remote table)</param> public DbDataJoiner(Expression <Func <TIn, TLookupKey> > joinOn, TargetTable dimTableTarget, DataflowOptions option, int batchSize = 8 * 1024, int cacheSize = 1024 * 1024) : base(option) { m_dimTableTarget = dimTableTarget; m_batchSize = batchSize; m_batcher = new BatchBlock <TIn>(batchSize, option.ToGroupingBlockOption()).ToDataflow(option); m_batcher.Name = "Batcher"; m_lookupNode = new TransformManyDataflow <JoinBatch <TIn>, TIn>(this.JoinBatch, option); m_lookupNode.Name = "LookupNode"; m_typeAccessor = TypeAccessorManager <TIn> .GetAccessorForTable(dimTableTarget); m_keyComparer = typeof(TLookupKey) == typeof(byte[]) ? (IEqualityComparer <TLookupKey>)((object)new ByteArrayEqualityComparer()) : EqualityComparer <TLookupKey> .Default; m_rowCache = new RowCache <TLookupKey>(cacheSize, m_keyComparer); m_logger = Utils.GetNamespaceLogger(); m_joinOnMapping = m_typeAccessor.DbColumnMappings.First(m => m.Host.PropertyInfo == this.ExtractPropertyInfo(joinOn)); var transformer = new TransformBlock <TIn[], JoinBatch <TIn> >( array => new JoinBatch <TIn>(array, CacheLookupStrategy.RemoteLookup), option.ToExecutionBlockOption()).ToDataflow(option); transformer.Name = "ArrayToJoinBatchConverter"; m_batcher.LinkTo(transformer); transformer.LinkTo(m_lookupNode); RegisterChild(m_batcher); RegisterChild(transformer); RegisterChild(m_lookupNode); m_dimInserter = new DimTableInserter(this, dimTableTarget, joinOn, option) { Name = "DimInserter" }; var hb = new HeartbeatNode <JoinBatch <TIn> >(option); m_dimInserter.RegisterDependency(m_lookupNode); m_dimInserter.LinkTo(hb); hb.LinkTo(m_lookupNode); RegisterChild(m_dimInserter); RegisterChild(hb); RegisterChildRing(transformer.CompletionTask, m_lookupNode, m_dimInserter, hb); }
/// <summary> /// Override this method to customize row cache initialization logic /// </summary> protected virtual void InitializeCache(RowCache <TLookupKey> cache) { string selectPartial = string.Format( "select TOP {3} [{0}], [{1}] from {2} order by [{0}] desc", Utils.GetAutoIncrementColumn(m_typeAccessor.SchemaTable.Columns).ColumnName, m_joinOnMapping.DestColumnName, m_dimTableTarget.TableName, cache.SizeLimit); m_logger.DebugFormat("[{0}] Start to initialize cache using sql: {1}", FullName, selectPartial); using (var connection = new SqlConnection(m_dimTableTarget.ConnectionString)) { connection.Open(); SqlCommand command = new SqlCommand(selectPartial, connection); var autoKeyColumn = Utils.GetAutoIncrementColumn(m_typeAccessor.SchemaTable.Columns); bool is64BitAutoKey = autoKeyColumn.DataType == typeof(long); SqlDataReader reader = command.ExecuteReader(); while (reader.Read()) { long dimKey = is64BitAutoKey ? reader.GetInt64(0) : reader.GetInt32(0); //$inserted.AutoKey TLookupKey joinColumnValue = (TLookupKey)reader[1]; //$inserted.JoinOnColumn //add to the subcache no matter it is an "UPDATE" or "INSERT" cache.TryAdd( joinColumnValue, new PartialDimRow <TLookupKey> { AutoIncrementKey = dimKey, JoinOn = joinColumnValue }); } } m_logger.DebugFormat("[{0}] Global cache initialized with {1} items", FullName, cache.Count); }