Skip to content

Commit 8c09f3b

Browse files
Merge pull request #147 from matteobortolazzo/dev
v3.1.1
2 parents 420d2de + 7891db1 commit 8c09f3b

27 files changed

+527
-97
lines changed

CHANGELOG.md

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
1-
# 3.1.0 (2020-03-200)
1+
# 3.1.1 (2021-10-14)
2+
3+
## Bug Fixes
4+
5+
* **Query**: Fix First/Last with conditions fail. ([#142](https://github.com/matteobortolazzo/couchdb-net/issues/142))
6+
* **Query**: Fix First/Last on splitted database. ([#136](https://github.com/matteobortolazzo/couchdb-net/issues/136))
7+
* **Query**: Throws exception on List.Count instead of wrong query. ([#138](https://github.com/matteobortolazzo/couchdb-net/issues/138))
8+
* **Query**: Fix multi thread call issues. ([#133](https://github.com/matteobortolazzo/couchdb-net/issues/133))
9+
* **FindManyAsync**: Filters out null results. ([#141](https://github.com/matteobortolazzo/couchdb-net/issues/141)) Thanks [AlexandrSHad](https://github.com/AlexandrSHad)
10+
* **Continuous Changes**: Fix multi thread issues. ([#140](https://github.com/matteobortolazzo/couchdb-net/issues/140))
11+
12+
# 3.1.0 (2020-03-20)
213

314
## Features
415

LATEST_CHANGE.md

+7-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
1-
## Features
1+
## Bug Fixes
22

3-
* **Views**: Possibility to query multiple views at once. ([#126](https://github.com/matteobortolazzo/couchdb-net/issues/126)) Thanks [Panos](https://github.com/panoukos41)
4-
* **Partitioned database**: It's possible to create partitioned databases. ([#122](https://github.com/matteobortolazzo/couchdb-net/issues/122))
5-
6-
## Bug Fixes
7-
8-
* **Views**: `CouchViewOptions` are serialized correctly when overriding the serializer. ([#125](https://github.com/matteobortolazzo/couchdb-net/issues/125)) Thanks [Panos](https://github.com/panoukos41)
9-
* **PropertyCaseType**: `PropertyCaseType` is not applied on internal properties anymore. ([#127](https://github.com/matteobortolazzo/couchdb-net/issues/127))
3+
* **Query**: Fix First/Last with conditions fail. ([#142](https://github.com/matteobortolazzo/couchdb-net/issues/142))
4+
* **Query**: Fix First/Last on splitted database. ([#136](https://github.com/matteobortolazzo/couchdb-net/issues/136))
5+
* **Query**: Throws exception on List.Count instead of wrong query. ([#138](https://github.com/matteobortolazzo/couchdb-net/issues/138))
6+
* **Query**: Fix multi thread call issues. ([#133](https://github.com/matteobortolazzo/couchdb-net/issues/133))
7+
* **FindManyAsync**: Filters out null results. ([#141](https://github.com/matteobortolazzo/couchdb-net/issues/141)) Thanks [AlexandrSHad](https://github.com/AlexandrSHad)
8+
* **Continuous Changes**: Fix multi thread issues. ([#140](https://github.com/matteobortolazzo/couchdb-net/issues/140))

README.md

+29-10
Original file line numberDiff line numberDiff line change
@@ -636,7 +636,10 @@ luke = await users.ChangeUserPassword(luke, "r2d2");
636636

637637
## Dependency Injection
638638

639-
* Install the DI package from NuGet: [https://www.nuget.org/packages/CouchDB.NET.DependencyInjection](https://www.nuget.org/packages/CouchDB.NET.DependencyInjection)
639+
As always you can leverage all the benefits of Dependency Injection.
640+
641+
**Info:** The context will be registered as a `singleton`.
642+
640643
* Create a `CouchContext` with a constructor like the following:
641644

642645
```csharp
@@ -649,14 +652,7 @@ public class MyDeathStarContext : CouchContext
649652
}
650653
```
651654

652-
* In the `Startup` class register the context:
653-
654-
```csharp
655-
// ConfigureServices
656-
services.AddCouchContext<MyDeathStarContext>(builder => builder
657-
.UseEndpoint("http://localhost:5984")
658-
.UseBasicAuthentication(username: "admin", password: "admin"));
659-
```
655+
* Register the context via any of supported containers (see appropriate section section below)
660656

661657
* Inject the context:
662658

@@ -673,7 +669,30 @@ public class RebelsController : Controller
673669
}
674670
```
675671

676-
**Info:** The context is registered as a `singleton`.
672+
### Microsoft container
673+
* Install the DI package from NuGet: [https://www.nuget.org/packages/CouchDB.NET.DependencyInjection](https://www.nuget.org/packages/CouchDB.NET.DependencyInjection)
674+
675+
* In the `Startup` class register the context:
676+
677+
```csharp
678+
// ConfigureServices
679+
services.AddCouchContext<MyDeathStarContext>(builder => builder
680+
.UseEndpoint("http://localhost:5984")
681+
.UseBasicAuthentication(username: "admin", password: "admin"));
682+
```
683+
684+
### Autofac container
685+
* Install the DI package from NuGet: [https://www.nuget.org/packages/CouchDB.NET.DependencyInjection.Autofac](https://www.nuget.org/packages/CouchDB.NET.DependencyInjection.Autofac)
686+
687+
* In the `Startup` class register the context:
688+
689+
```csharp
690+
// ConfigureServices
691+
var containerBuilder = new ContainerBuilder();
692+
containerBuilder.AddCouchContext<MyDeathStarContext>(optionsBuilder => optionsBuilder
693+
.UseEndpoint("http://localhost:5984")
694+
.UseBasicAuthentication(username: "admin", password: "admin"));
695+
```
677696

678697
## Advanced
679698

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using Autofac;
2+
using CouchDB.Driver.Options;
3+
using System;
4+
using CouchDB.Driver.Helpers;
5+
6+
namespace CouchDB.Driver.DependencyInjection.Autofac
7+
{
8+
public static class AutofacRegistrationExtensions
9+
{
10+
public static ContainerBuilder AddCouchContext<TContext>(this ContainerBuilder builder,
11+
Action<CouchOptionsBuilder<TContext>> optionBuilderAction)
12+
where TContext : CouchContext
13+
{
14+
Check.NotNull(builder, nameof(builder));
15+
Check.NotNull(optionBuilderAction, nameof(optionBuilderAction));
16+
17+
var optionsBuilder = new CouchOptionsBuilder<TContext>();
18+
optionBuilderAction?.Invoke(optionsBuilder);
19+
20+
builder
21+
.RegisterInstance(optionsBuilder.Options)
22+
.AsSelf();
23+
24+
builder
25+
.RegisterType<TContext>()
26+
.SingleInstance();
27+
28+
return builder;
29+
}
30+
}
31+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard2.1</TargetFramework>
5+
<AssemblyName>CouchDB.NET.DependencyInjection.Autofac</AssemblyName>
6+
<Authors>Matteo Bortolazzo</Authors>
7+
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
8+
<Description>Dependency injection utilities for CouchDB.NET with Autofac container support.</Description>
9+
<PackageProjectUrl>https://github.com/matteobortolazzo/couchdb-net</PackageProjectUrl>
10+
<RepositoryUrl>https://github.com/matteobortolazzo/couchdb-net</RepositoryUrl>
11+
<PackageTags>couchdb,driver,nosql,netstandard,pouchdb,xamarin</PackageTags>
12+
<PackageReleaseNotes></PackageReleaseNotes>
13+
<ApplicationIcon />
14+
<OutputType>Library</OutputType>
15+
<StartupObject />
16+
<NeutralLanguage>en</NeutralLanguage>
17+
<LangVersion>8.0</LangVersion>
18+
<Nullable>enable</Nullable>
19+
<Version>3.0.0</Version>
20+
<PackageIcon>icon.png</PackageIcon>
21+
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
22+
</PropertyGroup>
23+
24+
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
25+
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
26+
<WarningsAsErrors />
27+
</PropertyGroup>
28+
29+
<ItemGroup>
30+
<None Include="Images\icon.png" Pack="true" PackagePath="\" />
31+
<None Include="License\LICENSE.txt" Pack="true" PackagePath="\" />
32+
</ItemGroup>
33+
34+
<ItemGroup>
35+
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" PrivateAssets="All" Version="3.3.1" />
36+
<PackageReference Include="Autofac" Version="5.2.0" />
37+
</ItemGroup>
38+
39+
<ItemGroup>
40+
<ProjectReference Include="..\CouchDB.Driver\CouchDB.Driver.csproj" />
41+
</ItemGroup>
42+
43+
</Project>
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2018 Matteo Bortolazzo
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

src/CouchDB.Driver.DependencyInjection/ServiceCollectionExtensions.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
namespace CouchDB.Driver.DependencyInjection
77
{
8-
public static class ServiceCollectionExtensions
8+
public static class ServiceCollectionExtensions
99
{
1010
public static IServiceCollection AddCouchContext<TContext>(this IServiceCollection services,
1111
Action<CouchOptionsBuilder<TContext>> optionBuilderAction)

src/CouchDB.Driver/AssemblyInfo.cs

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22

33
[assembly: InternalsVisibleTo("CouchDB.Driver.UnitTests")]
44
[assembly: InternalsVisibleTo("CouchDB.NET.DependencyInjection")]
5+
[assembly: InternalsVisibleTo("CouchDB.NET.DependencyInjection.Autofac")]

src/CouchDB.Driver/ChangesFeed/Responses/ChangesFeedResponseResult.cs

+16-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#nullable disable
2+
using System;
23
using System.Collections.Generic;
34
using CouchDB.Driver.Types;
45
using Newtonsoft.Json;
@@ -7,19 +8,28 @@ namespace CouchDB.Driver.ChangesFeed.Responses
78
{
89
public class ChangesFeedResponseResult<TSource> where TSource: CouchDocument
910
{
10-
[JsonProperty("changes")]
11-
public IList<ChangesFeedResponseResultChange> Changes { get; internal set; }
11+
[JsonProperty("seq")]
12+
public string Seq { get; set; }
1213

1314
[JsonProperty("id")]
1415
public string Id { get; set; }
1516

16-
[JsonProperty("seq")]
17-
public string Seq { get; set; }
18-
1917
[JsonProperty("deleted")]
2018
public bool Deleted { get; set; }
2119

22-
[JsonProperty("doc")]
20+
[JsonProperty("changes")]
21+
public IList<ChangesFeedResponseResultChange> Changes { get; internal set; }
22+
23+
[JsonProperty("roleIds")]
24+
public IList<string> RoleIds { get; internal set; }
25+
26+
[JsonProperty("createdAt")]
27+
public DateTime CreatedAt { get; set; }
28+
29+
[JsonProperty("createdBy")]
30+
public string CreatedBy { get; set; }
31+
32+
[JsonProperty("doc", NullValueHandling = NullValueHandling.Ignore)]
2333
public TSource Document { get; set; }
2434
}
2535
}

src/CouchDB.Driver/CouchContext.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ private static bool AreFieldsEqual(Dictionary<string, IndexFieldDirection> curre
204204
return true;
205205
}
206206

207-
private IEnumerable<PropertyInfo> GetDatabaseProperties() =>
207+
protected virtual IEnumerable<PropertyInfo> GetDatabaseProperties() =>
208208
GetType()
209209
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
210210
.Where(p => p.PropertyType.IsGenericType &&

src/CouchDB.Driver/CouchDatabase.cs

+16-5
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
using CouchDB.Driver.Query;
2525
using Newtonsoft.Json;
2626
using System.Net;
27+
using System.Text.RegularExpressions;
2728
using CouchDB.Driver.Views;
2829

2930
namespace CouchDB.Driver
@@ -35,6 +36,7 @@ namespace CouchDB.Driver
3536
public class CouchDatabase<TSource> : ICouchDatabase<TSource>
3637
where TSource : CouchDocument
3738
{
39+
private readonly Regex _feedChangeLineStartPattern;
3840
private readonly IAsyncQueryProvider _queryProvider;
3941
private readonly IFlurlClient _flurlClient;
4042
private readonly CouchOptions _options;
@@ -52,6 +54,7 @@ public class CouchDatabase<TSource> : ICouchDatabase<TSource>
5254

5355
internal CouchDatabase(IFlurlClient flurlClient, CouchOptions options, QueryContext queryContext, string? discriminator)
5456
{
57+
_feedChangeLineStartPattern = new Regex(@"{""seq");
5558
_flurlClient = flurlClient;
5659
_options = options;
5760
_queryContext = queryContext;
@@ -122,6 +125,7 @@ public async Task<List<TSource>> FindManyAsync(IReadOnlyCollection<string> docId
122125
var documents = bulkGetResult.Results
123126
.SelectMany(r => r.Docs)
124127
.Select(d => d.Item)
128+
.Where(i => i != null)
125129
.ToList();
126130

127131
foreach (TSource document in documents)
@@ -411,9 +415,16 @@ public async IAsyncEnumerable<ChangesFeedResponseResult<TSource>> GetContinuousC
411415
{
412416
continue;
413417
}
414-
415-
ChangesFeedResponseResult<TSource> result = JsonConvert.DeserializeObject<ChangesFeedResponseResult<TSource>>(line);
416-
yield return result;
418+
419+
MatchCollection matches = _feedChangeLineStartPattern.Matches(line);
420+
for (var i = 0; i < matches.Count; i++)
421+
{
422+
var startIndex = matches[i].Index;
423+
var endIndex = i < matches.Count - 1 ? matches[i + 1].Index : line.Length;
424+
var lineLength = endIndex - startIndex;
425+
var substring = line.Substring(startIndex, lineLength);
426+
yield return JsonConvert.DeserializeObject<ChangesFeedResponseResult<TSource>>(substring);
427+
}
417428
}
418429
}
419430

@@ -526,7 +537,7 @@ public Task<CouchViewList<TKey, TValue, TSource>> GetDetailedViewAsync<TKey, TVa
526537

527538
IFlurlRequest request = NewRequest()
528539
.AppendPathSegments("_design", design, "_view", view);
529-
540+
530541
Task<CouchViewList<TKey, TValue, TSource>>? requestTask = options == null
531542
? request.GetJsonAsync<CouchViewList<TKey, TValue, TSource>>(cancellationToken)
532543
: request
@@ -558,7 +569,7 @@ public async Task<CouchViewList<TKey, TValue, TSource>[]> GetDetailedViewQueryAs
558569
IFlurlRequest request = NewRequest()
559570
.AppendPathSegments("_design", design, "_view", view, "queries");
560571

561-
CouchViewQueryResult<TKey, TValue, TSource> result =
572+
CouchViewQueryResult<TKey, TValue, TSource> result =
562573
await request
563574
.PostJsonAsync(new { queries }, cancellationToken)
564575
.ReceiveJson<CouchViewQueryResult<TKey, TValue, TSource>>()

src/CouchDB.Driver/Extensions/ExpressionExtensions.cs

+5
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ public static string GetPropertyName(this MemberExpression m, CouchOptions optio
2626

2727
Expression currentExpression = m.Expression;
2828

29+
if (currentExpression is MemberExpression rootMemberExpression && rootMemberExpression.Type.IsEnumerable())
30+
{
31+
throw new NotSupportedException();
32+
}
33+
2934
while (currentExpression is MemberExpression cm)
3035
{
3136
members.Add(cm.Member.GetCouchPropertyName(caseType));

src/CouchDB.Driver/Extensions/TypeExtensions.cs

+6
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Humanizer;
22
using Newtonsoft.Json;
33
using System;
4+
using System.Collections;
45
using System.Collections.Generic;
56
using System.Linq;
67
using System.Reflection;
@@ -96,6 +97,11 @@ public static IEnumerable<Type> GetGenericTypeImplementations(this Type type, Ty
9697
}
9798
}
9899

100+
public static bool IsEnumerable(this Type type)
101+
{
102+
return type.IsArray || typeof(IEnumerable).IsAssignableFrom(type);
103+
}
104+
99105
private static IEnumerable<Type> GetBaseTypes(this Type? type)
100106
{
101107
type = type?.BaseType;

src/CouchDB.Driver/Helpers/MethodCallExpressionBuilder.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public static MethodCallExpression SubstituteWithSelect(this MethodCallExpressio
3939
return Expression.Call(typeof(Queryable), nameof(Queryable.Select), genericArgumentTypes, node.Arguments[0], selectorNode.Arguments[1]);
4040
}
4141

42-
public static MethodCallExpression SubstituteWithWhere(this MethodCallExpression node, bool negate = false)
42+
public static MethodCallExpression SubstituteWithWhere(this MethodCallExpression node, ExpressionVisitor optimizer, bool negate = false)
4343
{
4444
Check.NotNull(node, nameof(node));
4545

@@ -52,7 +52,8 @@ public static MethodCallExpression SubstituteWithWhere(this MethodCallExpression
5252
predicate = body.WrapInLambda(lambdaExpression.Parameters);
5353
}
5454

55-
return Expression.Call(typeof(Queryable), nameof(Queryable.Where), node.Method.GetGenericArguments(), node.Arguments[0], predicate);
55+
MethodCallExpression e = Expression.Call(typeof(Queryable), nameof(Queryable.Where), node.Method.GetGenericArguments(), node.Arguments[0], predicate);
56+
return (MethodCallExpression)optimizer.Visit(e);
5657
}
5758

5859
#endregion

0 commit comments

Comments
 (0)