diff --git a/src/YesSql.Abstractions/Commands/DocumentChanged/DocumentChangeContext.cs b/src/YesSql.Abstractions/Commands/DocumentChanged/DocumentChangeContext.cs new file mode 100644 index 00000000..ac6ce03c --- /dev/null +++ b/src/YesSql.Abstractions/Commands/DocumentChanged/DocumentChangeContext.cs @@ -0,0 +1,15 @@ +using System.Data.Common; + +namespace YesSql.Commands.DocumentChanged +{ + public class DocumentChangeContext + { + public ISession Session { get; set; } + public object Entity { get; set; } + public Document Document { get; set; } + public IStore Store { get; set; } + public DbConnection Connection { get; set; } + public DbTransaction Transaction { get; set; } + public ISqlDialect Dialect { get; set; } + } +} diff --git a/src/YesSql.Abstractions/Commands/DocumentChanged/DocumentChangedInBatchContext.cs b/src/YesSql.Abstractions/Commands/DocumentChanged/DocumentChangedInBatchContext.cs new file mode 100644 index 00000000..1982ce73 --- /dev/null +++ b/src/YesSql.Abstractions/Commands/DocumentChanged/DocumentChangedInBatchContext.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.Data.Common; + +namespace YesSql.Commands.DocumentChanged +{ + public class DocumentChangeInBatchContext + { + public object Entity { get; set; } + public List Queries { get; set; } + public DbCommand BatchCommand { get; set; } + public ISession Session { get; set; } + public Document Document { get; set; } + } +} diff --git a/src/YesSql.Core/Commands/DocumentCommand.cs b/src/YesSql.Abstractions/Commands/DocumentCommand.cs similarity index 88% rename from src/YesSql.Core/Commands/DocumentCommand.cs rename to src/YesSql.Abstractions/Commands/DocumentCommand.cs index cc84336d..b1211fde 100644 --- a/src/YesSql.Core/Commands/DocumentCommand.cs +++ b/src/YesSql.Abstractions/Commands/DocumentCommand.cs @@ -9,15 +9,15 @@ namespace YesSql.Commands { public abstract class DocumentCommand : IIndexCommand, ICollectionName { - protected static readonly PropertyInfo[] AllProperties = new PropertyInfo[] - { + protected static readonly PropertyInfo[] AllProperties = + [ typeof(Document).GetProperty("Type") - }; + ]; - protected static readonly PropertyInfo[] AllKeys = new PropertyInfo[] - { + protected static readonly PropertyInfo[] AllKeys = + [ typeof(Document).GetProperty("Id") - }; + ]; public abstract int ExecutionOrder { get; } diff --git a/src/YesSql.Abstractions/Commands/IDocumentCommandHandler.cs b/src/YesSql.Abstractions/Commands/IDocumentCommandHandler.cs new file mode 100644 index 00000000..54bd56b6 --- /dev/null +++ b/src/YesSql.Abstractions/Commands/IDocumentCommandHandler.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; +using YesSql.Commands.DocumentChanged; + +namespace YesSql.Commands +{ + public interface IDocumentCommandHandler + { + Task CreatedAsync(DocumentChangeContext context); + bool CreatedInBatch(DocumentChangeInBatchContext context); + + Task RemovingAsync(DocumentChangeContext context); + bool RemovingInBatch(DocumentChangeInBatchContext context); + Task UpdatedAsync(DocumentChangeContext context); + bool UpdatedInBatch(DocumentChangeInBatchContext context); + } +} diff --git a/src/YesSql.Core/Commands/IIndexCommand.cs b/src/YesSql.Abstractions/Commands/IIndexCommand.cs similarity index 100% rename from src/YesSql.Core/Commands/IIndexCommand.cs rename to src/YesSql.Abstractions/Commands/IIndexCommand.cs diff --git a/src/YesSql.Abstractions/ISession.cs b/src/YesSql.Abstractions/ISession.cs index e5080919..5dc4f87d 100644 --- a/src/YesSql.Abstractions/ISession.cs +++ b/src/YesSql.Abstractions/ISession.cs @@ -3,6 +3,7 @@ using System.Data; using System.Data.Common; using System.Threading.Tasks; +using YesSql.Commands; using YesSql.Indexes; namespace YesSql @@ -147,5 +148,9 @@ public interface ISession : IDisposable, IAsyncDisposable /// Gets the instance that created this session. /// IStore Store { get; } + + IEnumerable ExtraIndexDescriptors { get; set; } + Func>> BuildExtraIndexDescriptors { get; set; } + IDocumentCommandHandler DocumentCommandHandler { get; set; } } } diff --git a/src/YesSql.Abstractions/IStore.cs b/src/YesSql.Abstractions/IStore.cs index 403d6e0f..061e8e52 100644 --- a/src/YesSql.Abstractions/IStore.cs +++ b/src/YesSql.Abstractions/IStore.cs @@ -43,6 +43,6 @@ public interface IStore : IDisposable /// /// Returns the instance used to create this store. /// - ITypeService TypeNames { get; } + ITypeService TypeService { get; set; } } } diff --git a/src/YesSql.Abstractions/ITypeService.cs b/src/YesSql.Abstractions/ITypeService.cs index 95775165..dd5d9668 100644 --- a/src/YesSql.Abstractions/ITypeService.cs +++ b/src/YesSql.Abstractions/ITypeService.cs @@ -1,4 +1,6 @@ using System; +using System.Reflection; +using YesSql.Serialization; namespace YesSql { @@ -16,5 +18,8 @@ public interface ITypeService /// Gets the type represented by a string. /// Type this[string s] { get; } + + PropertyInfo[] GetProperties(Type type); + PropertyInfoAccessor GetPropertyAccessors(PropertyInfo property, Func createFactory); } } diff --git a/src/YesSql.Core/Serialization/PropertyInfoAccessor.cs b/src/YesSql.Abstractions/PropertyInfoAccessor.cs similarity index 100% rename from src/YesSql.Core/Serialization/PropertyInfoAccessor.cs rename to src/YesSql.Abstractions/PropertyInfoAccessor.cs diff --git a/src/YesSql.Core/Commands/CreateDocumentCommand.cs b/src/YesSql.Core/Commands/CreateDocumentCommand.cs index ff0fd632..92940164 100644 --- a/src/YesSql.Core/Commands/CreateDocumentCommand.cs +++ b/src/YesSql.Core/Commands/CreateDocumentCommand.cs @@ -4,21 +4,25 @@ using System.Collections.Generic; using System.Data.Common; using System.Threading.Tasks; +using YesSql.Commands.DocumentChanged; namespace YesSql.Commands { - public sealed class CreateDocumentCommand : DocumentCommand + public class CreateDocumentCommand : DocumentCommand { private readonly IStore _store; - + private readonly ISession _session; + private readonly object _entity; public override int ExecutionOrder { get; } = 0; - public CreateDocumentCommand(Document document, IStore store, string collection) : base(document, collection) + public CreateDocumentCommand(object entity, Document document, IStore store, string collection, ISession session) : base(document, collection) { _store = store; + _session = session; + _entity = entity; } - public override Task ExecuteAsync(DbConnection connection, DbTransaction transaction, ISqlDialect dialect, ILogger logger) + public override async Task ExecuteAsync(DbConnection connection, DbTransaction transaction, ISqlDialect dialect, ILogger logger) { var documentTable = _store.Configuration.TableNameConvention.GetDocumentTable(Collection); @@ -28,8 +32,19 @@ public override Task ExecuteAsync(DbConnection connection, DbTransaction transac { logger.LogTrace(insertCmd); } + await connection.ExecuteAsync(insertCmd, Document, transaction); - return connection.ExecuteAsync(insertCmd, Document, transaction); + var context = new DocumentChangeContext + { + Session = _session, + Entity = _entity, + Document = Document, + Store = _store, + Connection = connection, + Transaction = transaction, + Dialect = dialect, + }; + await _session.DocumentCommandHandler.CreatedAsync(context); } public override bool AddToBatch(ISqlDialect dialect, List queries, DbCommand batchCommand, List> actions, int index) @@ -45,6 +60,16 @@ public override bool AddToBatch(ISqlDialect dialect, List queries, DbCom .AddParameter("Content_" + index, Document.Content) .AddParameter("Version_" + index, Document.Version); + var context = new DocumentChangeInBatchContext + { + Session = _session, + Document = Document, + Entity = _entity, + BatchCommand = batchCommand, + Queries = queries, + }; + _session.DocumentCommandHandler.CreatedInBatch(context); + return true; } } diff --git a/src/YesSql.Core/Commands/DefaultDocumentCommandHandler.cs b/src/YesSql.Core/Commands/DefaultDocumentCommandHandler.cs new file mode 100644 index 00000000..9666f3d1 --- /dev/null +++ b/src/YesSql.Core/Commands/DefaultDocumentCommandHandler.cs @@ -0,0 +1,20 @@ +using System.Threading.Tasks; +using YesSql.Commands.DocumentChanged; + +namespace YesSql.Commands +{ + public class DefaultDocumentCommandHandler : IDocumentCommandHandler + { + public Task CreatedAsync(DocumentChangeContext context) => Task.CompletedTask; + + public bool CreatedInBatch(DocumentChangeInBatchContext context) => true; + + public Task RemovingAsync(DocumentChangeContext context) => Task.CompletedTask; + + public bool RemovingInBatch(DocumentChangeInBatchContext context) => true; + + public Task UpdatedAsync(DocumentChangeContext context) => Task.CompletedTask; + + public bool UpdatedInBatch(DocumentChangeInBatchContext context) => true; + } +} diff --git a/src/YesSql.Core/Commands/DeleteDocumentCommand.cs b/src/YesSql.Core/Commands/DeleteDocumentCommand.cs index 7ca257ae..bf52bf3f 100644 --- a/src/YesSql.Core/Commands/DeleteDocumentCommand.cs +++ b/src/YesSql.Core/Commands/DeleteDocumentCommand.cs @@ -4,34 +4,61 @@ using System.Collections.Generic; using System.Data.Common; using System.Threading.Tasks; +using YesSql.Commands.DocumentChanged; namespace YesSql.Commands { - public sealed class DeleteDocumentCommand : DocumentCommand + public class DeleteDocumentCommand : DocumentCommand { private readonly IStore _store; + private readonly ISession _session; + private readonly object _entity; public override int ExecutionOrder { get; } = 4; - public DeleteDocumentCommand(Document document, IStore store, string collection) : base(document, collection) + public DeleteDocumentCommand(object entity, Document document, IStore store, string collection, Session session) : base(document, collection) { _store = store; + _session = session; + _entity = entity; } - public override Task ExecuteAsync(DbConnection connection, DbTransaction transaction, ISqlDialect dialect, ILogger logger) + public async override Task ExecuteAsync(DbConnection connection, DbTransaction transaction, ISqlDialect dialect, ILogger logger) { + var context = new DocumentChangeContext + { + Session = _session, + Entity = _entity, + Document = Document, + Store = _store, + Connection = connection, + Transaction = transaction, + Dialect = dialect, + }; + await _session.DocumentCommandHandler.RemovingAsync(context); + var documentTable = _store.Configuration.TableNameConvention.GetDocumentTable(Collection); var deleteCmd = $"delete from {dialect.QuoteForTableName(_store.Configuration.TablePrefix + documentTable, _store.Configuration.Schema)} where {dialect.QuoteForColumnName("Id")} = @Id;"; - + if (logger.IsEnabled(LogLevel.Trace)) { logger.LogTrace(deleteCmd); } - - return connection.ExecuteAsync(deleteCmd, Document, transaction); + + await connection.ExecuteAsync(deleteCmd, Document, transaction); } public override bool AddToBatch(ISqlDialect dialect, List queries, DbCommand command, List> actions, int index) { + var context = new DocumentChangeInBatchContext + { + Session = _session, + Document = Document, + Entity = _entity, + BatchCommand = command, + Queries = queries, + }; + _session.DocumentCommandHandler.RemovingInBatch(context); + var documentTable = _store.Configuration.TableNameConvention.GetDocumentTable(Collection); var deleteCmd = $"delete from {dialect.QuoteForTableName(_store.Configuration.TablePrefix + documentTable, _store.Configuration.Schema)} where {dialect.QuoteForColumnName("Id")} = @Id_{index};"; diff --git a/src/YesSql.Core/Commands/IndexCommand.cs b/src/YesSql.Core/Commands/IndexCommand.cs index 67bc7a3b..4af27676 100644 --- a/src/YesSql.Core/Commands/IndexCommand.cs +++ b/src/YesSql.Core/Commands/IndexCommand.cs @@ -2,12 +2,12 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Data; using System.Data.Common; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; +using YesSql.Data; using YesSql.Indexes; using YesSql.Serialization; @@ -19,8 +19,6 @@ public abstract class IndexCommand : IIndexCommand protected readonly IStore _store; - private static readonly ConcurrentDictionary PropertyAccessors = new(); - private static readonly ConcurrentDictionary TypeProperties = new(); private static readonly ConcurrentDictionary InsertsList = new(); private static readonly ConcurrentDictionary UpdatesList = new(); @@ -47,13 +45,13 @@ public static void ResetQueryCache() UpdatesList.Clear(); } - protected static void GetProperties(DbCommand command, object item, string suffix, ISqlDialect dialect) + protected void GetProperties(DbCommand command, object item, string suffix, ISqlDialect dialect) { var type = item.GetType(); foreach (var property in TypePropertiesCache(type)) { - var accessor = PropertyAccessors.GetOrAdd(property, p => new PropertyInfoAccessor(p)); + var accessor = _store.TypeService.GetPropertyAccessors(property, prop => new PropertyInfoAccessor(prop)); var value = accessor.Get(item); @@ -65,16 +63,9 @@ protected static void GetProperties(DbCommand command, object item, string suffi } } - protected static PropertyInfo[] TypePropertiesCache(Type type) + protected PropertyInfo[] TypePropertiesCache(Type type) { - if (TypeProperties.TryGetValue(type.FullName, out var pis)) - { - return pis; - } - - var properties = type.GetProperties().Where(IsWriteable).ToArray(); - TypeProperties[type.FullName] = properties; - return properties; + return _store.TypeService.GetProperties(type); } protected string Inserts(Type type, ISqlDialect dialect) @@ -178,15 +169,7 @@ protected string Updates(Type type, ISqlDialect dialect) return result; } - private static bool IsWriteable(PropertyInfo pi) - { - return - pi.Name != nameof(IIndex.Id) && - // don't read DocumentId when on a MapIndex as it might be used to - // read the DocumentId directly from an Index query - pi.Name != "DocumentId" - ; - } + public abstract bool AddToBatch(ISqlDialect dialect, List queries, DbCommand batchCommand, List> actions, int index); diff --git a/src/YesSql.Core/Commands/UpdateDocumentCommand.cs b/src/YesSql.Core/Commands/UpdateDocumentCommand.cs index d1f24268..5547578f 100644 --- a/src/YesSql.Core/Commands/UpdateDocumentCommand.cs +++ b/src/YesSql.Core/Commands/UpdateDocumentCommand.cs @@ -4,20 +4,24 @@ using System.Collections.Generic; using System.Data.Common; using System.Threading.Tasks; +using YesSql.Commands.DocumentChanged; namespace YesSql.Commands { - public sealed class UpdateDocumentCommand : DocumentCommand + public class UpdateDocumentCommand : DocumentCommand { private readonly IStore _store; + private readonly ISession _session; + private readonly object _entity; private readonly long _checkVersion; - public override int ExecutionOrder { get; } = 2; - public UpdateDocumentCommand(Document document, IStore store, long checkVersion, string collection) : base(document, collection) + public UpdateDocumentCommand(object entity, Document document, IStore store, long checkVersion, string collection, Session session) : base(document, collection) { _store = store; _checkVersion = checkVersion; + _session = session; + _entity = entity; } public override async Task ExecuteAsync(DbConnection connection, DbTransaction transaction, ISqlDialect dialect, ILogger logger) @@ -46,7 +50,17 @@ public override async Task ExecuteAsync(DbConnection connection, DbTransaction t throw new ConcurrencyException(Document); } - return; + var context = new DocumentChangeContext + { + Session = _session, + Entity = _entity, + Document = Document, + Store = _store, + Connection = connection, + Transaction = transaction, + Dialect = dialect, + }; + await _session.DocumentCommandHandler.UpdatedAsync(context); } public override bool AddToBatch(ISqlDialect dialect, List queries, DbCommand batchCommand, List> actions, int index) @@ -75,6 +89,16 @@ public override bool AddToBatch(ISqlDialect dialect, List queries, DbCom .AddParameter("Content_" + index, Document.Content) .AddParameter("Version_" + index, Document.Version); + var context = new DocumentChangeInBatchContext + { + Session = _session, + Entity = _entity, + Document = Document, + BatchCommand = batchCommand, + Queries = queries, + }; + _session.DocumentCommandHandler.UpdatedInBatch(context); + return true; } } diff --git a/src/YesSql.Core/Services/DefaultQuery.cs b/src/YesSql.Core/Services/DefaultQuery.cs index 04ad5667..2da6e4de 100644 --- a/src/YesSql.Core/Services/DefaultQuery.cs +++ b/src/YesSql.Core/Services/DefaultQuery.cs @@ -1180,7 +1180,7 @@ IQuery IQuery.For(bool filterType) if (filterType) { _queryState._sqlBuilder.WhereAnd(_queryState._sqlBuilder.FormatColumn(_queryState._documentTable, "Type", _queryState._store.Configuration.Schema) + " = @Type"); // TODO: investigate, this makes the query 3 times slower on sqlite - _queryState._sqlBuilder.Parameters["@Type"] = _session.Store.TypeNames[typeof(T)]; + _queryState._sqlBuilder.Parameters["@Type"] = _session.Store.TypeService[typeof(T)]; } return new Query(this); diff --git a/src/YesSql.Core/Services/TypeService.cs b/src/YesSql.Core/Services/TypeService.cs index 7792496f..4cfa8950 100644 --- a/src/YesSql.Core/Services/TypeService.cs +++ b/src/YesSql.Core/Services/TypeService.cs @@ -1,6 +1,10 @@ using System; using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; using System.Reflection; +using YesSql.Indexes; +using YesSql.Serialization; namespace YesSql.Services { @@ -10,6 +14,9 @@ public class TypeService : ITypeService private readonly ConcurrentDictionary nameTypes = new(); + private static readonly ConcurrentDictionary PropertyAccessors = new(); + private static readonly ConcurrentDictionary TypeProperties = new(); + public string this[Type t] { get @@ -56,5 +63,39 @@ private static bool IsAnonymousType(TypeInfo type) && (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$")) && (type.Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic; } + + public PropertyInfo[] GetProperties(Type type) + { + if (TypeProperties.TryGetValue(type, out var pis)) + { + return pis; + } + + var properties = type.GetProperties().Where(IsWriteable).ToArray(); + + var oldType = TypeProperties.FirstOrDefault(x => x.Key.FullName == type.FullName && x.Key != type); + if (oldType.Key != null) + { + TypeProperties.Remove(oldType.Key, out _); + } + + TypeProperties[type] = properties; + return properties; + } + + public PropertyInfoAccessor GetPropertyAccessors(PropertyInfo property, Func createFactory) + { + return PropertyAccessors.GetOrAdd(property, createFactory(property)); + } + + private static bool IsWriteable(PropertyInfo pi) + { + return + pi.Name != nameof(IIndex.Id) && + // don't read DocumentId when on a MapIndex as it might be used to + // read the DocumentId directly from an Index query + pi.Name != "DocumentId" + ; + } } } diff --git a/src/YesSql.Core/Session.cs b/src/YesSql.Core/Session.cs index a3647505..3cd2ec8d 100644 --- a/src/YesSql.Core/Session.cs +++ b/src/YesSql.Core/Session.cs @@ -34,8 +34,11 @@ public class Session : ISession protected string _tablePrefix; private readonly ISqlDialect _dialect; private readonly ILogger _logger; + public IEnumerable ExtraIndexDescriptors { get; set; } = []; + public IDocumentCommandHandler DocumentCommandHandler { get; set; } private readonly bool _withTracking; + public Func>> BuildExtraIndexDescriptors { get; set; } private readonly bool _enableThreadSafetyChecks; private int _asyncOperations = 0; private string _previousStackTrace = null; @@ -53,6 +56,7 @@ public Session(Store store, bool withTracking = true) { [string.Empty] = _defaultState }; + DocumentCommandHandler = new DefaultDocumentCommandHandler(); } public ISession RegisterIndexes(IIndexProvider[] indexProviders, string collection = null) @@ -165,7 +169,7 @@ public bool Import(object entity, long id = 0, long version = 0, string collecti var doc = new Document { - Type = Store.TypeNames[entity.GetType()], + Type = Store.TypeService[entity.GetType()], Content = Store.Configuration.ContentSerializer.Serialize(entity) }; @@ -271,7 +275,7 @@ private async Task SaveEntityAsync(object entity, string collection) var doc = new Document { - Type = Store.TypeNames[entity.GetType()] + Type = Store.TypeService[entity.GetType()] }; if (!state.IdentityMap.TryGetDocumentId(entity, out var id)) @@ -300,7 +304,7 @@ private async Task SaveEntityAsync(object entity, string collection) _commands ??= []; - _commands.Add(new CreateDocumentCommand(doc, Store, collection)); + _commands.Add(new CreateDocumentCommand(entity, doc, Store, collection, this)); state.IdentityMap.AddDocument(doc); @@ -390,7 +394,7 @@ private async Task UpdateEntityAsync(object entity, bool tracked, string collect _commands ??= []; - _commands.Add(new UpdateDocumentCommand(oldDoc, Store, version, collection)); + _commands.Add(new UpdateDocumentCommand(entity, oldDoc, Store, version, collection, this)); } private async Task GetDocumentByIdAsync(long id, string collection) @@ -469,7 +473,7 @@ private async Task DeleteEntityAsync(object obj, string collection) _commands ??= []; // The command needs to come after any index deletion because of the database constraints - _commands.Add(new DeleteDocumentCommand(doc, Store, collection)); + _commands.Add(new DeleteDocumentCommand(obj, doc, Store, collection, this)); } } @@ -531,7 +535,7 @@ public IEnumerable Get(IList documents, string collection) where var result = new List(); var defaultAccessor = _store.GetIdAccessor(typeof(T)); - var typeName = Store.TypeNames[typeof(T)]; + var typeName = Store.TypeService[typeof(T)]; var state = GetState(collection); @@ -550,7 +554,7 @@ public IEnumerable Get(IList documents, string collection) where // If the document type doesn't match the requested one, check it's a base type if (!string.Equals(typeName, d.Type, StringComparison.Ordinal)) { - var itemType = Store.TypeNames[d.Type]; + var itemType = Store.TypeService[d.Type]; // Ignore the document if it can't be casted to the requested type if (!typeof(T).IsAssignableFrom(itemType)) @@ -1225,7 +1229,7 @@ private Func GetGroupingMethod(IndexDescriptor descriptor) /// /// Resolves all the descriptors registered on the Store and the Session /// - private IEnumerable GetDescriptors(Type t, string collection) + private async Task> GetDescriptorsAsync(Type t, string collection) { _descriptors ??= new Dictionary>(); @@ -1246,12 +1250,20 @@ private IEnumerable GetDescriptors(Type t, string collection) _descriptors.Add(cacheKey, typedDescriptors); } - return typedDescriptors; - } + if (BuildExtraIndexDescriptors != null) + { + var dynamicIndexDes = await BuildExtraIndexDescriptors(t, collection); + if (dynamicIndexDes != null) + { + return typedDescriptors.Union(ExtraIndexDescriptors.Union(dynamicIndexDes)); + } + } + return typedDescriptors.Union(ExtraIndexDescriptors); + } private async Task MapNew(Document document, object obj, string collection) { - var descriptors = GetDescriptors(obj.GetType(), collection); + var descriptors = await GetDescriptorsAsync(obj.GetType(), collection); var state = GetState(collection); @@ -1311,7 +1323,7 @@ private async Task MapNew(Document document, object obj, string collection) /// private async Task MapDeleted(Document document, object obj, string collection) { - var descriptors = GetDescriptors(obj.GetType(), collection); + var descriptors = await GetDescriptorsAsync(obj.GetType(), collection); var state = GetState(collection); diff --git a/src/YesSql.Core/Store.cs b/src/YesSql.Core/Store.cs index d4bcf529..848a6e3a 100644 --- a/src/YesSql.Core/Store.cs +++ b/src/YesSql.Core/Store.cs @@ -21,7 +21,7 @@ public class Store : IStore public IConfiguration Configuration { get; set; } public ISqlDialect Dialect { get; private set; } - public ITypeService TypeNames { get; private set; } + public ITypeService TypeService { get; set; } internal readonly ConcurrentDictionary> GroupMethods = new(); @@ -89,7 +89,7 @@ public async Task InitializeAsync() IndexCommand.ResetQueryCache(); ValidateConfiguration(); - TypeNames = new TypeService(); + TypeService = new TypeService(); if (!string.IsNullOrEmpty(Configuration.Schema)) { diff --git a/test/YesSql.Tests/CoreTests.cs b/test/YesSql.Tests/CoreTests.cs index 6e097749..99556629 100644 --- a/test/YesSql.Tests/CoreTests.cs +++ b/test/YesSql.Tests/CoreTests.cs @@ -51,7 +51,7 @@ public async Task InitializeAsync() _store = await StoreFactory.CreateAndInitializeAsync(_configuration); await _store.InitializeCollectionAsync("Col1"); - _store.TypeNames[typeof(Person)] = "People"; + _store.TypeService[typeof(Person)] = "People"; await CoreTests.CreateTablesAsync(_configuration); } @@ -59,7 +59,7 @@ public async Task InitializeAsync() { _store = await StoreFactory.CreateAndInitializeAsync(_configuration); await _store.InitializeCollectionAsync("Col1"); - _store.TypeNames[typeof(Person)] = "People"; + _store.TypeService[typeof(Person)] = "People"; } // Clear the tables for each new test diff --git a/test/YesSql.Tests/SqliteTests.cs b/test/YesSql.Tests/SqliteTests.cs index ec72f391..0a518f58 100644 --- a/test/YesSql.Tests/SqliteTests.cs +++ b/test/YesSql.Tests/SqliteTests.cs @@ -1,6 +1,8 @@ +using System.Collections.Generic; using System.Threading.Tasks; using Xunit; using Xunit.Abstractions; +using YesSql.Indexes; using YesSql.Provider.Sqlite; using YesSql.Sql; using YesSql.Tests.Indexes; @@ -145,5 +147,74 @@ await builder await session.SaveAsync(property); } + + [Fact] + public async Task ShouldIndexByExtraDescriptors() + { + await using (var connection = _store.Configuration.ConnectionFactory.CreateConnection()) + { + await connection.OpenAsync(); + + await using var transaction = await connection.BeginTransactionAsync(_store.Configuration.IsolationLevel); + var builder = new SchemaBuilder(_store.Configuration, transaction); + + await builder + .DropMapIndexTableAsync(); + + await builder + .CreateMapIndexTableAsync(column => column + .Column(nameof(PropertyIndex.Name), col => col.WithLength(4000)) + .Column(nameof(PropertyIndex.ForRent)) + .Column(nameof(PropertyIndex.IsOccupied)) + .Column(nameof(PropertyIndex.Location), col => col.WithLength(4000)) + ); + + await builder + .AlterTableAsync(nameof(PropertyIndex), table => table + .CreateIndex("IDX_Property", "Name", "ForRent", "IsOccupied", "Location")); + + await transaction.CommitAsync(); + } + + //_store.RegisterIndexes(); + var extraDescriptors = new List + { + new IndexDescriptor + { + //Type = typeof(Property), + //IndexType = typeof(object), + Filter= (entity) => entity is Property, + Map = (entity) => + { + var property = (Property)entity; + return Task.FromResult>([ + new PropertyIndex + { + Name = property.Name, + ForRent = property.ForRent, + IsOccupied = property.IsOccupied, + Location = property.Location + } + ]); + } + } + }; + await using var session = _store.CreateSession(); + session.ExtraIndexDescriptors = extraDescriptors; + + + var property = new Property + { + Name = "test", + IsOccupied = true, + ForRent = true, + Location = new string('*', 4000) + }; + + await session.SaveAsync(property); + + var ls = await session.Query(x => x.Name == "test").ListAsync(); + Assert.NotEmpty(ls); + } } }