adding more tests
This commit is contained in:
@@ -947,8 +947,6 @@ namespace CoreIntegrationTests.Infrastructure
|
|||||||
|
|
||||||
#region Index Management
|
#region Index Management
|
||||||
|
|
||||||
|
|
||||||
//Instantiate a Singleton of the Semaphore with a value of 1. This means that only 1 thread can be granted access at a time.
|
|
||||||
static SemaphoreSlim textIndexSemaphore = new SemaphoreSlim(1, 1);
|
static SemaphoreSlim textIndexSemaphore = new SemaphoreSlim(1, 1);
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -1004,6 +1002,57 @@ namespace CoreIntegrationTests.Infrastructure
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CreateAscendingIndexAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const string expectedIndexName = "SomeContent_1";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await SUT.CreateAscendingIndexAsync<T>(x => x.SomeContent, null, PartitionKey);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var listOfIndexNames = await SUT.GetIndexesNamesAsync<T>(PartitionKey);
|
||||||
|
Assert.Contains(expectedIndexName, listOfIndexNames);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
await SUT.DropIndexAsync<T>(expectedIndexName, PartitionKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CreateDescendingIndexAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const string expectedIndexName = "SomeContent_-1";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await SUT.CreateDescendingIndexAsync<T>(x => x.SomeContent, null, PartitionKey);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var listOfIndexNames = await SUT.GetIndexesNamesAsync<T>(PartitionKey);
|
||||||
|
Assert.Contains(expectedIndexName, listOfIndexNames);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
await SUT.DropIndexAsync<T>(expectedIndexName, PartitionKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CreateHashedIndexAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const string expectedIndexName = "SomeContent_hashed";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await SUT.CreateHashedIndexAsync<T>(x => x.SomeContent, null, PartitionKey);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var listOfIndexNames = await SUT.GetIndexesNamesAsync<T>(PartitionKey);
|
||||||
|
Assert.Contains(expectedIndexName, listOfIndexNames);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
await SUT.DropIndexAsync<T>(expectedIndexName, PartitionKey);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion Index Management
|
#endregion Index Management
|
||||||
|
|
||||||
#region Test Utils
|
#region Test Utils
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Collections.Generic;
|
|||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
@@ -937,6 +938,116 @@ namespace CoreIntegrationTests.Infrastructure
|
|||||||
|
|
||||||
#endregion Max / Min Queries
|
#endregion Max / Min Queries
|
||||||
|
|
||||||
|
#region Index Management
|
||||||
|
|
||||||
|
static SemaphoreSlim textIndexSemaphore = new SemaphoreSlim(1, 1);
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CreateTextIndexNoOptionAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const string expectedIndexName = "SomeContent_text";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await textIndexSemaphore.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await SUT.CreateTextIndexAsync<T, TKey>(x => x.SomeContent, null, PartitionKey);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var listOfIndexNames = await SUT.GetIndexesNamesAsync<T, TKey>(PartitionKey);
|
||||||
|
Assert.Contains(expectedIndexName, listOfIndexNames);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
await SUT.DropIndexAsync<T, TKey>(expectedIndexName, PartitionKey);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
textIndexSemaphore.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CreateTextIndexWithOptionAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
string expectedIndexName = $"{Guid.NewGuid()}";
|
||||||
|
var option = new IndexCreationOptions
|
||||||
|
{
|
||||||
|
Name = expectedIndexName
|
||||||
|
};
|
||||||
|
await textIndexSemaphore.WaitAsync();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Act
|
||||||
|
var result = await SUT.CreateTextIndexAsync<T, TKey>(x => x.Version, option, PartitionKey);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var listOfIndexNames = await SUT.GetIndexesNamesAsync<T, TKey>(PartitionKey);
|
||||||
|
Assert.Contains(expectedIndexName, listOfIndexNames);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
await SUT.DropIndexAsync<T, TKey>(expectedIndexName, PartitionKey);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
textIndexSemaphore.Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CreateAscendingIndexAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const string expectedIndexName = "SomeContent_1";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await SUT.CreateAscendingIndexAsync<T, TKey>(x => x.SomeContent, null, PartitionKey);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var listOfIndexNames = await SUT.GetIndexesNamesAsync<T, TKey>(PartitionKey);
|
||||||
|
Assert.Contains(expectedIndexName, listOfIndexNames);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
await SUT.DropIndexAsync<T, TKey>(expectedIndexName, PartitionKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CreateDescendingIndexAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const string expectedIndexName = "SomeContent_-1";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await SUT.CreateDescendingIndexAsync<T, TKey>(x => x.SomeContent, null, PartitionKey);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var listOfIndexNames = await SUT.GetIndexesNamesAsync<T, TKey>(PartitionKey);
|
||||||
|
Assert.Contains(expectedIndexName, listOfIndexNames);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
await SUT.DropIndexAsync<T, TKey>(expectedIndexName, PartitionKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CreateHashedIndexAsync()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const string expectedIndexName = "SomeContent_hashed";
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await SUT.CreateHashedIndexAsync<T, TKey>(x => x.SomeContent, null, PartitionKey);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
var listOfIndexNames = await SUT.GetIndexesNamesAsync<T, TKey>(PartitionKey);
|
||||||
|
Assert.Contains(expectedIndexName, listOfIndexNames);
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
await SUT.DropIndexAsync<T, TKey>(expectedIndexName, PartitionKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion Index Management
|
||||||
|
|
||||||
#region Test Utils
|
#region Test Utils
|
||||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||||
private string GetCurrentMethod()
|
private string GetCurrentMethod()
|
||||||
|
|||||||
@@ -19,9 +19,9 @@ namespace MongoDbGenericRepository
|
|||||||
/// how the creation should be done.
|
/// how the creation should be done.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TDocument">The type representing a Document.</typeparam>
|
/// <typeparam name="TDocument">The type representing a Document.</typeparam>
|
||||||
/// <param name="field">The field we want to index</param>
|
/// <param name="field">The field we want to index.</param>
|
||||||
/// <param name="indexCreationOptions">Options for creating an index..</param>
|
/// <param name="indexCreationOptions">Options for creating an index.</param>
|
||||||
/// <param name="partitionKey">An optional partition key</param>
|
/// <param name="partitionKey">An optional partition key.</param>
|
||||||
/// <returns>The result of the create index operation.</returns>
|
/// <returns>The result of the create index operation.</returns>
|
||||||
Task<string> CreateTextIndexAsync<TDocument>(Expression<Func<TDocument, object>> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null)
|
Task<string> CreateTextIndexAsync<TDocument>(Expression<Func<TDocument, object>> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null)
|
||||||
where TDocument : IDocument;
|
where TDocument : IDocument;
|
||||||
@@ -33,91 +33,120 @@ namespace MongoDbGenericRepository
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TDocument">The type representing a Document.</typeparam>
|
/// <typeparam name="TDocument">The type representing a Document.</typeparam>
|
||||||
/// <typeparam name="TKey">The type of the primary key for a Document.</typeparam>
|
/// <typeparam name="TKey">The type of the primary key for a Document.</typeparam>
|
||||||
/// <param name="field">The field we want to index</param>
|
/// <param name="field">The field we want to index.</param>
|
||||||
/// <param name="indexCreationOptions">Options for creating an index..</param>
|
/// <param name="indexCreationOptions">Options for creating an index.</param>
|
||||||
/// <param name="partitionKey">An optional partition key</param>
|
/// <param name="partitionKey">An optional partition key.</param>
|
||||||
/// <returns>The result of the create index operation.</returns>
|
/// <returns>The result of the create index operation.</returns>
|
||||||
Task<string> CreateTextIndexAsync<TDocument, TKey>(Expression<Func<TDocument, object>> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null)
|
Task<string> CreateTextIndexAsync<TDocument, TKey>(Expression<Func<TDocument, object>> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null)
|
||||||
where TDocument : IDocument<TKey>
|
where TDocument : IDocument<TKey>
|
||||||
where TKey : IEquatable<TKey>;
|
where TKey : IEquatable<TKey>;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates an index on the given field in ascending order
|
/// Creates an index on the given field in ascending order.
|
||||||
|
/// IndexCreationOptions can be supplied to further specify
|
||||||
|
/// how the creation should be done.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TDocument">The type representing a Document.</typeparam>
|
/// <typeparam name="TDocument">The type representing a Document.</typeparam>
|
||||||
/// <param name="field">The field we want to index</param>
|
/// <param name="field">The field we want to index.</param>
|
||||||
/// <param name="indexCreationOptions">Options for creating an index..</param>
|
/// <param name="indexCreationOptions">Options for creating an index.</param>
|
||||||
/// <param name="partitionKey">An optional partition key</param>
|
/// <param name="partitionKey">An optional partition key.</param>
|
||||||
/// <returns>The result of the create index operation.</returns>
|
/// <returns>The result of the create index operation.</returns>
|
||||||
Task<string> CreateAscendingIndexAsync<TDocument>(Expression<Func<TDocument, object>> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) where TDocument : IDocument;
|
Task<string> CreateAscendingIndexAsync<TDocument>(Expression<Func<TDocument, object>> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null) where TDocument : IDocument;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates an index on the given field in ascending order
|
/// Creates an index on the given field in ascending order.
|
||||||
|
/// IndexCreationOptions can be supplied to further specify
|
||||||
|
/// how the creation should be done.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TDocument">The type representing a Document.</typeparam>
|
/// <typeparam name="TDocument">The type representing a Document.</typeparam>
|
||||||
/// <typeparam name="TKey">The type of the primary key for a Document.</typeparam>
|
/// <typeparam name="TKey">The type of the primary key for a Document.</typeparam>
|
||||||
/// <param name="field">The field we want to index</param>
|
/// <param name="field">The field we want to index.</param>
|
||||||
/// <param name="indexCreationOptions">Options for creating an index..</param>
|
/// <param name="indexCreationOptions">Options for creating an index.</param>
|
||||||
/// <param name="partitionKey">An optional partition key</param>
|
/// <param name="partitionKey">An optional partition key.</param>
|
||||||
/// <returns>The result of the create index operation.</returns>
|
/// <returns>The result of the create index operation.</returns>
|
||||||
Task<string> CreateAscendingIndexAsync<TDocument, TKey>(Expression<Func<TDocument, object>> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null)
|
Task<string> CreateAscendingIndexAsync<TDocument, TKey>(Expression<Func<TDocument, object>> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null)
|
||||||
where TDocument : IDocument<TKey>
|
where TDocument : IDocument<TKey>
|
||||||
where TKey : IEquatable<TKey>;
|
where TKey : IEquatable<TKey>;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates an index on the given field in descending order
|
/// Creates an index on the given field in descending order.
|
||||||
|
/// IndexCreationOptions can be supplied to further specify
|
||||||
|
/// how the creation should be done.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TDocument">The type representing a Document.</typeparam>
|
/// <typeparam name="TDocument">The type representing a Document.</typeparam>
|
||||||
/// <param name="field">The field we want to index</param>
|
/// <param name="field">The field we want to index.</param>
|
||||||
/// <param name="indexCreationOptions">Options for creating an index..</param>
|
/// <param name="indexCreationOptions">Options for creating an index.</param>
|
||||||
/// <param name="partitionKey">An optional partition key</param>
|
/// <param name="partitionKey">An optional partition key.</param>
|
||||||
/// <returns>A string containing the result of the operation.</returns>
|
/// <returns>A string containing the result of the operation.</returns>
|
||||||
Task<string> CreateDescendingIndexAsync<TDocument>(Expression<Func<TDocument, object>> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null)
|
Task<string> CreateDescendingIndexAsync<TDocument>(Expression<Func<TDocument, object>> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null)
|
||||||
where TDocument : IDocument;
|
where TDocument : IDocument;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a hashed index on the given field.
|
/// Creates an index on the given field in descending order.
|
||||||
|
/// IndexCreationOptions can be supplied to further specify
|
||||||
|
/// how the creation should be done.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TDocument">The type representing a Document.</typeparam>
|
/// <typeparam name="TDocument">The type representing a Document.</typeparam>
|
||||||
/// <param name="field">The field we want to index</param>
|
/// <typeparam name="TKey">The type of the primary key for a Document.</typeparam>
|
||||||
/// <param name="indexCreationOptions">Options for creating an index..</param>
|
/// <param name="field">The field we want to index.</param>
|
||||||
/// <param name="partitionKey">An optional partition key</param>
|
/// <param name="indexCreationOptions">Options for creating an index.</param>
|
||||||
|
/// <param name="partitionKey">An optional partition key.</param>
|
||||||
|
/// <returns>The result of the create index operation.</returns>
|
||||||
|
Task<string> CreateDescendingIndexAsync<TDocument, TKey>(Expression<Func<TDocument, object>> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null)
|
||||||
|
where TDocument : IDocument<TKey>
|
||||||
|
where TKey : IEquatable<TKey>;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a hashed index on the given field.
|
||||||
|
/// IndexCreationOptions can be supplied to further specify
|
||||||
|
/// how the creation should be done.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TDocument">The type representing a Document.</typeparam>
|
||||||
|
/// <param name="field">The field we want to index.</param>
|
||||||
|
/// <param name="indexCreationOptions">Options for creating an index.</param>
|
||||||
|
/// <param name="partitionKey">An optional partition key.</param>
|
||||||
/// <returns>A string containing the result of the operation.</returns>
|
/// <returns>A string containing the result of the operation.</returns>
|
||||||
Task<string> CreateHashedIndexAsync<TDocument>(Expression<Func<TDocument, object>> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null)
|
Task<string> CreateHashedIndexAsync<TDocument>(Expression<Func<TDocument, object>> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null)
|
||||||
where TDocument : IDocument;
|
where TDocument : IDocument;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a hashed index on the given field.
|
/// Creates a hashed index on the given field.
|
||||||
|
/// IndexCreationOptions can be supplied to further specify
|
||||||
|
/// how the creation should be done.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TDocument">The type representing a Document.</typeparam>
|
/// <typeparam name="TDocument">The type representing a Document.</typeparam>
|
||||||
/// <typeparam name="TKey">The type of the primary key for a Document.</typeparam>
|
/// <typeparam name="TKey">The type of the primary key for a Document.</typeparam>
|
||||||
/// <param name="field">The field we want to index</param>
|
/// <param name="field">The field we want to index.</param>
|
||||||
/// <param name="indexCreationOptions">Options for creating an index..</param>
|
/// <param name="indexCreationOptions">Options for creating an index.</param>
|
||||||
/// <param name="partitionKey">An optional partition key</param>
|
/// <param name="partitionKey">An optional partition key.</param>
|
||||||
/// <returns>The result of the create index operation.</returns>
|
/// <returns>The result of the create index operation.</returns>
|
||||||
Task<string> CreateHashedIndexAsync<TDocument, TKey>(Expression<Func<TDocument, object>> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null)
|
Task<string> CreateHashedIndexAsync<TDocument, TKey>(Expression<Func<TDocument, object>> field, IndexCreationOptions indexCreationOptions = null, string partitionKey = null)
|
||||||
where TDocument : IDocument<TKey>
|
where TDocument : IDocument<TKey>
|
||||||
where TKey : IEquatable<TKey>;
|
where TKey : IEquatable<TKey>;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a combined text index
|
/// Creates a combined text index.
|
||||||
|
/// IndexCreationOptions can be supplied to further specify
|
||||||
|
/// how the creation should be done.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TDocument">The type representing a Document.</typeparam>
|
/// <typeparam name="TDocument">The type representing a Document.</typeparam>
|
||||||
/// <param name="fields">The fields we want to index</param>
|
/// <param name="fields">The fields we want to index.</param>
|
||||||
/// <param name="indexCreationOptions">Options for creating an index..</param>
|
/// <param name="indexCreationOptions">Options for creating an index.</param>
|
||||||
/// <param name="partitionKey">An optional partition key</param>
|
/// <param name="partitionKey">An optional partition key.</param>
|
||||||
/// <returns>The result of the create index operation.</returns>
|
/// <returns>The result of the create index operation.</returns>
|
||||||
Task<string> CreateCombinedTextIndexAsync<TDocument>(IEnumerable<Expression<Func<TDocument, object>>> fields, IndexCreationOptions indexCreationOptions = null, string partitionKey = null)
|
Task<string> CreateCombinedTextIndexAsync<TDocument>(IEnumerable<Expression<Func<TDocument, object>>> fields, IndexCreationOptions indexCreationOptions = null, string partitionKey = null)
|
||||||
where TDocument : IDocument;
|
where TDocument : IDocument;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a combined text index
|
/// Creates a combined text index.
|
||||||
|
/// IndexCreationOptions can be supplied to further specify
|
||||||
|
/// how the creation should be done.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TDocument">The type representing a Document.</typeparam>
|
/// <typeparam name="TDocument">The type representing a Document.</typeparam>
|
||||||
/// <typeparam name="TKey">The type of the primary key for a Document.</typeparam>
|
/// <typeparam name="TKey">The type of the primary key for a Document.</typeparam>
|
||||||
/// <param name="fields">The fields we want to index</param>
|
/// <param name="fields">The fields we want to index.</param>
|
||||||
/// <param name="indexCreationOptions">Options for creating an index..</param>
|
/// <param name="indexCreationOptions">Options for creating an index.</param>
|
||||||
/// <param name="partitionKey">An optional partition key</param>
|
/// <param name="partitionKey">An optional partition key.</param>
|
||||||
/// <returns>The result of the create index operation.</returns>
|
/// <returns>The result of the create index operation.</returns>
|
||||||
Task<string> CreateCombinedTextIndexAsync<TDocument, TKey>(IEnumerable<Expression<Func<TDocument, object>>> fields, IndexCreationOptions indexCreationOptions = null, string partitionKey = null)
|
Task<string> CreateCombinedTextIndexAsync<TDocument, TKey>(IEnumerable<Expression<Func<TDocument, object>>> fields, IndexCreationOptions indexCreationOptions = null, string partitionKey = null)
|
||||||
where TDocument : IDocument<TKey>
|
where TDocument : IDocument<TKey>
|
||||||
|
|||||||
Reference in New Issue
Block a user