Skip to content

Commit

Permalink
feature (#51)
Browse files Browse the repository at this point in the history
* fix use Guid in filter
* feature ToRelativeUri
* review use contaner model
  • Loading branch information
ZEXSM authored Apr 27, 2021
1 parent 821b984 commit 69201a8
Show file tree
Hide file tree
Showing 21 changed files with 329 additions and 132 deletions.
21 changes: 13 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,19 +40,24 @@ dotnet add package OData.QueryBuilder

1. Build instance

As soon as possible, create a new instance of the OData.QueryBuilder object indicating the data models and the base path:
As soon as possible, create a new instance of the OData.QueryBuilder object indicating the data models and the optional base path:

```csharp
var odataQueryBuilder = new ODataQueryBuilder<Your ODataContainerModel>(<Your base url>);
var odataQueryBuilder = new ODataQueryBuilder<Your OData root model>(<Your base url>?);

// specify the resource for which the request will be built
odataQueryBuilder.For<Your OData entity model>(s => s.ODataEntity)
```

2. Specify the resource for which the request will be built

:information_source: __OData.Query Builder assumes you are using [OData Connected Service](https://marketplace.visualstudio.com/items?itemName=marketplace.ODataConnectedService) and you have a root model otherwise use:__
```csharp
odataQueryBuilder.For<Your ODataEntityModel>(s => s.ODataEntity)
var odataQueryBuilder = new ODataQueryBuilder(<Your base url>?);

// specify the resource for which the request will be built
odataQueryBuilder.For<Your OData entity model>("ODataEntity")
```

3. Select request type
2. Select request type

The builder allows you to build queries on the key and the list:
* [ByKey](#ByKey)
Expand All @@ -77,7 +82,7 @@ dotnet add package OData.QueryBuilder
* [top](#top)
* [skip](#skip)
* [count](#count)
4. Get Uri request or collection of operators from the builder
3. Get Uri request or collection of operators from the builder
```csharp
odataQueryBuilder.ToUri()
odataQueryBuilder.ToDictionary()
Expand Down
32 changes: 32 additions & 0 deletions src/OData.QueryBuilder/Builders/AbstractODataQueryBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using OData.QueryBuilder.Conventions.Constants;
using OData.QueryBuilder.Options;
using System;

namespace OData.QueryBuilder.Builders
{
public abstract class AbstractODataQueryBuilder
{
protected readonly string _baseUrl;
protected readonly ODataQueryBuilderOptions _odataQueryBuilderOptions;

public AbstractODataQueryBuilder(ODataQueryBuilderOptions odataQueryBuilderOptions = default)
{
_baseUrl = string.Empty;
_odataQueryBuilderOptions = odataQueryBuilderOptions ?? new ODataQueryBuilderOptions();
}

public AbstractODataQueryBuilder(string baseUrl, ODataQueryBuilderOptions odataQueryBuilderOptions = default)
{
_baseUrl = !string.IsNullOrEmpty(baseUrl) ?
$"{baseUrl.TrimEnd(QuerySeparators.Slash)}{QuerySeparators.Slash}"
:
throw new ArgumentException($"{nameof(baseUrl)} is null");
_odataQueryBuilderOptions = odataQueryBuilderOptions ?? new ODataQueryBuilderOptions();
}

public AbstractODataQueryBuilder(Uri baseUrl, ODataQueryBuilderOptions odataQueryBuilderOptions = default)
: this(baseUrl?.OriginalString, odataQueryBuilderOptions)
{
}
}
}
26 changes: 17 additions & 9 deletions src/OData.QueryBuilder/Builders/ODataQueryBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
using OData.QueryBuilder.Conventions.AddressingEntities.Resources;
using OData.QueryBuilder.Conventions.Constants;
using OData.QueryBuilder.Conventions.AddressingEntities;
using OData.QueryBuilder.Conventions.AddressingEntities.Resources;
using OData.QueryBuilder.Options;
using System;
using System.Text;

namespace OData.QueryBuilder.Builders
{
public class ODataQueryBuilder<TResource> : ODataResource<TResource>
public sealed class ODataQueryBuilder : AbstractODataQueryBuilder
{
public ODataQueryBuilder(Uri baseUrl, ODataQueryBuilderOptions odataQueryBuilderOptions = default)
: base($"{baseUrl.OriginalString.TrimEnd(QuerySeparators.Slash)}{QuerySeparators.Slash}",
odataQueryBuilderOptions ?? new ODataQueryBuilderOptions())
public ODataQueryBuilder(ODataQueryBuilderOptions odataQueryBuilderOptions = default)
: base(odataQueryBuilderOptions)
{
}

public ODataQueryBuilder(string baseUrl, ODataQueryBuilderOptions odataQueryBuilderOptions = default)
: base($"{baseUrl.TrimEnd(QuerySeparators.Slash)}{QuerySeparators.Slash}",
odataQueryBuilderOptions ?? new ODataQueryBuilderOptions())
: base(baseUrl, odataQueryBuilderOptions)
{
}

public ODataQueryBuilder(Uri baseUrl, ODataQueryBuilderOptions odataQueryBuilderOptions = default)
: base(baseUrl, odataQueryBuilderOptions)
{
}

public IAddressingEntries<TEntity> For<TEntity>(string resource) =>
new ODataResource(new StringBuilder(_baseUrl), _odataQueryBuilderOptions)
.For<TEntity>(resource);
}
}
}
31 changes: 31 additions & 0 deletions src/OData.QueryBuilder/Builders/ODataQueryBuilder{T}.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using OData.QueryBuilder.Conventions.AddressingEntities;
using OData.QueryBuilder.Conventions.AddressingEntities.Resources;
using OData.QueryBuilder.Options;
using System;
using System.Linq.Expressions;
using System.Text;

namespace OData.QueryBuilder.Builders
{
public sealed class ODataQueryBuilder<TResource> : AbstractODataQueryBuilder
{
public ODataQueryBuilder(ODataQueryBuilderOptions odataQueryBuilderOptions = default)
: base(odataQueryBuilderOptions)
{
}

public ODataQueryBuilder(string baseUrl, ODataQueryBuilderOptions odataQueryBuilderOptions = default)
: base(baseUrl, odataQueryBuilderOptions)
{
}

public ODataQueryBuilder(Uri baseUrl, ODataQueryBuilderOptions odataQueryBuilderOptions = default)
: base(baseUrl, odataQueryBuilderOptions)
{
}

public IAddressingEntries<TEntity> For<TEntity>(Expression<Func<TResource, object>> resource) =>
new ODataResource<TResource>(new StringBuilder(_baseUrl), _odataQueryBuilderOptions)
.For<TEntity>(resource);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@

namespace OData.QueryBuilder.Conventions.AddressingEntities.Query.Expand
{
internal class ODataQueryExpandBase
internal abstract class AbstractODataQueryExpand
{
protected readonly ODataQueryBuilderOptions _odataQueryBuilderOptions;
protected readonly StringBuilder _stringBuilder;

public ODataQueryExpandBase(StringBuilder stringBuilder, ODataQueryBuilderOptions odataQueryBuilderOptions)
public AbstractODataQueryExpand(StringBuilder stringBuilder, ODataQueryBuilderOptions odataQueryBuilderOptions)
{
_stringBuilder = stringBuilder;
_odataQueryBuilderOptions = odataQueryBuilderOptions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace OData.QueryBuilder.Conventions.AddressingEntities.Query.Expand
{
internal class ODataQueryExpand<TEntity> : ODataQueryExpandBase, IODataQueryExpand<TEntity>
internal class ODataQueryExpand<TEntity> : AbstractODataQueryExpand, IODataQueryExpand<TEntity>
{
public ODataQueryExpand(ODataQueryBuilderOptions odataQueryBuilderOptions)
: base(new StringBuilder(), odataQueryBuilderOptions)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ namespace OData.QueryBuilder.Conventions.AddressingEntities.Query
{
public interface IODataQuery
{
Uri ToUri();
Uri ToUri(UriKind uriKind = UriKind.RelativeOrAbsolute);

Dictionary<string, string> ToDictionary();
IDictionary<string, string> ToDictionary();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public ODataQuery(StringBuilder stringBuilder, ODataQueryBuilderOptions odataQue
_odataQueryBuilderOptions = odataQueryBuilderOptions;
}

public Dictionary<string, string> ToDictionary()
public IDictionary<string, string> ToDictionary()
{
var odataOperators = _stringBuilder.ToString()
.Split(new char[2] { QuerySeparators.Begin, QuerySeparators.Main }, StringSplitOptions.RemoveEmptyEntries);
Expand All @@ -34,6 +34,6 @@ public Dictionary<string, string> ToDictionary()
return dictionary;
}

public Uri ToUri() => new Uri(_stringBuilder.ToString().TrimEnd(QuerySeparators.Main));
public Uri ToUri(UriKind uriKind = UriKind.RelativeOrAbsolute) => new Uri(_stringBuilder.ToString().TrimEnd(QuerySeparators.Main), uriKind);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ internal class ODataExpandResource<TEntity> : IODataExpandResource<TEntity>
{
private readonly ODataQueryBuilderOptions _odataQueryBuilderOptions;
private readonly StringBuilder _stringBuilder;
private ODataQueryExpandBase _odataOptionNestedBase;
private AbstractODataQueryExpand _odataQueryExpand;

public string Query => $"{_stringBuilder}({_odataOptionNestedBase.Query})";
public string Query => $"{_stringBuilder}({_odataQueryExpand.Query})";

public ODataExpandResource(ODataQueryBuilderOptions odataQueryBuilderOptions)
{
Expand All @@ -25,18 +25,18 @@ public IODataQueryExpand<TNestedEntity> For<TNestedEntity>(Expression<Func<TEnti
{
var query = new ODataResourceExpressionVisitor().ToQuery(nestedExpand.Body);

if (!string.IsNullOrEmpty(_odataOptionNestedBase?.Query))
if (!string.IsNullOrEmpty(_odataQueryExpand?.Query))
{
_stringBuilder.Append($"({_odataOptionNestedBase.Query}),{query}");
_stringBuilder.Append($"({_odataQueryExpand.Query}),{query}");
}
else
{
_stringBuilder.Append(query);
}

_odataOptionNestedBase = new ODataQueryExpand<TNestedEntity>(_odataQueryBuilderOptions);
_odataQueryExpand = new ODataQueryExpand<TNestedEntity>(_odataQueryBuilderOptions);

return _odataOptionNestedBase as ODataQueryExpand<TNestedEntity>;
return _odataQueryExpand as ODataQueryExpand<TNestedEntity>;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
using System;
using System.Linq.Expressions;

namespace OData.QueryBuilder.Conventions.AddressingEntities.Resources
namespace OData.QueryBuilder.Conventions.AddressingEntities.Resources
{
public interface IODataResource<TResource>
internal interface IODataResource
{
IAddressingEntries<TEntity> For<TEntity>(Expression<Func<TResource, object>> resource);
IAddressingEntries<TEntity> For<TEntity>(string resource);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System;
using System.Linq.Expressions;

namespace OData.QueryBuilder.Conventions.AddressingEntities.Resources
{
internal interface IODataResource<TResource>
{
IAddressingEntries<TEntity> For<TEntity>(Expression<Func<TResource, object>> resource);
}
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
using OData.QueryBuilder.Expressions.Visitors;
using OData.QueryBuilder.Options;
using System;
using System.Linq.Expressions;
using OData.QueryBuilder.Options;
using System.Text;

namespace OData.QueryBuilder.Conventions.AddressingEntities.Resources
{
public class ODataResource<TResource> : IODataResource<TResource>
internal class ODataResource : IODataResource
{
private readonly ODataQueryBuilderOptions _odataQueryBuilderOptions;
private readonly string _resourse;
private readonly StringBuilder _stringBuilder;

public ODataResource(string resourse, ODataQueryBuilderOptions odataQueryBuilderOptions)
public ODataResource(StringBuilder stringBuilder, ODataQueryBuilderOptions odataQueryBuilderOptions)
{
_odataQueryBuilderOptions = odataQueryBuilderOptions;
_resourse = resourse;
_stringBuilder = stringBuilder;
}

public IAddressingEntries<TEntity> For<TEntity>(Expression<Func<TResource, object>> resource)
public IAddressingEntries<TEntity> For<TEntity>(string resource)
{
var query = new ODataResourceExpressionVisitor().ToQuery(resource.Body);
_stringBuilder.Append(resource);

return new AddressingEntries<TEntity>(new StringBuilder($"{_resourse}{query}"), _odataQueryBuilderOptions);
return new AddressingEntries<TEntity>(_stringBuilder, _odataQueryBuilderOptions);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using OData.QueryBuilder.Expressions.Visitors;
using OData.QueryBuilder.Options;
using System;
using System.Linq.Expressions;
using System.Text;

namespace OData.QueryBuilder.Conventions.AddressingEntities.Resources
{
internal class ODataResource<TResource> : IODataResource<TResource>
{
private readonly ODataQueryBuilderOptions _odataQueryBuilderOptions;
private readonly StringBuilder _stringBuilder;

public ODataResource(StringBuilder stringBuilder, ODataQueryBuilderOptions odataQueryBuilderOptions)
{
_odataQueryBuilderOptions = odataQueryBuilderOptions;
_stringBuilder = stringBuilder;
}

public IAddressingEntries<TEntity> For<TEntity>(Expression<Func<TResource, object>> resource)
{
var query = new ODataResourceExpressionVisitor().ToQuery(resource.Body);

_stringBuilder.Append(query);

return new AddressingEntries<TEntity>(_stringBuilder, _odataQueryBuilderOptions);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -181,12 +181,7 @@ protected override string VisitNewExpression(NewExpression newExpression)
arguments[i] = _valueExpression.GetValue(newExpression.Arguments[i]);
}

if (newExpression.Type == typeof(DateTime) || newExpression.Type == typeof(DateTimeOffset))
{
return newExpression.Constructor.Invoke(arguments).ToQuery();
}

return default;
return (arguments.Length == 0 ? Activator.CreateInstance(newExpression.Type) : newExpression.Constructor.Invoke(arguments)).ToQuery();
}

return base.VisitNewExpression(newExpression);
Expand Down
2 changes: 2 additions & 0 deletions src/OData.QueryBuilder/Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ public static string ToQuery(this object @object)
var stringValuesString = string.Join($"'{QuerySeparators.Comma}'", stringValues);

return !string.IsNullOrEmpty(stringValuesString) ? $"'{stringValuesString}'" : string.Empty;
case Guid @guid:
return $"{@guid}";
default:
return $"'{@object}'";
}
Expand Down
4 changes: 2 additions & 2 deletions test/OData.QueryBuilder.Benchmark/ODataQueryBenchmark.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ public Uri ODataQueryBuilderKey_Expand_Nested_Filter() => _odataQueryBuilder
.ToUri();

[Benchmark]
public Dictionary<string, string> ODataQueryBuilderKey_ToDicionary() => _odataQueryBuilder
public IDictionary<string, string> ODataQueryBuilderKey_ToDicionary() => _odataQueryBuilder
.For<ODataTypeEntity>(s => s.ODataType)
.ByKey("223123123")
.Expand(s => s.ODataKind)
Expand Down Expand Up @@ -584,7 +584,7 @@ public Uri FilterEnumTest() => _odataQueryBuilder
private ODataTypeEntity ODataQueryBuilderList_ToDicionary_NewObject = new ODataTypeEntity { IsOpen = false };

[Benchmark]
public Dictionary<string, string> ToDicionary() => _odataQueryBuilder
public IDictionary<string, string> ToDicionary() => _odataQueryBuilder
.For<ODataTypeEntity>(s => s.ODataType)
.ByList()
.Filter(s => s.IsActive
Expand Down
2 changes: 2 additions & 0 deletions test/OData.QueryBuilder.Fakes/ODataTypeEntity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ namespace OData.QueryBuilder.Fakes
{
public class ODataTypeEntity
{
public Guid Id { get; set; }

public int IdType { get; set; }

public int? IdRule { get; set; }
Expand Down
Loading

0 comments on commit 69201a8

Please sign in to comment.