Skip to content

Commit b3f9934

Browse files
committed
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 142302f commit b3f9934

18 files changed

+580
-51
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: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,27 +15,34 @@ 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>
3336
/// 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

@@ -48,6 +55,30 @@ internal JsonNetSerializer(IConnectionSettingsValues settings, JsonConverter sta
4855
};
4956
}
5057

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+
5182
public virtual void Serialize(object data, Stream writableStream, SerializationFormatting formatting = SerializationFormatting.Indented)
5283
{
5384
var serializer = _defaultSerializers[formatting];
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: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ public IMultiGetResponse MultiGet(IMultiGetRequest request) =>
5656
new MultiGetConverter((r, s) => this.DeserializeMultiGetResponse(r, s, CreateCovariantMultiGetConverter(request))),
5757
this.LowLevelDispatch.MgetDispatchAsync<MultiGetResponse>
5858
);
59-
private MultiGetResponse DeserializeMultiGetResponse(IApiCallDetails response, Stream stream, JsonConverter converter)=>
60-
new JsonNetSerializer(this.ConnectionSettings, converter).Deserialize<MultiGetResponse>(stream);
59+
private MultiGetResponse DeserializeMultiGetResponse(IApiCallDetails response, Stream stream, JsonConverter converter) =>
60+
this.ConnectionSettings.StatefulSerializer(converter).Deserialize<MultiGetResponse>(stream);
6161

6262
private JsonConverter CreateCovariantMultiGetConverter(IMultiGetRequest descriptor) => new MultiGetHitJsonConverter(descriptor);
6363

src/Nest/Nest.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,7 @@
467467
<Compile Include="CommonAbstractions\SerializationBehavior\GenericJsonConverters\VerbatimDictionaryKeysConverter.cs" />
468468
<Compile Include="CommonAbstractions\SerializationBehavior\JsonNetSerializer.cs" />
469469
<Compile Include="CommonAbstractions\SerializationBehavior\JsonReaderExtensions.cs" />
470+
<Compile Include="CommonAbstractions\SerializationBehavior\SerializerFactory.cs" />
470471
<Compile Include="CommonAbstractions\SerializationBehavior\StatefulDeserialization\ConcreteTypeConverter.cs" />
471472
<Compile Include="CommonAbstractions\SerializationBehavior\StatefulDeserialization\JsonConverterPiggyBackState.cs" />
472473
<Compile Include="CommonAbstractions\Static\Static.cs" />

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public IMultiSearchResponse MultiSearch(IMultiSearchRequest request)
4242
(p, d) =>
4343
{
4444
var converter = CreateMultiSearchDeserializer(p);
45-
var serializer = new JsonNetSerializer(this.ConnectionSettings, converter);
45+
var serializer = this.ConnectionSettings.StatefulSerializer(converter);
4646
var json = serializer.SerializeToBytes(p).Utf8String();
4747
var creator = new MultiSearchCreator((r, s) => serializer.Deserialize<MultiSearchResponse>(s));
4848
request.RequestParameters.DeserializationOverride(creator);
@@ -65,7 +65,7 @@ public IMultiSearchResponse MultiSearch(IMultiSearchRequest request)
6565
(p, d, c) =>
6666
{
6767
var converter = CreateMultiSearchDeserializer(p);
68-
var serializer = new JsonNetSerializer(this.ConnectionSettings, converter);
68+
var serializer = this.ConnectionSettings.StatefulSerializer(converter);
6969
var json = serializer.SerializeToBytes(p).Utf8String();
7070
var creator = new MultiSearchCreator((r, s) => serializer.Deserialize<MultiSearchResponse>(s));
7171
request.RequestParameters.DeserializationOverride(creator);

src/Nest/Search/MultiSearch/MultiSearchResponseJsonConverter.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,23 +54,23 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
5454
var descriptor = m.Descriptor.Value;
5555
var concreteTypeSelector = descriptor.TypeSelector;
5656
var baseType = m.Descriptor.Value.ClrType ?? typeof(object);
57-
57+
5858
var generic = MakeDelegateMethodInfo.MakeGenericMethod(baseType);
5959

6060
if (concreteTypeSelector != null)
6161
{
6262
var state = typeof(ConcreteTypeConverter<>).CreateGenericInstance(baseType, concreteTypeSelector) as JsonConverter;
6363
if (state != null)
6464
{
65-
var elasticSerializer = new JsonNetSerializer(this._settings, state);
65+
var elasticSerializer = this._settings.StatefulSerializer(state);
6666

67-
generic.Invoke(null, new object[] { m, elasticSerializer.Serializer, response.Responses, this._settings });
67+
generic.Invoke(null, new object[] { m, elasticSerializer, response.Responses, this._settings });
6868
continue;
6969
}
7070
}
7171
generic.Invoke(null, new object[] { m, serializer, response.Responses, this._settings });
7272
}
73-
73+
7474
return response;
7575
}
7676

@@ -86,9 +86,9 @@ private class MultiHitTuple
8686
}
8787

8888
private static void CreateMultiHit<T>(
89-
MultiHitTuple tuple,
90-
JsonSerializer serializer,
91-
IDictionary<string, object> collection,
89+
MultiHitTuple tuple,
90+
JsonSerializer serializer,
91+
IDictionary<string, object> collection,
9292
IConnectionSettingsValues settings
9393
)
9494
where T : class

0 commit comments

Comments
 (0)