Skip to content

Commit d27ca43

Browse files
committed
tests addded
1 parent 0a07733 commit d27ca43

File tree

5 files changed

+140
-43
lines changed

5 files changed

+140
-43
lines changed

src/EFCore.PostgresExtensions/Extensions/QueryableExtensions.cs

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -22,46 +22,5 @@ public static IQueryable<T> ForUpdate<T>(this IQueryable<T> query,
2222

2323
return query.AsQueryable();
2424
}
25-
26-
27-
// public static Task<T> FirstOrDefaultByBytesAsync<T>(
28-
// this IQueryable<T> query,
29-
// Expression<Func<T, byte[]>> byteArrayProperty,
30-
// byte[] searchBytes,
31-
// int numberOfBytes,
32-
// CancellationToken cancellationToken = default) where T : class
33-
// {
34-
// if (searchBytes == null || searchBytes.Length < numberOfBytes)
35-
// {
36-
// throw new ArgumentException($"Input array must be at least {numberOfBytes} bytes long.");
37-
// }
38-
//
39-
// var firstBytes = searchBytes.Take(numberOfBytes).ToArray();
40-
//
41-
// // Retrieve the DbContext from the IQueryable
42-
// var context = query.GetDbContext();
43-
//
44-
// // Get the table name from the DbContext model
45-
// var entityType = context.Model.FindEntityType(typeof(T));
46-
// var tableName = entityType.GetTableName();
47-
//
48-
// // Construct the SQL query
49-
// var queryText = $@"
50-
// SELECT * FROM ""{tableName}""
51-
// WHERE SUBSTRING(""Data"" FROM 1 FOR {numberOfBytes}) = @p0
52-
// LIMIT 1";
53-
//
54-
// // Apply the byte array comparison using FromSqlRaw
55-
// return query.FromSql(queryText, firstBytes).FirstOrDefaultAsync(cancellationToken);
56-
// }
57-
//
58-
// private static DbContext GetDbContext<T>(this IQueryable<T> query) where T : class
59-
// {
60-
// var infrastructure = (IInfrastructure<IServiceProvider>)query;
61-
// var serviceProvider = infrastructure.Instance;
62-
// var currentContext = serviceProvider.GetService(typeof(ICurrentDbContext)) as ICurrentDbContext;
63-
// return currentContext!.Context;
64-
// }
65-
// }
6625
}
6726
}
Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
1-
using Microsoft.EntityFrameworkCore;
1+
using EFCoreQueryMagic.PostgresContext;
2+
using Microsoft.EntityFrameworkCore;
23
using PandaNuGet.Demo.Entities;
34

45
namespace PandaNuGet.Demo.Context;
56

6-
public class PostgresContext(DbContextOptions<PostgresContext> options) : DbContext(options)
7+
public class PostgresContext(DbContextOptions<PostgresContext> options) : PostgresDbContext(options)
78
{
89
public DbSet<UserEntity> Users { get; set; } = null!;
10+
}
11+
12+
public abstract class PostgresDbContext(DbContextOptions options) : DbContext(options)
13+
{
14+
[DbFunction("substr", IsBuiltIn = true)]
15+
public static byte[] substr(byte[] byteArray, int start, int length)
16+
{
17+
return byteArray.Skip(start).Take(length).ToArray();
18+
}
919
}

test/PandaNuGet.Demo/PandaNuGet.Demo.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
<PackageReference Include="Dapper" Version="2.1.35" />
1212
<PackageReference Include="EFCore.BulkExtensions.PostgreSql" Version="8.0.4" />
1313
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.6" />
14+
<PackageReference Include="Pandatech.Crypto" Version="2.3.2" />
15+
<PackageReference Include="Pandatech.EFCoreQueryMagic" Version="1.1.0" />
1416
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
1517
</ItemGroup>
1618

test/PandaNuGet.Demo/Program.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
builder.AddPostgresContext();
88
builder.Services.AddScoped<BulkInsertService>();
9+
builder.Services.AddScoped<GetByFirstBytesService>();
910

1011
builder.Services.AddEndpointsApiExplorer();
1112
builder.Services.AddSwaggerGen();
@@ -19,6 +20,18 @@
1920

2021
app.MapGet("ping", () => "pong");
2122

23+
app.MapGet("/get-user-new", async (GetByFirstBytesService service) =>
24+
{
25+
await service.GetByFirstBytes();
26+
return "OK";
27+
});
28+
29+
app.MapGet("/get-user-old", async (GetByFirstBytesService service) =>
30+
{
31+
await service.GetByFirstBytesDavit();
32+
return "OK";
33+
});
34+
2235
app.MapGet("/benchmark-sync/{minimumRows:int}", (BulkInsertService service, int minimumRows) =>
2336
{
2437
var results = new List<BulkBenchmarkResponse>
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
using System.Linq.Expressions;
2+
using Microsoft.EntityFrameworkCore;
3+
using PandaNuGet.Demo.Context;
4+
using PandaNuGet.Demo.Entities;
5+
using Pandatech.Crypto;
6+
using PostgresDbContext = PandaNuGet.Demo.Context.PostgresDbContext;
7+
8+
namespace PandaNuGet.Demo.Services;
9+
10+
public class GetByFirstBytesService(PostgresContext context)
11+
{
12+
private static byte[] DocumentNumber => Sha3.Hash("1234567890");
13+
14+
public async Task<int> SeedUser()
15+
{
16+
var user = new UserEntity();
17+
var documentNumber = DocumentNumber;
18+
var randomBytes = new byte[10];
19+
documentNumber = documentNumber.Concat(randomBytes).ToArray();
20+
user.Document = documentNumber;
21+
context.Users.Add(user);
22+
await context.SaveChangesAsync();
23+
return user.Id;
24+
}
25+
26+
public async Task GetByFirstBytes()
27+
{
28+
var userId = await SeedUser();
29+
30+
var userByDocument = await context
31+
.Users
32+
.WhereStartWithBytes(x => x.Document, 1, 64, DocumentNumber)
33+
.FirstOrDefaultAsync();
34+
35+
Console.WriteLine($"AAAAAA {userByDocument.Id}");
36+
37+
var user = await context.Users.FindAsync(userId);
38+
if (user != null)
39+
{
40+
context.Users.Remove(user);
41+
await context.SaveChangesAsync();
42+
}
43+
}
44+
45+
public async Task GetByFirstBytesDavit()
46+
{
47+
var userId = await SeedUser();
48+
49+
var userByDocument = await context
50+
.Users
51+
.Where(u => PostgresDbContext.substr(u.Document, 1, 64).SequenceEqual(DocumentNumber))
52+
.FirstOrDefaultAsync();
53+
54+
Console.WriteLine($"AAAAAA {userByDocument.Id}");
55+
56+
var user = await context.Users.FindAsync(userId);
57+
if (user != null)
58+
{
59+
context.Users.Remove(user);
60+
await context.SaveChangesAsync();
61+
}
62+
}
63+
}
64+
65+
public static class QueryableExtensions
66+
{
67+
public static IQueryable<T> WhereStartWithBytes<T>(
68+
this IQueryable<T> source,
69+
Expression<Func<T, byte[]>> byteArraySelector,
70+
int start,
71+
int length,
72+
byte[] prefix) where T : class
73+
{
74+
// Parameter expression for the source element
75+
var parameter = Expression.Parameter(typeof(T), "x");
76+
77+
// Expression to get the byte array from the element
78+
var member = Expression.Invoke(byteArraySelector, parameter);
79+
80+
// MethodInfo for the substr method
81+
var methodInfo = typeof(PostgresDbContext).GetMethod(nameof(PostgresDbContext.substr), new[] { typeof(byte[]), typeof(int), typeof(int) });
82+
83+
// Call to substr method
84+
var call = Expression.Call(
85+
methodInfo,
86+
member,
87+
Expression.Constant(start),
88+
Expression.Constant(length));
89+
90+
// MethodInfo for the SequenceEqual method
91+
var sequenceEqualMethod = typeof(Enumerable).GetMethods()
92+
.First(m => m.Name == "SequenceEqual" && m.GetParameters().Length == 2)
93+
.MakeGenericMethod(typeof(byte));
94+
95+
// Call to SequenceEqual method
96+
var sequenceEqualCall = Expression.Call(
97+
sequenceEqualMethod,
98+
call,
99+
Expression.Constant(prefix));
100+
101+
// Lambda expression for the final predicate
102+
var lambda = Expression.Lambda<Func<T, bool>>(sequenceEqualCall, parameter);
103+
104+
// Apply the predicate to the source IQueryable with client-side evaluation
105+
return source.AsEnumerable().AsQueryable().Where(lambda);
106+
}
107+
}
108+
109+
110+
111+
112+
113+

0 commit comments

Comments
 (0)