Skip to content

Commit f223038

Browse files
authored
Another attempt to fix #2182 #2183 (#2197)
Taking another approach then #2189. Here we yield responsibility of creating a new client to ISerializerFactory. The overloads taking a Func factory are now obsolete and others taking ISerializerFactory are added applied feedback on #2197 fix null reference is test setup post switch to serializerfactory
1 parent 6feda6a commit f223038

19 files changed

+591
-64
lines changed

src/Elasticsearch.Net/Configuration/ConnectionConfiguration.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@ private static void DefaultRequestDataCreated(RequestData response) { }
151151
IElasticsearchSerializer IConnectionConfigurationValues.Serializer => _serializer;
152152

153153
private readonly IConnectionPool _connectionPool;
154-
private readonly Func<T, IElasticsearchSerializer> _serializerFactory;
155154
IConnectionPool IConnectionConfigurationValues.ConnectionPool => _connectionPool;
156155

157156
private readonly IConnection _connection;
@@ -164,9 +163,8 @@ protected ConnectionConfiguration(IConnectionPool connectionPool, IConnection co
164163
{
165164
this._connectionPool = connectionPool;
166165
this._connection = connection ?? new HttpConnection();
167-
this._serializerFactory = serializerFactory ?? (c=>this.DefaultSerializer((T)this));
168166
// ReSharper disable once VirtualMemberCallInContructor
169-
this._serializer = _serializerFactory((T)this);
167+
this._serializer = serializerFactory?.Invoke((T)this) ?? this.DefaultSerializer((T)this);
170168

171169
this._requestTimeout = ConnectionConfiguration.DefaultTimeout;
172170
this._sniffOnConnectionFault = true;

src/Elasticsearch.Net/Serialization/IElasticsearchSerializer.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ public static byte[] SerializeToBytes(this IElasticsearchSerializer serializer,
2626
return ms.ToArray();
2727
}
2828
}
29-
public static string SerializeToString(this IElasticsearchSerializer serializer, object data, SerializationFormatting formatting = SerializationFormatting.Indented) =>
29+
public static string SerializeToString(this IElasticsearchSerializer serializer, object data, SerializationFormatting formatting = SerializationFormatting.Indented) =>
3030
serializer.SerializeToBytes(data, formatting).Utf8String();
3131
}
32-
33-
}
32+
}

src/Nest/CommonAbstractions/ConnectionSettings/ConnectionSettingsBase.cs

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,23 @@ public ConnectionSettings(Uri uri = null)
1919
: this(new SingleNodeConnectionPool(uri ?? new Uri("http://localhost:9200"))) { }
2020

2121
public ConnectionSettings(IConnectionPool connectionPool)
22-
: this(connectionPool, null, null) { }
22+
: this(connectionPool, null, new SerializerFactory()) { }
2323

2424
public ConnectionSettings(IConnectionPool connectionPool, IConnection connection)
25-
: this(connectionPool, connection, null) { }
25+
: this(connectionPool, connection, new SerializerFactory()) { }
2626

2727
public ConnectionSettings(IConnectionPool connectionPool, Func<ConnectionSettings, IElasticsearchSerializer> serializerFactory)
28+
#pragma warning disable CS0618 // Type or member is obsolete
2829
: this(connectionPool, null, serializerFactory) { }
30+
#pragma warning restore CS0618 // Type or member is obsolete
2931

32+
public ConnectionSettings(IConnectionPool connectionPool, IConnection connection, ISerializerFactory serializerFactory)
33+
: base(connectionPool, connection, serializerFactory, s => serializerFactory.Create(s)) { }
34+
35+
[Obsolete("Please use the constructor taking ISerializerFactory instead of a Func")]
3036
public ConnectionSettings(IConnectionPool connectionPool, IConnection connection, Func<ConnectionSettings, IElasticsearchSerializer> serializerFactory)
31-
: base(connectionPool, connection, serializerFactory) { }
37+
: base(connectionPool, connection, null, s => serializerFactory?.Invoke(s)) { }
38+
3239
}
3340

3441
/// <summary>
@@ -37,7 +44,7 @@ public ConnectionSettings(IConnectionPool connectionPool, IConnection connection
3744
[Browsable(false)]
3845
[EditorBrowsable(EditorBrowsableState.Never)]
3946
public abstract class ConnectionSettingsBase<TConnectionSettings> : ConnectionConfiguration<TConnectionSettings>, IConnectionSettingsValues
40-
where TConnectionSettings : ConnectionSettingsBase<TConnectionSettings>
47+
where TConnectionSettings : ConnectionSettingsBase<TConnectionSettings>, IConnectionSettingsValues
4148
{
4249
private string _defaultIndex;
4350
string IConnectionSettingsValues.DefaultIndex => this._defaultIndex;
@@ -63,17 +70,36 @@ public abstract class ConnectionSettingsBase<TConnectionSettings> : ConnectionCo
6370
private readonly FluentDictionary<MemberInfo, IPropertyMapping> _propertyMappings = new FluentDictionary<MemberInfo, IPropertyMapping>();
6471
FluentDictionary<MemberInfo, IPropertyMapping> IConnectionSettingsValues.PropertyMappings => _propertyMappings;
6572

66-
protected ConnectionSettingsBase(IConnectionPool connectionPool, IConnection connection, Func<TConnectionSettings, IElasticsearchSerializer> serializerFactory)
67-
: base(connectionPool, connection, serializerFactory)
73+
private readonly ISerializerFactory _serializerFactory;
74+
ISerializerFactory IConnectionSettingsValues.SerializerFactory => _serializerFactory;
75+
76+
protected ConnectionSettingsBase(
77+
IConnectionPool connectionPool,
78+
IConnection connection,
79+
ISerializerFactory serializerFactory,
80+
Func<TConnectionSettings, IElasticsearchSerializer> serializerFactoryFunc
81+
)
82+
: base(connectionPool, connection, serializerFactoryFunc)
6883
{
6984
this._defaultTypeNameInferrer = (t => t.Name.ToLowerInvariant());
7085
this._defaultFieldNameInferrer = (p => p.ToCamelCase());
7186
this._defaultIndices = new FluentDictionary<Type, string>();
7287
this._defaultTypeNames = new FluentDictionary<Type, string>();
88+
this._serializerFactory = serializerFactory ?? new SerializerFactory();
7389

7490
this._inferrer = new Inferrer(this);
7591
}
7692

93+
protected ConnectionSettingsBase(
94+
IConnectionPool connectionPool,
95+
IConnection connection,
96+
Func<TConnectionSettings, IElasticsearchSerializer> serializerFactoryFunc
97+
)
98+
: this(connectionPool, connection, null, serializerFactoryFunc) { }
99+
100+
IElasticsearchSerializer IConnectionSettingsValues.StatefulSerializer(JsonConverter converter) =>
101+
this._serializerFactory.CreateStateful(this, converter);
102+
77103
/// <summary>
78104
/// The default serializer for requests and responses
79105
/// </summary>
@@ -92,7 +118,7 @@ public TConnectionSettings PluralizeTypeNames()
92118
/// <summary>
93119
/// The default index to use when no index is specified.
94120
/// </summary>
95-
/// <param name="defaultIndex">When null/empty/not set might throw
121+
/// <param name="defaultIndex">When null/empty/not set might throw
96122
/// <see cref="NullReferenceException"/> later on when not specifying index explicitly while indexing.
97123
/// </param>
98124
public TConnectionSettings DefaultIndex(string defaultIndex)
@@ -177,10 +203,10 @@ public TConnectionSettings MapPropertiesFor<TDocument>(Action<PropertyMappingDes
177203
var mapper = new PropertyMappingDescriptor<TDocument>();
178204
propertiesSelector(mapper);
179205
ApplyPropertyMappings(mapper.Mappings);
180-
return (TConnectionSettings) this;
206+
return (TConnectionSettings)this;
181207
}
182208

183-
private void ApplyPropertyMappings<TDocument>(IList<IClrTypePropertyMapping<TDocument>> mappings)
209+
private void ApplyPropertyMappings<TDocument>(IList<IClrTypePropertyMapping<TDocument>> mappings)
184210
where TDocument : class
185211
{
186212
foreach (var mapping in mappings)
@@ -198,7 +224,7 @@ private void ApplyPropertyMappings<TDocument>(IList<IClrTypePropertyMapping<TDoc
198224
{
199225
var newName = mapping.NewName;
200226
var mappedAs = _propertyMappings[memberInfo].Name;
201-
var typeName = typeof (TDocument).Name;
227+
var typeName = typeof(TDocument).Name;
202228
if (mappedAs.IsNullOrEmpty() && newName.IsNullOrEmpty())
203229
throw new ArgumentException("Property mapping '{0}' on type is already ignored"
204230
.F(e, newName, mappedAs, typeName));
@@ -228,12 +254,12 @@ public TConnectionSettings InferMappingFor<TDocument>(Func<ClrTypeMappingDescrip
228254
if (inferMapping.IdProperty != null)
229255
#pragma warning disable CS0618 // Type or member is obsolete but will be private in the future OK to call here
230256
this.MapIdPropertyFor<TDocument>(inferMapping.IdProperty);
231-
#pragma warning restore CS0618
257+
#pragma warning restore CS0618
232258

233259
if (inferMapping.Properties != null)
234260
this.ApplyPropertyMappings<TDocument>(inferMapping.Properties);
235-
236-
return (TConnectionSettings) this;
261+
262+
return (TConnectionSettings)this;
237263
}
238264

239265
}

src/Nest/CommonAbstractions/ConnectionSettings/IConnectionSettingsValues.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,9 @@ public interface IConnectionSettingsValues : IConnectionConfigurationValues
1616
string DefaultIndex { get; }
1717
Func<string, string> DefaultFieldNameInferrer { get; }
1818
Func<Type, string> DefaultTypeNameInferrer { get; }
19+
20+
ISerializerFactory SerializerFactory { get; }
21+
22+
IElasticsearchSerializer StatefulSerializer(JsonConverter converter);
1923
}
20-
}
24+
}

src/Nest/CommonAbstractions/SerializationBehavior/JsonNetSerializer.cs

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,41 +15,70 @@ public class JsonNetSerializer : IElasticsearchSerializer
1515
{
1616
private static readonly Encoding ExpectedEncoding = new UTF8Encoding(false);
1717

18+
1819
protected IConnectionSettingsValues Settings { get; }
1920
protected ElasticContractResolver ContractResolver { get; }
2021

2122
//todo this internal smells
2223
internal JsonSerializer Serializer => _defaultSerializer;
2324

24-
private readonly Dictionary<SerializationFormatting, JsonSerializer> _defaultSerializers;
25-
private readonly JsonSerializer _defaultSerializer;
25+
private Dictionary<SerializationFormatting, JsonSerializer> _defaultSerializers;
26+
private JsonSerializer _defaultSerializer;
2627

28+
[Obsolete("Use the connection settings constructor that takes an ISerializerFactory")]
2729
protected virtual void ModifyJsonSerializerSettings(JsonSerializerSettings settings) { }
30+
2831
protected virtual IList<Func<Type, JsonConverter>> ContractConverters => null;
2932

3033
public JsonNetSerializer(IConnectionSettingsValues settings) : this(settings, null) { }
3134

3235
/// <summary>
33-
/// this constructor is only here for stateful (de)serialization
36+
/// this constructor is only here for stateful (de)serialization
3437
/// </summary>
35-
internal JsonNetSerializer(IConnectionSettingsValues settings, JsonConverter stateFullConverter)
38+
protected internal JsonNetSerializer(
39+
IConnectionSettingsValues settings,
40+
JsonConverter statefulConverter
41+
)
3642
{
3743
this.Settings = settings;
38-
var piggyBackState = stateFullConverter == null ? null : new JsonConverterPiggyBackState { ActualJsonConverter = stateFullConverter };
44+
45+
var piggyBackState = statefulConverter == null ? null : new JsonConverterPiggyBackState { ActualJsonConverter = statefulConverter };
3946
// ReSharper disable once VirtualMemberCallInContructor
4047
this.ContractResolver = new ElasticContractResolver(this.Settings, this.ContractConverters) { PiggyBackState = piggyBackState };
4148

4249
this._defaultSerializer = JsonSerializer.Create(this.CreateSettings(SerializationFormatting.None));
43-
//this._defaultSerializer.Formatting = Formatting.None;
4450
var indentedSerializer = JsonSerializer.Create(this.CreateSettings(SerializationFormatting.Indented));
45-
//indentedSerializer.Formatting = Formatting.Indented;
4651
this._defaultSerializers = new Dictionary<SerializationFormatting, JsonSerializer>
4752
{
4853
{ SerializationFormatting.None, this._defaultSerializer },
4954
{ SerializationFormatting.Indented, indentedSerializer }
5055
};
5156
}
5257

58+
/// <summary>
59+
/// If you subclass JsonNetSerializer and want to apply state passed in the constructor call this to
60+
/// overwrite the DefaultSerializers's JsonSerializerSettings and/or connectionsettings.
61+
/// </summary>
62+
/// <param name="settingsModifier"></param>
63+
protected void OverwriteDefaultSerializers(Action<JsonSerializerSettings, IConnectionSettingsValues> settingsModifier)
64+
{
65+
settingsModifier.ThrowIfNull(nameof(settingsModifier));
66+
var collapsed = this.CreateSettings(SerializationFormatting.None);
67+
var indented = this.CreateSettings(SerializationFormatting.Indented);
68+
settingsModifier(collapsed, this.Settings);
69+
settingsModifier(indented, this.Settings);
70+
71+
this._defaultSerializer = JsonSerializer.Create(collapsed);
72+
var indentedSerializer = JsonSerializer.Create(indented);
73+
this._defaultSerializers = new Dictionary<SerializationFormatting, JsonSerializer>
74+
{
75+
{ SerializationFormatting.None, this._defaultSerializer },
76+
{ SerializationFormatting.Indented, indentedSerializer }
77+
};
78+
79+
}
80+
81+
5382
public virtual void Serialize(object data, Stream writableStream, SerializationFormatting formatting = SerializationFormatting.Indented)
5483
{
5584
var serializer = _defaultSerializers[formatting];
@@ -117,4 +146,4 @@ private JsonSerializerSettings CreateSettings(SerializationFormatting formatting
117146
return settings;
118147
}
119148
}
120-
}
149+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using Elasticsearch.Net;
7+
using Newtonsoft.Json;
8+
9+
namespace Nest
10+
{
11+
public interface ISerializerFactory
12+
{
13+
IElasticsearchSerializer Create(IConnectionSettingsValues settings);
14+
15+
IElasticsearchSerializer CreateStateful(IConnectionSettingsValues settings, JsonConverter converter);
16+
}
17+
18+
public class SerializerFactory : ISerializerFactory
19+
{
20+
private Func<IConnectionSettingsValues, IElasticsearchSerializer> _serializerFactoryFunc;
21+
22+
public SerializerFactory()
23+
{
24+
25+
}
26+
public SerializerFactory(Func<IConnectionSettingsValues , IElasticsearchSerializer> serializerFactoryFunc)
27+
{
28+
this._serializerFactoryFunc = serializerFactoryFunc;
29+
}
30+
31+
public IElasticsearchSerializer Create(IConnectionSettingsValues settings) =>
32+
this._serializerFactoryFunc?.Invoke(settings) ?? new JsonNetSerializer(settings);
33+
34+
public IElasticsearchSerializer CreateStateful(IConnectionSettingsValues settings, JsonConverter converter) =>
35+
new JsonNetSerializer(settings, converter);
36+
}
37+
}

src/Nest/Document/Multiple/MultiGet/ElasticClient-MultiGet.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
namespace Nest
88
{
99
using MultiGetConverter = Func<IApiCallDetails, Stream, MultiGetResponse>;
10-
10+
1111
public partial interface IElasticClient
1212
{
1313
/// <summary>
14-
/// Multi GET API allows to get multiple documents based on an index, type (optional) and id (and possibly routing).
15-
/// The response includes a docs array with all the fetched documents, each element similar in structure to a document
14+
/// Multi GET API allows to get multiple documents based on an index, type (optional) and id (and possibly routing).
15+
/// The response includes a docs array with all the fetched documents, each element similar in structure to a document
1616
/// provided by the get API.
1717
/// <para> </para>http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-multi-get.html
1818
/// </summary>
@@ -36,7 +36,7 @@ public IMultiGetResponse MultiGet(Func<MultiGetDescriptor, IMultiGetRequest> sel
3636
this.MultiGet(selector.InvokeOrDefault(new MultiGetDescriptor()));
3737

3838
/// <inheritdoc/>
39-
public IMultiGetResponse MultiGet(IMultiGetRequest request) =>
39+
public IMultiGetResponse MultiGet(IMultiGetRequest request) =>
4040
this.Dispatcher.Dispatch<IMultiGetRequest, MultiGetRequestParameters, MultiGetResponse>(
4141
request,
4242
new MultiGetConverter((r, s) => this.DeserializeMultiGetResponse(r, s, CreateCovariantMultiGetConverter(request))),
@@ -48,16 +48,16 @@ public Task<IMultiGetResponse> MultiGetAsync(Func<MultiGetDescriptor, IMultiGetR
4848
this.MultiGetAsync(selector.InvokeOrDefault(new MultiGetDescriptor()));
4949

5050
/// <inheritdoc/>
51-
public Task<IMultiGetResponse> MultiGetAsync(IMultiGetRequest request) =>
51+
public Task<IMultiGetResponse> MultiGetAsync(IMultiGetRequest request) =>
5252
this.Dispatcher.DispatchAsync<IMultiGetRequest, MultiGetRequestParameters, MultiGetResponse, IMultiGetResponse>(
5353
request,
5454
new MultiGetConverter((r, s) => this.DeserializeMultiGetResponse(r, s, CreateCovariantMultiGetConverter(request))),
5555
this.LowLevelDispatch.MgetDispatchAsync<MultiGetResponse>
5656
);
57-
private MultiGetResponse DeserializeMultiGetResponse(IApiCallDetails response, Stream stream, JsonConverter converter)=>
58-
new JsonNetSerializer(this.ConnectionSettings, converter).Deserialize<MultiGetResponse>(stream);
57+
private MultiGetResponse DeserializeMultiGetResponse(IApiCallDetails response, Stream stream, JsonConverter converter) =>
58+
this.ConnectionSettings.StatefulSerializer(converter).Deserialize<MultiGetResponse>(stream);
5959

6060
private JsonConverter CreateCovariantMultiGetConverter(IMultiGetRequest descriptor) => new MultiGetHitJsonConverter(descriptor);
6161

6262
}
63-
}
63+
}

src/Nest/Nest.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,7 @@
456456
<Compile Include="CommonAbstractions\SerializationBehavior\GenericJsonConverters\VerbatimDictionaryKeysConverter.cs" />
457457
<Compile Include="CommonAbstractions\SerializationBehavior\JsonNetSerializer.cs" />
458458
<Compile Include="CommonAbstractions\SerializationBehavior\JsonReaderExtensions.cs" />
459+
<Compile Include="CommonAbstractions\SerializationBehavior\SerializerFactory.cs" />
459460
<Compile Include="CommonAbstractions\SerializationBehavior\StatefulDeserialization\ConcreteTypeConverter.cs" />
460461
<Compile Include="CommonAbstractions\SerializationBehavior\StatefulDeserialization\JsonConverterPiggyBackState.cs" />
461462
<Compile Include="CommonAbstractions\Static\Static.cs" />

src/Nest/Search/MultiSearch/ElasticClient-MultiSearch.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ public IMultiSearchResponse MultiSearch(IMultiSearchRequest request)
4141
(p, d) =>
4242
{
4343
var converter = CreateMultiSearchDeserializer(p);
44-
var serializer = new JsonNetSerializer(this.ConnectionSettings, converter);
44+
var serializer = this.ConnectionSettings.StatefulSerializer(converter);
4545
var json = serializer.SerializeToBytes(p).Utf8String();
4646
var creator = new MultiSearchCreator((r, s) => serializer.Deserialize<MultiSearchResponse>(s));
4747
request.RequestParameters.DeserializationOverride(creator);
@@ -63,7 +63,7 @@ public Task<IMultiSearchResponse> MultiSearchAsync(IMultiSearchRequest request)
6363
(p, d) =>
6464
{
6565
var converter = CreateMultiSearchDeserializer(p);
66-
var serializer = new JsonNetSerializer(this.ConnectionSettings, converter);
66+
var serializer = this.ConnectionSettings.StatefulSerializer(converter);
6767
var json = serializer.SerializeToBytes(p).Utf8String();
6868
var creator = new MultiSearchCreator((r, s) => serializer.Deserialize<MultiSearchResponse>(s));
6969
request.RequestParameters.DeserializationOverride(creator);
@@ -83,4 +83,4 @@ private JsonConverter CreateMultiSearchDeserializer(IMultiSearchRequest request)
8383
return new MultiSearchResponseJsonConverter(this.ConnectionSettings, request);
8484
}
8585
}
86-
}
86+
}

0 commit comments

Comments
 (0)