Skip to content

Commit f92ad61

Browse files
authored
Merge pull request #5 from yv989c/feature/new-types
Feature/new types
2 parents 7ef835d + 80a9cff commit f92ad61

18 files changed

+1160
-153
lines changed

README.md

Lines changed: 41 additions & 35 deletions
Large diffs are not rendered by default.

Version.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
<Project>
22
<PropertyGroup>
3-
<VersionEFCore3>3.2.0</VersionEFCore3>
4-
<VersionEFCore5>5.2.0</VersionEFCore5>
5-
<VersionEFCore6>6.2.0</VersionEFCore6>
3+
<VersionEFCore3>3.3.0</VersionEFCore3>
4+
<VersionEFCore5>5.3.0</VersionEFCore5>
5+
<VersionEFCore6>6.3.0</VersionEFCore6>
66
</PropertyGroup>
77
<!--4a1164f5-e747-41d5-b575-cbd08bdbedbd-->
88
</Project>

docs/README.md

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,36 @@
11
# QueryableValues
22

3-
This library allows us to efficiently compose an [IEnumerable<T>] in our [Entity Framework Core] queries when using the [SQL Server Database Provider]. This is done by using the `AsQueryableValues` extension method that is made available on the [DbContext] class. Everything is evaluated on the server with a single roundtrip, in a way that preserves the query's [execution plan], even when the values behind the [IEnumerable<T>] are changed on subsequent executions.
3+
This library allows you to efficiently compose an [IEnumerable<T>] in your [Entity Framework Core] queries when using the [SQL Server Database Provider]. This is accomplished by using the `AsQueryableValues` extension method available on the [DbContext] class. Everything is evaluated on the server with a single round trip, in a way that preserves the query's [execution plan], even when the values behind the [IEnumerable<T>] are changed on subsequent executions.
44

55
The supported types for `T` are:
6-
- Simple Type: [Int32], [Int64], [Decimal], [Double], [DateTime], [DateTimeOffset], [Guid], and [String].
6+
- Simple Type: [Byte], [Int16], [Int32], [Int64], [Decimal], [Single], [Double], [DateTime], [DateTimeOffset], [Guid], [Char], and [String].
77
- Complex Type:
88
- Can be an anonymous type.
9-
- Can be a user defined class or struct, with read/write properties and a public constructor.
10-
- Must have one or more simple type properties.
9+
- Can be a user-defined class or struct with read/write properties and a public constructor.
10+
- Must have one or more simple type properties, including [Boolean].
1111

1212
For a detailed explanation, please continue reading [here][readme-background].
1313

14-
## When Should I Use It?
15-
The `AsQueryableValues` extension method is intended for queries that are dependent on a *non-constant* sequence of external values. In this case, the underline SQL query will be efficient on subsequent executions.
14+
## When Should You Use It?
15+
The `AsQueryableValues` extension method is intended for queries that are dependent upon a *non-constant* sequence of external values. In such cases, the underlying SQL query will be efficient on subsequent executions.
1616

17-
It provides a solution to the following long standing EF Core [issue](https://github.com/dotnet/efcore/issues/13617) and enables other currently unsupported scenarios; like the ability to efficiently create joins with in-memory data.
17+
It provides a solution to the following long standing [EF Core issue](https://github.com/dotnet/efcore/issues/13617) and enables other currently unsupported scenarios; like the ability to efficiently create joins with in-memory data.
1818

1919
## Getting Started
2020

2121
### Installation
22-
QueryableValues is distributed as a [NuGet Package]. The major version number of this library is aligned with the version of [Entity Framework Core] that's supported by it; for example, if you are using EF Core 5, then you must use version 5 of QueryableValues.
22+
QueryableValues is distributed as a [NuGet Package]. The major version number of this library is aligned with the version of [Entity Framework Core] by which it's supported (e.g. If you are using EF Core 5, then you must use version 5 of QueryableValues).
2323

2424
Please choose the appropriate command below to install it using the NuGet Package Manager Console window in Visual Studio:
2525

2626
EF Core | Command
2727
:---: | ---
28-
3.x | `Install-Package BlazarTech.QueryableValues.SqlServer -Version 3.2.0`
29-
5.x | `Install-Package BlazarTech.QueryableValues.SqlServer -Version 5.2.0`
30-
6.x | `Install-Package BlazarTech.QueryableValues.SqlServer -Version 6.2.0`
28+
3.x | `Install-Package BlazarTech.QueryableValues.SqlServer -Version 3.3.0`
29+
5.x | `Install-Package BlazarTech.QueryableValues.SqlServer -Version 5.3.0`
30+
6.x | `Install-Package BlazarTech.QueryableValues.SqlServer -Version 6.3.0`
3131

3232
### Configuration
33-
Look for the place in your code where you are setting up your [DbContext] and calling the [UseSqlServer] extension method, then use a lambda expression to access the `SqlServerDbContextOptionsBuilder` provided by it. It is on this builder that you must call the `UseQueryableValues` extension method, as shown in the following simplified examples:
33+
Look for the place in your code where you are setting up your [DbContext] and calling the [UseSqlServer] extension method, then use a lambda expression to access the `SqlServerDbContextOptionsBuilder` provided by it. It is on this builder that you must call the `UseQueryableValues` extension method as shown in the following simplified examples:
3434

3535
When using the `OnConfiguring` method inside your [DbContext]:
3636
```c#
@@ -71,13 +71,13 @@ public class Startup
7171
}
7272
```
7373

74-
### How Do I Use It?
75-
The `AsQueryableValues` extension method is provided by the `BlazarTech.QueryableValues` namespace, therefore, you must add the following `using` directive to your source code file in order for it to appear as a method of your [DbContext] instance:
74+
### How Do You Use It?
75+
The `AsQueryableValues` extension method is provided by the `BlazarTech.QueryableValues` namespace; therefore, you must add the following `using` directive to your source code file for it to appear as a method of your [DbContext] instance:
7676
```
7777
using BlazarTech.QueryableValues;
7878
```
7979

80-
Below you can find a few examples composing a query using the values provided by an [IEnumerable<T>].
80+
Below are a few examples composing a query using the values provided by an [IEnumerable<T>].
8181

8282
#### Simple Type Examples
8383
Using the [Contains][ContainsQueryable] LINQ method:
@@ -138,10 +138,11 @@ var myQuery2 =
138138
i.PropA
139139
});
140140
```
141-
#### Complex Type Examples
141+
#### Complex Type Example
142142
```c#
143-
// If your IEnumerable<T> variable's item type is a complex type with many properties,
144-
// project only what you need to a new variable and use it in your query.
143+
// Performance Tip:
144+
// If your IEnumerable<T> item type (T) has many properties, project only
145+
// the ones you need to a new variable and use it in your query.
145146
var projectedItems = items.Select(i => new { i.CategoryId, i.ColorName });
146147

147148
var myQuery =
@@ -154,12 +155,12 @@ var myQuery =
154155
};
155156
```
156157
**About Complex Types**
157-
> :warning: All the data provided by this type is transmitted to the server, therefore, ensure that it only contains the properties that you need for your query. Not following this recommendation will degrade the query's performance.
158+
> :warning: All the data provided by this type is transmitted to the server; therefore, ensure that it only contains the properties you need for your query. Not following this recommendation will degrade the query's performance.
158159
159-
> :warning: There is a limit of up to ten properties for any given simple type (e.g., cannot have more than ten [Int32] properties). Exceeding that limit will cause an exception and may also be a sign that you should rethink your strategy.
160+
> :warning: There is a limit of up to 10 properties for any given simple type (e.g. cannot have more than 10 [Int32] properties). Exceeding that limit will cause an exception and may also suggest that you should rethink your strategy.
160161
161-
## Do You Want to Know More? 📚
162-
Please take a look at the repository [here](https://github.com/yv989c/BlazarTech.QueryableValues).
162+
## Do You Want To Know More? 📚
163+
Please take a look at the [repository](https://github.com/yv989c/BlazarTech.QueryableValues).
163164

164165

165166
[Entity Framework Core]: https://docs.microsoft.com/en-us/ef/core/
@@ -176,11 +177,16 @@ Please take a look at the repository [here](https://github.com/yv989c/BlazarTech
176177
[NuGet Package]: https://www.nuget.org/packages/BlazarTech.QueryableValues.SqlServer/
177178
[readme-background]: https://github.com/yv989c/BlazarTech.QueryableValues#background-
178179
[execution plan]: https://docs.microsoft.com/en-us/sql/relational-databases/query-processing-architecture-guide?#execution-plan-caching-and-reuse
180+
[Boolean]: https://docs.microsoft.com/en-us/dotnet/api/system.boolean
181+
[Byte]: https://docs.microsoft.com/en-us/dotnet/api/system.byte
182+
[Int16]: https://docs.microsoft.com/en-us/dotnet/api/system.int16
179183
[Int32]: https://docs.microsoft.com/en-us/dotnet/api/system.int32
180184
[Int64]: https://docs.microsoft.com/en-us/dotnet/api/system.int64
181185
[Decimal]: https://docs.microsoft.com/en-us/dotnet/api/system.decimal
186+
[Single]: https://docs.microsoft.com/en-us/dotnet/api/system.single
182187
[Double]: https://docs.microsoft.com/en-us/dotnet/api/system.double
183188
[DateTime]: https://docs.microsoft.com/en-us/dotnet/api/system.datetime
184189
[DateTimeOffset]: https://docs.microsoft.com/en-us/dotnet/api/system.datetimeoffset
185190
[Guid]: https://docs.microsoft.com/en-us/dotnet/api/system.guid
191+
[Char]: https://docs.microsoft.com/en-us/dotnet/api/system.char
186192
[String]: https://docs.microsoft.com/en-us/dotnet/api/system.string

src/QueryableValues.SqlServer/Builders/EntityOptionsBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public PropertyOptionsBuilder<TProperty> Property<TProperty>(Expression<Func<T,
5252
}
5353

5454
/// <summary>
55-
/// Sets the default behavior for the handling of unicode characters in <see cref="string"/> properties.
55+
/// Sets the default behavior for the handling of unicode characters in <see cref="char"/> and <see cref="string"/> properties.
5656
/// </summary>
5757
/// <param name="isUnicode">A value indicating whether support for unicode characters is enabled by default.</param>
5858
/// <returns>The same builder instance so that multiple configuration calls can be chained.</returns>

src/QueryableValues.SqlServer/Builders/PropertyOptionsBuilder.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,21 @@ internal PropertyOptionsBuilder(MemberInfo memberInfo)
2323
}
2424

2525
/// <summary>
26-
/// Configures the property as capable of handling unicode characters. Can only be set on <see cref="string"/> properties.
26+
/// Configures the property as capable of handling unicode characters. Can only be set on <see cref="char"/> and <see cref="string"/> properties.
2727
/// </summary>
2828
/// <param name="isUnicode">A value indicating whether the property can handle unicode characters.</param>
2929
/// <returns>The same builder instance so that multiple configuration calls can be chained.</returns>
3030
/// <exception cref="InvalidOperationException"></exception>
3131
public PropertyOptionsBuilder<TProperty> IsUnicode(bool isUnicode = true)
3232
{
33-
if (typeof(TProperty) != typeof(string))
33+
var isValidType =
34+
typeof(TProperty) == typeof(string) ||
35+
typeof(TProperty) == typeof(char) ||
36+
typeof(TProperty) == typeof(char?);
37+
38+
if (!isValidType)
3439
{
35-
throw new InvalidOperationException("This method can only be used on String properties.");
40+
throw new InvalidOperationException("This method can only be used on Char and String properties.");
3641
}
3742

3843
_isUnicode = isUnicode;

src/QueryableValues.SqlServer/DeferredValues.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,18 @@ public long ToInt64(IFormatProvider? provider)
7474
public ulong ToUInt64(IFormatProvider? provider) => throw new NotImplementedException();
7575
}
7676

77+
internal sealed class DeferredByteValues : DeferredValues<byte>
78+
{
79+
public DeferredByteValues(IEnumerable<byte> values) : base(values) { }
80+
public override string ToString(IFormatProvider? provider) => XmlUtil.GetXml(_values);
81+
}
82+
83+
internal sealed class DeferredInt16Values : DeferredValues<short>
84+
{
85+
public DeferredInt16Values(IEnumerable<short> values) : base(values) { }
86+
public override string ToString(IFormatProvider? provider) => XmlUtil.GetXml(_values);
87+
}
88+
7789
internal sealed class DeferredInt32Values : DeferredValues<int>
7890
{
7991
public DeferredInt32Values(IEnumerable<int> values) : base(values) { }
@@ -92,6 +104,12 @@ public DeferredDecimalValues(IEnumerable<decimal> values) : base(values) { }
92104
public override string ToString(IFormatProvider? provider) => XmlUtil.GetXml(_values);
93105
}
94106

107+
internal sealed class DeferredSingleValues : DeferredValues<float>
108+
{
109+
public DeferredSingleValues(IEnumerable<float> values) : base(values) { }
110+
public override string ToString(IFormatProvider? provider) => XmlUtil.GetXml(_values);
111+
}
112+
95113
internal sealed class DeferredDoubleValues : DeferredValues<double>
96114
{
97115
public DeferredDoubleValues(IEnumerable<double> values) : base(values) { }
@@ -116,10 +134,15 @@ public DeferredGuidValues(IEnumerable<Guid> values) : base(values) { }
116134
public override string ToString(IFormatProvider? provider) => XmlUtil.GetXml(_values);
117135
}
118136

137+
internal sealed class DeferredCharValues : DeferredValues<char>
138+
{
139+
public DeferredCharValues(IEnumerable<char> values) : base(values) { }
140+
public override string ToString(IFormatProvider? provider) => XmlUtil.GetXml(_values);
141+
}
142+
119143
internal sealed class DeferredStringValues : DeferredValues<string>
120144
{
121145
public DeferredStringValues(IEnumerable<string> values) : base(values) { }
122-
123146
public override string ToString(IFormatProvider? provider) => XmlUtil.GetXml(_values);
124147
}
125148

@@ -133,6 +156,7 @@ public DeferredEntityValues(IEnumerable<T> values, IReadOnlyList<EntityPropertyM
133156
{
134157
_mappings = mappings;
135158
}
159+
136160
public override string ToString(IFormatProvider? provider) => XmlUtil.GetXml(_values, _mappings);
137161
}
138162
}

src/QueryableValues.SqlServer/EntityPropertyMapping.cs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ namespace BlazarTech.QueryableValues
88
{
99
internal sealed class EntityPropertyMapping
1010
{
11+
internal static readonly IReadOnlyDictionary<Type, EntityPropertyTypeName> SimpleTypes;
12+
1113
private static readonly PropertyInfo[] EntityProperties = typeof(QueryableValuesEntity).GetProperties();
1214
private static readonly ConcurrentDictionary<Type, IReadOnlyList<EntityPropertyMapping>> MappingCache = new ConcurrentDictionary<Type, IReadOnlyList<EntityPropertyMapping>>();
13-
private static readonly Dictionary<Type, EntityPropertyTypeName> SimpleTypes;
1415

1516
public PropertyInfo Source { get; }
1617
public PropertyInfo Target { get; }
@@ -21,13 +22,18 @@ static EntityPropertyMapping()
2122
{
2223
SimpleTypes = new Dictionary<Type, EntityPropertyTypeName>
2324
{
24-
{ typeof(int), EntityPropertyTypeName.Int },
25-
{ typeof(long), EntityPropertyTypeName.Long },
25+
{ typeof(bool), EntityPropertyTypeName.Boolean },
26+
{ typeof(byte), EntityPropertyTypeName.Byte },
27+
{ typeof(short), EntityPropertyTypeName.Int16 },
28+
{ typeof(int), EntityPropertyTypeName.Int32 },
29+
{ typeof(long), EntityPropertyTypeName.Int64 },
2630
{ typeof(decimal), EntityPropertyTypeName.Decimal },
31+
{ typeof(float), EntityPropertyTypeName.Single },
2732
{ typeof(double), EntityPropertyTypeName.Double },
2833
{ typeof(DateTime), EntityPropertyTypeName.DateTime },
2934
{ typeof(DateTimeOffset), EntityPropertyTypeName.DateTimeOffset },
3035
{ typeof(Guid), EntityPropertyTypeName.Guid },
36+
{ typeof(char), EntityPropertyTypeName.Char },
3137
{ typeof(string), EntityPropertyTypeName.String }
3238
};
3339
}

src/QueryableValues.SqlServer/EntityPropertyTypeName.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,18 @@
22
{
33
internal enum EntityPropertyTypeName
44
{
5-
Int,
6-
Long,
5+
Boolean,
6+
Byte,
7+
Int16,
8+
Int32,
9+
Int64,
710
Decimal,
11+
Single,
812
Double,
913
DateTime,
1014
DateTimeOffset,
1115
Guid,
16+
Char,
1217
String
1318
}
1419
}

src/QueryableValues.SqlServer/ModelCustomizer.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,18 @@ private static void SetupEntity(ModelBuilder modelBuilder)
3737

3838
public void Customize(ModelBuilder modelBuilder, DbContext context)
3939
{
40+
SetupEntity<byte>(modelBuilder);
41+
SetupEntity<short>(modelBuilder);
4042
SetupEntity<int>(modelBuilder);
4143
SetupEntity<long>(modelBuilder);
4244
SetupEntity<decimal>(modelBuilder);
45+
SetupEntity<float>(modelBuilder);
4346
SetupEntity<double>(modelBuilder);
4447
SetupEntity<DateTime>(modelBuilder);
4548
SetupEntity<DateTimeOffset>(modelBuilder);
46-
SetupEntity<string>(modelBuilder);
4749
SetupEntity<Guid>(modelBuilder);
50+
SetupEntity<char>(modelBuilder);
51+
SetupEntity<string>(modelBuilder);
4852

4953
SetupEntity(modelBuilder);
5054

0 commit comments

Comments
 (0)