Skip to content

Commit 871a2bd

Browse files
Ihar YakimushIhar Yakimush
authored andcommitted
select anonymous type
1 parent 8427b2b commit 871a2bd

File tree

11 files changed

+138
-71
lines changed

11 files changed

+138
-71
lines changed

SolrNet.Linq.IntegrationTests/SelectTests.cs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4-
using System.Runtime.Serialization;
5-
using SolrNet.Attributes;
6-
using SolrNet.Commands.Parameters;
74
using SolrNet.Exceptions;
85
using Xunit;
96

@@ -38,6 +35,25 @@ public void AnonymousClass()
3835

3936
Assert.NotNull(t1);
4037
Assert.NotNull(t1.Id);
38+
Assert.Equal(4, t1.Qwe);
39+
Assert.True(t1.Categories.Count > 0);
40+
Assert.True(t1.Price > 0);
41+
}
42+
43+
[Fact]
44+
public void AnonymousClassSolrResult()
45+
{
46+
var t1 = Product.SolrOperations.Value.AsQueryable().Where(p => p.Id != null)
47+
.Select(p => new {p.Id, p.Price, p.Categories, Qwe = Math.Pow(2, 2)})
48+
.Where(arg => arg.Categories.Any(s => s == "electronics"))
49+
.OrderBy(arg => arg.Id)
50+
.ToSolrQueryResults();
51+
52+
Assert.NotNull(t1);
53+
Assert.NotNull(t1[0].Id);
54+
Assert.Equal(4, t1[0].Qwe);
55+
Assert.True(t1.Count > 0);
56+
Assert.True(t1.NumFound > 0);
4157
}
4258

4359
[Fact]

SolrNet.Linq/Impl/AnonymousTypeActivator.cs

Lines changed: 0 additions & 35 deletions
This file was deleted.

SolrNet.Linq/Impl/ExecuterExtensions.cs

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Linq;
33
using System.Reflection;
4+
using System.Runtime.CompilerServices;
45
using SolrNet.Impl;
56
using SolrNet.Impl.DocumentPropertyVisitors;
67
using SolrNet.Impl.FieldParsers;
@@ -11,7 +12,7 @@ namespace SolrNet.Linq.Impl
1112
{
1213
public static class ExecuterExtensions
1314
{
14-
public static IExecuter<TNew> ChangeType<TNew, TOld>(this IExecuter<TOld> executer)
15+
public static IExecuter<TNew> ChangeType<TNew, TOld>(this IExecuter<TOld> executer, ISolrFieldParser sfp = null)
1516
{
1617
try
1718
{
@@ -21,15 +22,23 @@ public static IExecuter<TNew> ChangeType<TNew, TOld>(this IExecuter<TOld> execut
2122
ISolrQuerySerializer serializer = oldExecuter.GetSingleField<ISolrQuerySerializer>();
2223
ISolrFacetQuerySerializer facetQuerySerializer = oldExecuter.GetSingleField<ISolrFacetQuerySerializer>();
2324

24-
//TODO:
25-
IReadOnlyMappingManager mapper = new AllPropertiesMappingManager();
25+
sfp = sfp ?? new DefaultFieldParser();
26+
ISolrDocumentResponseParser<TNew> docParser;
2627

27-
ISolrFieldParser sfp = new DefaultFieldParser();
28-
ISolrDocumentPropertyVisitor sdpv = new DefaultDocumentVisitor(mapper, sfp);
29-
30-
ISolrAbstractResponseParser<TNew> parser =
31-
new DefaultResponseParser<TNew>(
32-
new SolrDocumentResponseParser<TNew>(mapper, sdpv, new AnonymousTypeActivator<TNew>()));
28+
// Anonymous types can't be created by default SolrNet parsers, because the don't have property setters.
29+
if (CheckIfAnonymousType(typeof(TNew)))
30+
{
31+
docParser =
32+
new SelectResponseParser<TNew>(sfp);
33+
}
34+
else
35+
{
36+
IReadOnlyMappingManager mapper = new AllPropertiesMappingManager();
37+
docParser = new SolrDocumentResponseParser<TNew>(mapper, new DefaultDocumentVisitor(mapper, sfp),
38+
new SolrDocumentActivator<TNew>());
39+
}
40+
41+
ISolrAbstractResponseParser<TNew> parser = new DefaultResponseParser<TNew>(docParser);
3342

3443
SolrQueryExecuter<TNew> newExecuter = new SolrQueryExecuter<TNew>(parser, connection, serializer,
3544
facetQuerySerializer,
@@ -61,5 +70,17 @@ internal static T GetSingleField<T>(this object instance)
6170

6271
return (T)field.GetValue(instance);
6372
}
73+
74+
private static bool CheckIfAnonymousType(Type type)
75+
{
76+
if (type == null)
77+
throw new ArgumentNullException(nameof(type));
78+
79+
// HACK: The only way to detect anonymous types right now.
80+
return Attribute.IsDefined(type, typeof(CompilerGeneratedAttribute), false)
81+
&& type.IsGenericType && type.Name.Contains("AnonymousType")
82+
&& (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$"))
83+
&& (type.Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic;
84+
}
6485
}
6586
}

SolrNet.Linq/Impl/IAsyncProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System.Linq.Expressions;
22
using System.Threading.Tasks;
33

4-
namespace SolrNet.Linq
4+
namespace SolrNet.Linq.Impl
55
{
66
public interface IAsyncProvider<TEntity>
77
{

SolrNet.Linq/Impl/SelectQueryExecutor.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,21 @@ namespace SolrNet.Linq.Impl
77
{
88
public class SelectQueryExecutor<T> : IExecuter<T>
99
{
10-
private readonly SolrQueryExecuter<T> _executer;
11-
12-
public SelectQueryExecutor(SolrQueryExecuter<T> executer)
10+
public SelectQueryExecutor(
11+
SolrQueryExecuter<T> executer)
1312
{
14-
_executer = executer ?? throw new ArgumentNullException(nameof(executer));
13+
Executer = executer ?? throw new ArgumentNullException(nameof(executer));
1514
}
1615
public SolrQueryResults<T> Execute(ISolrQuery q, QueryOptions options)
1716
{
18-
return this._executer.Execute(q, options);
17+
return this.Executer.Execute(q, options);
1918
}
2019

2120
public Task<SolrQueryResults<T>> ExecuteAsync(ISolrQuery q, QueryOptions options)
2221
{
23-
return this._executer.ExecuteAsync(q, options);
22+
return this.Executer.ExecuteAsync(q, options);
2423
}
2524

26-
public SolrQueryExecuter<T> Executer => this._executer;
27-
}
25+
public SolrQueryExecuter<T> Executer { get; }
26+
}
2827
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
using System.Xml.Linq;
6+
using SolrNet.Impl;
7+
using SolrNet.Mapping;
8+
9+
namespace SolrNet.Linq.Impl
10+
{
11+
public class SelectResponseParser<T> : ISolrDocumentResponseParser<T>
12+
{
13+
private readonly ConstructorInfo CtorInfo = typeof(T).GetConstructors().Single();
14+
private readonly ISolrFieldParser parser ;
15+
16+
public SelectResponseParser(ISolrFieldParser parser)
17+
{
18+
this.parser = parser ?? throw new ArgumentNullException(nameof(parser));
19+
}
20+
21+
public IList<T> ParseResults(XElement parentNode)
22+
{
23+
List<T> objList = new List<T>();
24+
if (parentNode == null)
25+
return (IList<T>)objList;
26+
foreach (XElement element in parentNode.Elements((XName)"doc"))
27+
objList.Add(this.ParseDocument(element));
28+
return (IList<T>)objList;
29+
}
30+
31+
public T ParseDocument(XElement node)
32+
{
33+
Dictionary<string, XElement> fields = node.Elements().ToDictionary(element => element.Attribute((XName) "name").Value);
34+
35+
List<object> args = new List<object>(fields.Count);
36+
foreach (ParameterInfo p in CtorInfo.GetParameters())
37+
{
38+
object obj = p.ParameterType.IsValueType ? Activator.CreateInstance(p.ParameterType) : null;
39+
if (fields.ContainsKey(p.Name))
40+
{
41+
if (this.parser.CanHandleSolrType(fields[p.Name].Name.LocalName) &&
42+
this.parser.CanHandleType(p.ParameterType))
43+
{
44+
obj = this.parser.Parse(fields[p.Name], p.ParameterType);
45+
46+
if (obj != null)
47+
{
48+
if (!p.ParameterType.IsAssignableFrom(obj.GetType()))
49+
{
50+
throw new InvalidOperationException(
51+
$"Unable to set value for {p.Name}. Value {obj} of type {obj.GetType()} not assignable to type {p.ParameterType}");
52+
}
53+
}
54+
else if (p.ParameterType.IsValueType)
55+
{
56+
throw new InvalidOperationException(
57+
$"Unable to set value for {p.Name}. Value null not assignable to type {p.ParameterType}");
58+
}
59+
}
60+
}
61+
62+
args.Add(obj);
63+
}
64+
65+
return (T) CtorInfo.Invoke(args.ToArray());
66+
}
67+
}
68+
}

SolrNet.Linq/Impl/TypeSystem.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33

4-
namespace SolrNet.Linq
4+
namespace SolrNet.Linq.Impl
55
{
66
internal static class TypeSystem
77
{

SolrNet.Linq/SolrLinqExtensions.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Linq.Expressions;
44
using System.Reflection;
55
using System.Threading.Tasks;
6+
using SolrNet.Linq.Impl;
67
using SolrNet.Mapping.Validation;
78

89
namespace SolrNet.Linq

SolrNet.Linq/SolrNetLinqOptions.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using SolrNet.Commands.Parameters;
33
using SolrNet.Impl;
4+
using SolrNet.Impl.FieldParsers;
45
using SolrNet.Impl.FieldSerializers;
56
using SolrNet.Mapping;
67

@@ -30,5 +31,10 @@ public class SolrNetLinqOptions
3031
/// Set <see cref="IReadOnlyMappingManager"/> field mapping manager. If not set <see cref="AttributesMappingManager"/> attributes mapping manager will be used. More about SolrNet mapping: https://github.com/SolrNet/SolrNet/blob/master/Documentation/Mapping.md
3132
/// </summary>
3233
public IReadOnlyMappingManager MappingManager { get; set; } = null;
34+
35+
/// <summary>
36+
/// Set solr field parser which will be used in case of Select() method applying. If not set <see cref="DefaultFieldParser"/> will be used.
37+
/// </summary>
38+
public ISolrFieldParser SolrFieldParser { get; set; } = null;
3339
}
3440
}

SolrNet.Linq/SolrOperationsExtensions.cs

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,12 @@ public static class SolrOperationsExtensions
1111
{
1212
public static IQueryable<T> AsQueryable<T>(this ISolrBasicReadOnlyOperations<T> operations, Action<SolrNetLinqOptions> setupOptions = null)
1313
{
14-
SolrNetLinqOptions o = new SolrNetLinqOptions();
15-
setupOptions?.Invoke(o);
14+
SolrNetLinqOptions options = new SolrNetLinqOptions();
15+
setupOptions?.Invoke(options);
1616
return new SolrQuery<T>(new SolrQueryProvider<T>(
1717
new SolrQueryExecuterWrapperBasicOperations<T>(operations),
18-
o, null));
19-
}
20-
21-
//public static IQueryable<T> AsQueryable<T>(this ISolrQueryExecuter<T> operations, Action<SolrNetLinqOptions> setupOptions = null)
22-
//{
23-
// SolrNetLinqOptions o = new SolrNetLinqOptions();
24-
// setupOptions?.Invoke(o);
25-
// return new SolrQuery<T>(new SolrQueryProvider<T>(
26-
// operations,
27-
// o, null));
28-
//}
18+
options, null));
19+
}
2920

3021
public static SolrQueryResults<T> ToSolrQueryResults<T>(this IQueryable<T> queryable)
3122
{

0 commit comments

Comments
 (0)