Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement document change tracking #521

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/YesSql.Abstractions/Commands/IExternalCommand.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Collections.Generic;
using System.Data.Common;
using System.Threading.Tasks;

namespace YesSql.Commands
{
public interface IExternalCommand : IIndexCommand
{
IExternalCommand SetBatchCommand(string customBatchSql, IEnumerable<DbParameter> batchCommandParameters = null);
IExternalCommand SetCommand(string customSql, object param = null);
}
}
7 changes: 7 additions & 0 deletions src/YesSql.Abstractions/IConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;
using YesSql.Commands;

namespace YesSql
{
Expand Down Expand Up @@ -90,6 +92,11 @@ public interface IConfiguration
/// Gets or sets the identity column size. Default is <see cref="IdentityColumnSize.Int32"/>.
/// </summary>
IdentityColumnSize IdentityColumnSize { get; set; }
/// <summary>
/// Gets or sets the <see cref="IDocumentChangedEventHandler" /> instance.
/// Suggest inherited from DocumentChangedEventHandlerBase
/// </summary>
IDocumentChangedEventHandler DocumentChangedEventHandler { get; set; }
}

public static class ConfigurationExtensions
Expand Down
14 changes: 14 additions & 0 deletions src/YesSql.Abstractions/IDocumentChangedEventHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using YesSql.Commands;

namespace YesSql
{
public interface IDocumentChangedEventHandler
{
Task<IEnumerable<IExternalCommand>> CreatedAsync(Document document, object entity);
Task<IEnumerable<IExternalCommand>> DeletedAsync(Document document, object entity);
Task<IEnumerable<IExternalCommand>> UpdatedAsync(Document document, object entity);
}
}
13 changes: 10 additions & 3 deletions src/YesSql.Abstractions/Indexes/DescribeContext.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

namespace YesSql.Indexes
{
Expand All @@ -25,12 +27,13 @@ public IEnumerable<IndexDescriptor> Describe(params Type[] types)
});
}

public IMapFor<T, TIndex> For<TIndex>() where TIndex : IIndex

public IMapFor<T, TIndex> For<TIndex>(Type indexType = null) where TIndex : IIndex
{
return For<TIndex, object>();
return For<TIndex, object>(indexType);
}

public IMapFor<T, TIndex> For<TIndex, TKey>() where TIndex : IIndex
public IMapFor<T, TIndex> For<TIndex, TKey>(Type indexType = null) where TIndex : IIndex
{
List<IDescribeFor> descriptors;

Expand All @@ -40,6 +43,10 @@ public IMapFor<T, TIndex> For<TIndex, TKey>() where TIndex : IIndex
}

var describeFor = new IndexDescriptor<T, TIndex, TKey>();
if (indexType != null)
{
describeFor.IndexType = indexType;
}
descriptors.Add(describeFor);

return describeFor;
Expand Down
2 changes: 1 addition & 1 deletion src/YesSql.Abstractions/Indexes/DescribeFor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public class IndexDescriptor<T, TIndex, TKey> : IDescribeFor, IMapFor<T, TIndex>
private Func<object, bool> _filter;

public PropertyInfo GroupProperty { get; set; }
public Type IndexType { get { return typeof(TIndex); } }
public Type IndexType { get; set; } = typeof(TIndex);

public Func<object, bool> Filter => _filter;

Expand Down
50 changes: 50 additions & 0 deletions src/YesSql.Abstractions/Indexes/DynamicIndexDescriptor.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

namespace YesSql.Indexes
{
public class DynamicIndexDescriptor<T, TIndex> : IDescribeFor where TIndex : IIndex
{
private Func<T, Task<IEnumerable<IIndex>>> _map;
public DynamicIndexDescriptor(Func<T, Task<IEnumerable<IIndex>>> mapfn)
{
_map = mapfn;
}

private Func<TIndex, IEnumerable<TIndex>, TIndex> _delete;
private IDescribeFor _reduceDescribeFor;
private Func<object, bool> _filter;


public PropertyInfo GroupProperty { get; set; }

public Type IndexType => typeof(TIndex);

public Func<object, bool> Filter => throw new NotImplementedException();

Func<IIndex, IEnumerable<IIndex>, IIndex> IDescribeFor.GetDelete()
{
if (_reduceDescribeFor != null)
{
return _reduceDescribeFor.GetDelete();
}

return (index, obj) => _delete((TIndex)index, obj.Cast<TIndex>());
}

Func<object, Task<IEnumerable<IIndex>>> IDescribeFor.GetMap()
{
return async x => (await _map((T)x) ?? Enumerable.Empty<IIndex>()).Cast<IIndex>();
}

public Func<IGrouping<object, IIndex>, IIndex> GetReduce()
{
return null;
}

}

}
3 changes: 3 additions & 0 deletions src/YesSql.Abstractions/YesSql.Abstractions.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<Compile Remove="Indexes\DynamicIndexDescriptor.cs" />
</ItemGroup>
<ItemGroup>
<!-- Latest minimum LTS version. We don't want to force a higher version to applications -->
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
Expand Down
3 changes: 3 additions & 0 deletions src/YesSql.Core/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;
using YesSql.Commands;
using YesSql.Data;
using YesSql.Serialization;
using YesSql.Services;
Expand Down Expand Up @@ -42,5 +44,6 @@ public Configuration()
public ICommandInterpreter CommandInterpreter { get; set; }
public ISqlDialect SqlDialect { get; set; }
public IdentityColumnSize IdentityColumnSize { get; set; } = IdentityColumnSize.Int32;
public IDocumentChangedEventHandler DocumentChangedEventHandler { get; set; }
}
}
26 changes: 26 additions & 0 deletions src/YesSql.Core/Provider/DocumentEventHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using YesSql.Commands;

namespace YesSql.Provider
{
public class DocumentChangedEventHandlerBase : IDocumentChangedEventHandler
{
public virtual Task<IEnumerable<IExternalCommand>> CreatedAsync(Document document, object entity)
{
return Task.FromResult(Enumerable.Empty<IExternalCommand>());
}

public virtual Task<IEnumerable<IExternalCommand>> DeletedAsync(Document document, object entity)
{
return Task.FromResult(Enumerable.Empty<IExternalCommand>());
}

public virtual Task<IEnumerable<IExternalCommand>> UpdatedAsync(Document document, object entity)
{
return Task.FromResult(Enumerable.Empty<IExternalCommand>());
}
}
}
38 changes: 37 additions & 1 deletion src/YesSql.Core/Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using YesSql.Data;
using YesSql.Indexes;
using YesSql.Services;
using static Dapper.SqlMapper;

namespace YesSql
{
Expand All @@ -19,6 +20,32 @@ public class Session : ISession
private DbConnection _connection;
private DbTransaction _transaction;

private async Task InvokeHandlerAsync(MapStates mapState, Document document, object entity)
{
if (Store.Configuration.DocumentChangedEventHandler != null)
{
var handler = Store.Configuration.DocumentChangedEventHandler;
var commands = new List<IExternalCommand>();
switch (mapState)
{
case MapStates.New:
commands.AddRange(await handler.CreatedAsync(document, entity));
break;
case MapStates.Update:
commands.AddRange(await handler.UpdatedAsync(document, entity));
break;
case MapStates.Delete:
commands.AddRange(await handler.DeletedAsync(document, entity));
break;
}
if (commands != null && commands.Any())
{
_commands ??= new List<IIndexCommand>();
_commands.AddRange(commands);
}
}
}

internal List<IIndexCommand> _commands;
private readonly Dictionary<string, SessionState> _collectionStates;
private readonly SessionState _defaultState;
Expand Down Expand Up @@ -370,16 +397,18 @@ private async Task UpdateEntityAsync(object entity, bool tracked, string collect

var oldObj = Store.Configuration.ContentSerializer.Deserialize(oldDoc.Content, entity.GetType());

_commands ??= new List<IIndexCommand>();
// Update map index
await MapDeleted(oldDoc, oldObj, collection);

await MapNew(oldDoc, entity, collection);


await CreateConnectionAsync();

oldDoc.Content = newContent;

_commands ??= new List<IIndexCommand>();
await InvokeHandlerAsync(MapStates.Update, oldDoc, entity);

_commands.Add(new UpdateDocumentCommand(oldDoc, Store, version, collection));
}
Expand Down Expand Up @@ -1206,6 +1235,8 @@ private IEnumerable<IndexDescriptor> GetDescriptors(Type t, string collection)

private async Task MapNew(Document document, object obj, string collection)
{
await InvokeHandlerAsync(MapStates.New, document, obj);

var descriptors = GetDescriptors(obj.GetType(), collection);

var state = GetState(collection);
Expand Down Expand Up @@ -1266,6 +1297,8 @@ private async Task MapNew(Document document, object obj, string collection)
/// </summary>
private async Task MapDeleted(Document document, object obj, string collection)
{
await InvokeHandlerAsync(MapStates.Delete, document, obj);

var descriptors = GetDescriptors(obj.GetType(), collection);

var state = GetState(collection);
Expand Down Expand Up @@ -1357,6 +1390,9 @@ public Task CancelAsync()
return ReleaseTransactionAsync();
}


public IStore Store => _store;


}
}
32 changes: 32 additions & 0 deletions test/YesSql.Tests/Events/TestDocumentChangeEventHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using YesSql.Commands;
using YesSql.Provider;

namespace YesSql.Tests.Events
{
public class TestDocumentChangeEventHandler : DocumentChangedEventHandlerBase
{
private readonly IStore _store;
public TestDocumentChangeEventHandler(IStore store)
{
_store = store;
}

public override Task<IEnumerable<IExternalCommand>> CreatedAsync(Document document, object entity)
{
var cmds = new List<IExternalCommand>
{
new ExternalCommand().SetCommand("update " + _store.Configuration.TablePrefix + "Document set ID=@newId;", new { newId = 2 }),
//The command should be executed only once in batches or in a single command
new ExternalCommand()
.SetCommand("update " + _store.Configuration.TablePrefix + "Document set ID=@newId;", new { newId = 5 })
.SetBatchCommand("update " + _store.Configuration.TablePrefix + "Document set ID=ID+10;")
};

return Task.FromResult(cmds.AsEnumerable());
}
}
}
Loading