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

Commit d3b0e38

Browse files
committed
Ported changes from version 5. Added Inheritance Mapping support.
1 parent 97481ed commit d3b0e38

File tree

9 files changed

+221
-27
lines changed

9 files changed

+221
-27
lines changed

Directory.Packages.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<PackageVersion Include="NUnit" Version="3.13.2" />
66
<PackageVersion Include="FluentAssertions" Version="5.10.3" />
77

8-
<PackageVersion Include="linq2db" Version="3.4.3" />
8+
<PackageVersion Include="linq2db" Version="3.4.4" />
99
<PackageVersion Include="linq2db.Tools" Version="3.4.3" />
1010

1111
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.0.0" />

Source/LinqToDB.EntityFrameworkCore/EFCoreMetadataReader.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,24 @@ public T[] GetAttributes<T>(Type type, bool inherit = true) where T : Attribute
108108
if (tableAttribute != null)
109109
return new[] { (T)(Attribute)new TableAttribute(tableAttribute.Name) { Schema = tableAttribute.Schema } };
110110
}
111+
else if (_model != null && typeof(T) == typeof(InheritanceMappingAttribute))
112+
{
113+
if (et != null)
114+
{
115+
var derivedEntities = _model.GetEntityTypes().Where(e => e.BaseType == et && e.GetDiscriminatorValue() != null).ToList();
116+
117+
return
118+
derivedEntities.Select(e =>
119+
(T)(Attribute)new InheritanceMappingAttribute
120+
{
121+
Type = e.ClrType,
122+
Code = e.GetDiscriminatorValue()
123+
}
124+
)
125+
.ToArray();
126+
}
127+
128+
}
111129

112130
return Array.Empty<T>();
113131
}
@@ -186,11 +204,13 @@ public T[] GetAttributes<T>(Type type, MemberInfo memberInfo, bool inherit = tru
186204

187205
if (prop != null)
188206
{
207+
var discriminator = et.GetDiscriminatorProperty();
208+
189209
var isPrimaryKey = prop.IsPrimaryKey();
190210
var primaryKeyOrder = 0;
191211
if (isPrimaryKey)
192212
{
193-
var pk = prop.FindContainingPrimaryKey();
213+
var pk = prop.FindContainingPrimaryKey()!;
194214
primaryKeyOrder = pk.Properties.Select((p, i) => new { p, index = i })
195215
.FirstOrDefault(v => CompareProperty(v.p, memberInfo))?.index ?? 0;
196216
}
@@ -248,6 +268,7 @@ public T[] GetAttributes<T>(Type type, MemberInfo memberInfo, bool inherit = tru
248268
IsPrimaryKey = isPrimaryKey,
249269
PrimaryKeyOrder = primaryKeyOrder,
250270
IsIdentity = isIdentity,
271+
IsDiscriminator = discriminator == prop
251272
}
252273
};
253274
}

Source/LinqToDB.EntityFrameworkCore/Internal/EFCoreExpressionAttribute.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,13 @@ public override ISqlExpression GetExpression(
3232
{
3333
var mc = (MethodCallExpression) expression;
3434
if (!mc.Method.IsStatic)
35-
knownExpressions.Add(mc.Object);
35+
knownExpressions.Add(mc.Object!);
3636
knownExpressions.AddRange(mc.Arguments);
3737
}
3838
else
3939
{
4040
var me = (MemberExpression) expression;
41-
knownExpressions.Add(me.Expression);
41+
knownExpressions.Add(me.Expression!);
4242
}
4343

4444
var pams = new List<ISqlExpression?>(knownExpressions.Select(_ => (ISqlExpression?) null));

Source/LinqToDB.EntityFrameworkCore/Internal/LinqToDBForEFQueryProvider.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
6767
/// </summary>
6868
/// <param name="expression">Query expression.</param>
6969
/// <returns>Query result.</returns>
70-
public object Execute(Expression expression)
70+
public object? Execute(Expression expression)
7171
{
7272
return QueryProvider.Execute(expression);
7373
}
@@ -109,7 +109,7 @@ TResult IAsyncQueryProvider.ExecuteAsync<TResult>(Expression expression, Cancell
109109
{
110110
var item = typeof(TResult).GetGenericArguments()[0];
111111
var method = _executeAsyncMethodInfo.MakeGenericMethod(item);
112-
return (TResult) method.Invoke(QueryProvider, new object[] { expression, cancellationToken });
112+
return (TResult) method.Invoke(QueryProvider, new object[] { expression, cancellationToken })!;
113113
}
114114

115115
/// <summary>
@@ -167,7 +167,7 @@ public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToke
167167
/// Returns generated SQL for specific LINQ query.
168168
/// </summary>
169169
/// <returns>Generated SQL.</returns>
170-
public override string ToString()
170+
public override string? ToString()
171171
{
172172
return QueryProvider.ToString();
173173
}

Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFExtensions.Async.EF.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ public static Task<Dictionary<TKey, TSource>> ToDictionaryAsyncEF<TSource, TKey>
3939
this IQueryable<TSource> source,
4040
Func<TSource, TKey> keySelector,
4141
CancellationToken cancellationToken = default)
42+
where TKey: notnull
4243
=> EntityFrameworkQueryableExtensions.ToDictionaryAsync(source, keySelector, cancellationToken);
4344

4445
/// <inheritdoc cref="EntityFrameworkQueryableExtensions.ToDictionaryAsync{TSource, TKey, TElement}(IQueryable{TSource}, Func{TSource, TKey}, Func{TSource, TElement}, CancellationToken)"/>
@@ -47,6 +48,7 @@ public static Task<Dictionary<TKey,TElement>> ToDictionaryAsyncEF<TSource,TKey,T
4748
Func<TSource,TKey> keySelector,
4849
Func<TSource,TElement> elementSelector,
4950
CancellationToken cancellationToken = default)
51+
where TKey : notnull
5052
=> EntityFrameworkQueryableExtensions.ToDictionaryAsync(source, keySelector, elementSelector, cancellationToken);
5153

5254
/// <inheritdoc cref="EntityFrameworkQueryableExtensions.ToDictionaryAsync{TSource, TKey, TElement}(IQueryable{TSource}, Func{TSource, TKey}, Func{TSource, TElement}, IEqualityComparer{TKey}, CancellationToken)"/>
@@ -56,6 +58,7 @@ public static Task<Dictionary<TKey,TElement>> ToDictionaryAsyncEF<TSource,TKey,T
5658
Func<TSource,TElement> elementSelector,
5759
IEqualityComparer<TKey> comparer,
5860
CancellationToken cancellationToken = default)
61+
where TKey : notnull
5962
=> EntityFrameworkQueryableExtensions.ToDictionaryAsync(source, keySelector, elementSelector, comparer, cancellationToken);
6063

6164
/// <inheritdoc cref="EntityFrameworkQueryableExtensions.FirstAsync{TSource}(IQueryable{TSource}, CancellationToken)"/>

Source/LinqToDB.EntityFrameworkCore/LinqToDBForEFToolsDataConnection.cs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,10 @@ public class LinqToDBForEFToolsDataConnection : DataConnection, IExpressionPrepr
5656
/// <param name="model">EF.Core data model.</param>
5757
/// <param name="transformFunc">Expression converter.</param>
5858
public LinqToDBForEFToolsDataConnection(
59-
DbContext? context,
60-
IDataProvider dataProvider,
61-
string connectionString,
62-
IModel? model,
59+
DbContext? context,
60+
[NotNull] IDataProvider dataProvider,
61+
[NotNull] string connectionString,
62+
IModel? model,
6363
Func<Expression, IDataContext, DbContext?, IModel?, Expression>? transformFunc) : base(dataProvider, connectionString)
6464
{
6565
Context = context;
@@ -79,10 +79,10 @@ public LinqToDBForEFToolsDataConnection(
7979
/// <param name="model">EF.Core data model.</param>
8080
/// <param name="transformFunc">Expression converter.</param>
8181
public LinqToDBForEFToolsDataConnection(
82-
DbContext? context,
83-
IDataProvider dataProvider,
84-
IDbTransaction transaction,
85-
IModel? model,
82+
DbContext? context,
83+
[NotNull] IDataProvider dataProvider,
84+
[NotNull] IDbTransaction transaction,
85+
IModel? model,
8686
Func<Expression, IDataContext, DbContext?, IModel?, Expression>? transformFunc
8787
) : base(dataProvider, transaction)
8888
{
@@ -103,10 +103,10 @@ public LinqToDBForEFToolsDataConnection(
103103
/// <param name="model">EF.Core data model.</param>
104104
/// <param name="transformFunc">Expression converter.</param>
105105
public LinqToDBForEFToolsDataConnection(
106-
DbContext? context,
107-
IDataProvider dataProvider,
108-
IDbConnection connection,
109-
IModel? model,
106+
DbContext? context,
107+
[NotNull] IDataProvider dataProvider,
108+
[NotNull] IDbConnection connection,
109+
IModel? model,
110110
Func<Expression, IDataContext, DbContext?, IModel?, Expression>? transformFunc) : base(dataProvider, connection)
111111
{
112112
Context = context;

Tests/LinqToDB.EntityFrameworkCore.BaseTests/ForMappingTestsBase.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public virtual void TestIdentityMapping()
2020
using var connection = context.CreateLinqToDbConnection();
2121

2222
var ed = connection.MappingSchema.GetEntityDescriptor(typeof(WithIdentity));
23-
var pk = ed.Columns.Where(c => c.IsPrimaryKey).Single();
23+
var pk = ed.Columns.Single(c => c.IsPrimaryKey);
2424

2525
pk.IsIdentity.Should().BeTrue();
2626
}
@@ -32,7 +32,7 @@ public virtual void TestNoIdentityMapping()
3232
using var connection = context.CreateLinqToDbConnection();
3333

3434
var ed = connection.MappingSchema.GetEntityDescriptor(typeof(NoIdentity));
35-
var pk = ed.Columns.Where(c => c.IsPrimaryKey).Single();
35+
var pk = ed.Columns.Single(c => c.IsPrimaryKey);
3636

3737
pk.IsIdentity.Should().BeFalse();
3838
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
using Microsoft.EntityFrameworkCore;
2+
3+
namespace LinqToDB.EntityFrameworkCore.SqlServer.Tests.Models.Inheritance
4+
{
5+
public abstract class BlogBase
6+
{
7+
public int Id { get; set; }
8+
public string BlogType { get; set; } = null!;
9+
}
10+
11+
public class Blog : BlogBase
12+
{
13+
public string Url { get; set; } = null!;
14+
}
15+
16+
public class RssBlog : BlogBase
17+
{
18+
public string Url { get; set; } = null!;
19+
}
20+
21+
public abstract class ShadowBlogBase
22+
{
23+
public int Id { get; set; }
24+
public string BlogType { get; set; } = null!;
25+
}
26+
27+
public class ShadowBlog : ShadowBlogBase
28+
{
29+
public string Url { get; set; } = null!;
30+
}
31+
32+
public class ShadowRssBlog : ShadowBlogBase
33+
{
34+
public string Url { get; set; } = null!;
35+
}
36+
37+
public class InheritanceContext : DbContext
38+
{
39+
public InheritanceContext(DbContextOptions options) : base(options)
40+
{
41+
42+
}
43+
44+
protected override void OnModelCreating(ModelBuilder modelBuilder)
45+
{
46+
modelBuilder.Entity<BlogBase>()
47+
.HasDiscriminator(b => b.BlogType)
48+
.HasValue<Blog>("blog_base")
49+
.HasValue<RssBlog>("blog_rss");
50+
51+
modelBuilder.Entity<BlogBase>()
52+
.Property(e => e.BlogType)
53+
.HasColumnName("BlogType")
54+
.HasMaxLength(200);
55+
56+
modelBuilder.Entity<Blog>()
57+
.Property(b => b.Url)
58+
.HasColumnName("Url");
59+
60+
modelBuilder.Entity<RssBlog>()
61+
.Property(b => b.Url)
62+
.HasColumnName("Url");
63+
64+
modelBuilder.Entity<BlogBase>().ToTable("Blogs");
65+
66+
/////
67+
68+
modelBuilder.Entity<ShadowBlogBase>()
69+
.HasDiscriminator()
70+
.HasValue<ShadowBlog>("blog_base")
71+
.HasValue<ShadowRssBlog>("blog_rss");
72+
73+
modelBuilder.Entity<ShadowBlogBase>()
74+
.Property(e => e.BlogType)
75+
.HasColumnName("BlogType")
76+
.HasMaxLength(200);
77+
78+
modelBuilder.Entity<ShadowBlog>()
79+
.Property(b => b.Url)
80+
.HasColumnName("Url");
81+
82+
modelBuilder.Entity<ShadowRssBlog>()
83+
.Property(b => b.Url)
84+
.HasColumnName("Url");
85+
86+
modelBuilder.Entity<ShadowBlogBase>().ToTable("ShadowBlogs");
87+
}
88+
89+
public DbSet<BlogBase> Blogs { get; set; } = null!;
90+
public DbSet<ShadowBlogBase> ShadowBlogs { get; set; } = null!;
91+
}
92+
93+
}

0 commit comments

Comments
 (0)