Skip to content
This repository was archived by the owner on Feb 1, 2025. It is now read-only.

Commit 904f047

Browse files
authored
Merge pull request #70 from linq2db/master
Release 3.7.0
2 parents 185489d + 064c8f0 commit 904f047

24 files changed

+723
-9
lines changed

Build/linq2db.Default.props

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project>
22
<PropertyGroup>
3-
<Version>3.6.0</Version>
3+
<Version>3.7.0</Version>
44

55
<Description>Allows to execute Linq to DB (linq2db) queries in Entity Framework Core DbContext.</Description>
66
<Title>Linq to DB (linq2db) extensions for Entity Framework Core</Title>

NuGet/linq2db.EntityFrameworkCore.nuspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
<dependencies>
1717
<group targetFramework=".NETStandard2.0">
1818
<dependency id="Microsoft.EntityFrameworkCore.Relational" version="3.1.3" />
19-
<dependency id="linq2db" version="3.1.2" />
19+
<dependency id="linq2db" version="3.1.5" />
2020
</group>
2121
</dependencies>
2222
</metadata>

Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFTools.cs

+24-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using System.Data.Common;
44
using System.Linq;
55
using System.Linq.Expressions;
6-
6+
using System.Reflection;
77
using Microsoft.EntityFrameworkCore;
88
using Microsoft.EntityFrameworkCore.Infrastructure;
99
using Microsoft.EntityFrameworkCore.Metadata;
@@ -390,6 +390,9 @@ public static DataConnection CreateLinq2DbConnectionDetached([JetBrains.Annotati
390390
return dc;
391391
}
392392

393+
394+
static ConcurrentDictionary<Type, Func<DbConnection, string>> _connectionStringExtractors = new ConcurrentDictionary<Type, Func<DbConnection, string>>();
395+
393396
/// <summary>
394397
/// Extracts database connection information from EF.Core provider data.
395398
/// </summary>
@@ -398,7 +401,26 @@ public static DataConnection CreateLinq2DbConnectionDetached([JetBrains.Annotati
398401
public static EFConnectionInfo GetConnectionInfo(EFProviderInfo info)
399402
{
400403
var connection = info.Connection;
401-
var connectionString = connection?.ConnectionString;
404+
string connectionString = null;
405+
if (connection != null)
406+
{
407+
var connectionStringFunc = _connectionStringExtractors.GetOrAdd(connection.GetType(), t =>
408+
{
409+
// NpgSQL workaround
410+
var originalProp = t.GetProperty("OriginalConnectionString", BindingFlags.Instance | BindingFlags.NonPublic);
411+
412+
if (originalProp == null)
413+
return c => c.ConnectionString;
414+
415+
var parameter = Expression.Parameter(typeof(DbConnection), "c");
416+
var lambda = Expression.Lambda<Func<DbConnection, string>>(
417+
Expression.MakeMemberAccess(Expression.Convert(parameter, t), originalProp), parameter);
418+
419+
return lambda.Compile();
420+
});
421+
422+
connectionString = connectionStringFunc(connection);
423+
}
402424

403425
if (connection != null && connectionString != null)
404426
return new EFConnectionInfo { Connection = connection, ConnectionString = connectionString };

Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFToolsDataConnection.cs

+28-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Data;
3+
using System.Linq;
34
using System.Linq.Expressions;
45

56
using Microsoft.EntityFrameworkCore.Metadata;
@@ -100,7 +101,33 @@ private void OnEntityCreatedHandler(EntityCreatedEventArgs args)
100101
if (_stateManager == null)
101102
_stateManager = Context.GetService<IStateManager>();
102103

103-
var entry = _stateManager.StartTrackingFromQuery(_lastEntityType, args.Entity, ValueBuffer.Empty);
104+
105+
// It is a real pain to register entity in change tracker
106+
//
107+
InternalEntityEntry entry = null;
108+
109+
foreach (var key in _lastEntityType.GetKeys())
110+
{
111+
//TODO: Find faster way
112+
var keyArray = key.Properties.Where(p => p.PropertyInfo != null || p.FieldInfo != null).Select(p =>
113+
p.PropertyInfo != null
114+
? p.PropertyInfo.GetValue(args.Entity)
115+
: p.FieldInfo.GetValue(args.Entity)).ToArray();
116+
117+
if (keyArray.Length == key.Properties.Count)
118+
{
119+
entry = _stateManager.TryGetEntry(key, keyArray);
120+
121+
if (entry != null)
122+
break;
123+
}
124+
}
125+
126+
if (entry == null)
127+
{
128+
entry = _stateManager.StartTrackingFromQuery(_lastEntityType, args.Entity, ValueBuffer.Empty);
129+
}
130+
104131
args.Entity = entry.Entity;
105132
}
106133

Source/LinqToDB.EntityFrameworkCore/linq2db.EntityFrameworkCore.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
</PropertyGroup>
1010

1111
<ItemGroup>
12-
<PackageReference Include="linq2db" Version="3.1.2" />
12+
<PackageReference Include="linq2db" Version="3.1.5" />
1313
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="3.1.3" />
1414
</ItemGroup>
1515

Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
</PropertyGroup>
66

77
<ItemGroup>
8+
<PackageReference Include="FluentAssertions" Version="6.0.0-alpha0001" />
89
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.3.0" />
910
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="3.1.3" />
1011
<PackageReference Include="NUnit3TestAdapter" Version="3.16.1">

Tests/LinqToDB.EntityFrameworkCore.PostgreSQL.Tests/NpgSqlTests.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
using Microsoft.EntityFrameworkCore;
77
using NUnit.Framework;
88

9-
namespace LinqToDB.EntityFrameworkCore.Tests
9+
namespace LinqToDB.EntityFrameworkCore.PostgreSQL.Tests
1010
{
1111
public class NpgSqlTests : TestsBase
1212
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
4+
namespace LinqToDB.EntityFrameworkCore.PostgreSQL.Tests.SampleTests
5+
{
6+
public class Unit
7+
{
8+
9+
}
10+
11+
public static class ExceptionExtensions
12+
{
13+
public static Unit Throw(this Exception e) => throw e;
14+
}
15+
16+
public static class AAA
17+
{
18+
public static ArrangeResult<T, Unit> Arrange<T>(this T @object, Action<T> action)
19+
{
20+
action(@object);
21+
return new ArrangeResult<T, Unit>(@object, default);
22+
}
23+
24+
public static ArrangeResult<T, Unit> Arrange<T>(T @object)
25+
=> new ArrangeResult<T, Unit>(@object, default);
26+
27+
public static ArrangeResult<T, TMock> Arrange<T, TMock>(this TMock mock, Func<TMock, T> @object)
28+
=> new ArrangeResult<T, TMock>(@object(mock), mock);
29+
30+
public static ActResult<T, TMock> Act<T, TMock>(this ArrangeResult<T, TMock> arrange, Action<T> act)
31+
{
32+
try
33+
{
34+
act(arrange.Object);
35+
return new ActResult<T, TMock>(arrange.Object, arrange.Mock, default);
36+
}
37+
catch (Exception e)
38+
{
39+
return new ActResult<T, TMock>(arrange.Object, arrange.Mock, e);
40+
}
41+
}
42+
43+
public static ActResult<TResult, TMock> Act<T, TMock, TResult>(this ArrangeResult<T, TMock> arrange, Func<T, TResult> act)
44+
{
45+
try
46+
{
47+
return new ActResult<TResult, TMock>(act(arrange.Object), arrange.Mock, default);
48+
}
49+
catch (Exception e)
50+
{
51+
return new ActResult<TResult, TMock>(default, arrange.Mock, e);
52+
}
53+
}
54+
55+
public static void Assert<T, TMock>(this ActResult<T, TMock> act, Action<T> assert)
56+
{
57+
act.Exception?.Throw();
58+
assert(act.Object);
59+
}
60+
61+
public static void Assert<T, TMock>(this ActResult<T, TMock> act, Action<T, TMock> assert)
62+
{
63+
act.Exception?.Throw();
64+
assert(act.Object, act.Mock);
65+
}
66+
67+
public static Task<ArrangeResult<T, Unit>> ArrangeAsync<T>(T @object)
68+
=> Task.FromResult(new ArrangeResult<T, Unit>(@object, default));
69+
70+
public static async Task<ActResult<TResult, TMock>> Act<T, TMock, TResult>(this Task<ArrangeResult<T, TMock>> arrange, Func<T, Task<TResult>> act)
71+
{
72+
var a = await arrange;
73+
try
74+
{
75+
return new ActResult<TResult, TMock>(await act(a.Object), a.Mock, default);
76+
}
77+
catch (Exception e)
78+
{
79+
return new ActResult<TResult, TMock>(default, a.Mock, e);
80+
}
81+
}
82+
83+
public static async Task Assert<T, TMock>(this Task<ActResult<T, TMock>> act, Func<T, Task> assert)
84+
{
85+
var result = await act;
86+
await assert(result.Object);
87+
}
88+
89+
public readonly struct ArrangeResult<T, TMock>
90+
{
91+
internal ArrangeResult(T @object, TMock mock) => (Object, Mock) = (@object, mock);
92+
internal T Object { get; }
93+
internal TMock Mock { get; }
94+
}
95+
96+
public readonly struct ActResult<T, TMock>
97+
{
98+
internal ActResult(T @object, TMock mock, Exception exception)
99+
=> (Object, Mock, Exception) = (@object, mock, exception);
100+
internal T Object { get; }
101+
internal TMock Mock { get; }
102+
internal Exception Exception { get; }
103+
}
104+
}
105+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
namespace LinqToDB.EntityFrameworkCore.PostgreSQL.Tests.SampleTests
2+
{
3+
public sealed class Child : IHasWriteableId<Child, long>
4+
{
5+
public Id<Child, long> Id { get; set; }
6+
public Id<Entity, long> ParentId { get; set; }
7+
public string Name { get; set; }
8+
public Entity Parent { get; set; }
9+
}
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace LinqToDB.EntityFrameworkCore.PostgreSQL.Tests.SampleTests
2+
{
3+
public static class DataContextExtensions
4+
{
5+
public static Id<T, long> Insert<T>(this IDataContext context, T item)
6+
where T : IHasWriteableId<T, long>
7+
{
8+
item.Id = context.InsertWithInt64Identity(item).AsId<T>();
9+
return item.Id;
10+
}
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System.Collections.Generic;
2+
3+
namespace LinqToDB.EntityFrameworkCore.PostgreSQL.Tests.SampleTests
4+
{
5+
public sealed class Detail : IHasWriteableId<Detail, long>
6+
{
7+
public Id<Detail, long> Id { get; set; }
8+
public Id<Entity, long> MasterId { get; set; }
9+
public string Name { get; set; }
10+
public Entity Master { get; set; }
11+
public IEnumerable<SubDetail> Details { get; set; }
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
using System.Collections.Generic;
2+
3+
namespace LinqToDB.EntityFrameworkCore.PostgreSQL.Tests.SampleTests
4+
{
5+
public sealed class Entity : IHasWriteableId<Entity, long>
6+
{
7+
public Id<Entity, long> Id { get; set; }
8+
public string Name { get; set; }
9+
10+
public IEnumerable<Detail> Details { get; set; }
11+
public IEnumerable<Child> Children { get; set; }
12+
public IEnumerable<Entity2Item> Items { get; set; }
13+
}
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
namespace LinqToDB.EntityFrameworkCore.PostgreSQL.Tests.SampleTests
2+
{
3+
public sealed class Entity2Item
4+
{
5+
public Id<Entity, long> EntityId { get; set; }
6+
public Entity Entity { get; set; }
7+
public Id<Item, long> ItemId { get; set; }
8+
9+
public Entity2Item()
10+
{
11+
}
12+
13+
public Item Item { get; set; }
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace LinqToDB.EntityFrameworkCore.PostgreSQL.Tests.SampleTests
2+
{
3+
public interface IHasId<T, TId> where T: IHasId<T, TId>
4+
{
5+
Id<T, TId> Id { get; }
6+
}
7+
8+
public interface IHasWriteableId<T, TId> : IHasId<T, TId> where T: IHasWriteableId<T, TId>
9+
{
10+
new Id<T, TId> Id { get; set; }
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System.Collections.Generic;
2+
3+
namespace LinqToDB.EntityFrameworkCore.PostgreSQL.Tests.SampleTests
4+
{
5+
public static class Id
6+
{
7+
public static Id<T, long> AsId<T>(this long id) where T : IHasId<T, long> => id.AsId<T, long>();
8+
9+
public static Id<T, TId> AsId<T, TId>(this TId id) where T : IHasId<T, TId>
10+
=> new Id<T, TId>(id);
11+
}
12+
13+
public readonly struct Id<T, TId> where T : IHasId<T, TId>
14+
{
15+
internal Id(TId value) => Value = value;
16+
TId Value { get; }
17+
18+
public static implicit operator TId (in Id<T, TId> id) => id.Value;
19+
public static bool operator == (Id<T, TId> left, Id<T, TId> right)
20+
=> EqualityComparer<TId>.Default.Equals(left.Value, right.Value);
21+
public static bool operator != (Id<T, TId> left, Id<T, TId> right) => !(left == right);
22+
23+
public override string ToString() => $"{typeof(T).Name}({Value})";
24+
public bool Equals(Id<T, TId> other) => EqualityComparer<TId>.Default.Equals(Value, other.Value);
25+
public override bool Equals(object obj) => obj is Id<T, TId> other && Equals(other);
26+
public override int GetHashCode() => EqualityComparer<TId>.Default.GetHashCode(Value);
27+
}
28+
}

0 commit comments

Comments
 (0)