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

Commit b7a8b04

Browse files
committed
Fix for #143. Changed Identity handling logic.
1 parent a23793f commit b7a8b04

File tree

7 files changed

+179
-10
lines changed

7 files changed

+179
-10
lines changed

Source/LinqToDB.EntityFrameworkCore/EFCoreMetadataReader.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,6 @@ public T[] GetAttributes<T>(Type type, MemberInfo memberInfo, bool inherit = tru
160160
var isIdentity = prop.GetAnnotations()
161161
.Any(a => a.Name.EndsWith(":ValueGenerationStrategy") && a.Value?.ToString() == "IdentityColumn");
162162

163-
if (!isIdentity && isPrimaryKey)
164-
{
165-
isIdentity = prop.ValueGenerated == ValueGenerated.OnAdd;
166-
}
167-
168163
var storeObjectId = GetStoreObjectIdentifier(et);
169164

170165
return new T[]{(T)(Attribute) new ColumnAttribute
@@ -175,6 +170,8 @@ public T[] GetAttributes<T>(Type type, MemberInfo memberInfo, bool inherit = tru
175170
DbType = prop.GetColumnType(),
176171
IsPrimaryKey = isPrimaryKey,
177172
PrimaryKeyOrder = primaryKeyOrder,
173+
SkipOnInsert = prop.ValueGenerated == ValueGenerated.OnAdd || prop.ValueGenerated == ValueGenerated.OnAddOrUpdate,
174+
SkipOnUpdate = prop.ValueGenerated == ValueGenerated.OnUpdate || prop.ValueGenerated == ValueGenerated.OnAddOrUpdate,
178175
IsIdentity = isIdentity,
179176
}};
180177
}

Tests/LinqToDB.EntityFrameworkCore.BaseTests/LinqToDB.EntityFrameworkCore.BaseTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
</ItemGroup>
1010

1111
<ItemGroup>
12+
<PackageReference Include="FluentAssertions" Version="5.10.3" />
1213
<PackageReference Include="linq2db.Tools" Version="3.1.6" />
1314
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="5.0.0" />
1415
<PackageReference Include="Microsoft.Extensions.Logging" Version="5.0.0" />

Tests/LinqToDB.EntityFrameworkCore.SQLite.Tests/LinqToDB.EntityFrameworkCore.SQLite.Tests.csproj

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,6 @@
1313
<ProjectReference Include="..\LinqToDB.EntityFrameworkCore.BaseTests\LinqToDB.EntityFrameworkCore.BaseTests.csproj" />
1414
</ItemGroup>
1515

16-
<ItemGroup>
17-
<Folder Include="Models\" />
18-
</ItemGroup>
19-
2016
<ItemGroup>
2117
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.3" />
2218
</ItemGroup>
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
using System;
2+
using System.Linq;
3+
using System.Threading.Tasks;
4+
using Microsoft.Data.Sqlite;
5+
using Microsoft.EntityFrameworkCore;
6+
using Microsoft.EntityFrameworkCore.Metadata;
7+
8+
namespace LinqToDB.EntityFrameworkCore.SQLite.Tests.Models.Identity
9+
{
10+
public sealed class EfCoreSqliteInMemoryDbFactory : IDisposable, IAsyncDisposable
11+
{
12+
private readonly string _connectionString;
13+
private SqliteConnection? _connection;
14+
15+
public EfCoreSqliteInMemoryDbFactory()
16+
{
17+
_connectionString = "DataSource=:memory:";
18+
}
19+
20+
public EfCoreSqliteInMemoryDbFactory(string connectionString)
21+
{
22+
_connectionString = connectionString;
23+
}
24+
25+
26+
public async ValueTask DisposeAsync()
27+
{
28+
await Task.Run(Dispose);
29+
}
30+
31+
public void Dispose()
32+
{
33+
Dispose(true);
34+
GC.SuppressFinalize(this);
35+
}
36+
37+
public T CreateDbContext<T>(bool addVersionGeneratorTrigger = true, Action<string> logger = null)
38+
where T : DbContext
39+
{
40+
_connection = new SqliteConnection(_connectionString);
41+
_connection.Open();
42+
43+
var optionsBuilder = new DbContextOptionsBuilder<DbContext>().UseSqlite(_connection);
44+
45+
if (logger != null)
46+
optionsBuilder.LogTo(logger);
47+
48+
var context = (T) Activator.CreateInstance(typeof(T), optionsBuilder.Options);
49+
50+
context?.Database.EnsureCreated();
51+
52+
if (!addVersionGeneratorTrigger)
53+
return context;
54+
55+
AddVersionTrigger(context);
56+
57+
return context;
58+
}
59+
60+
private void AddVersionTrigger<T>(T context) where T : DbContext
61+
{
62+
var tables = context.Model.GetEntityTypes();
63+
64+
foreach (var table in tables)
65+
{
66+
var props = table.GetProperties().Where(p =>
67+
p.ClrType == typeof(byte[]) && p.ValueGenerated == ValueGenerated.OnAddOrUpdate &&
68+
p.IsConcurrencyToken);
69+
70+
var tableName = table.GetTableName();
71+
72+
foreach (var field in props)
73+
{
74+
string[] sqlStrings =
75+
{
76+
$@"CREATE TRIGGER Set{tableName}_{field.Name}OnUpdate
77+
AFTER UPDATE ON {tableName}
78+
BEGIN
79+
UPDATE {tableName}
80+
SET {field.Name} = randomblob(8)
81+
WHERE rowid = NEW.rowid;
82+
END
83+
",
84+
$@"CREATE TRIGGER Set{tableName}_{field.Name}OnInsert
85+
AFTER INSERT ON {tableName}
86+
BEGIN
87+
UPDATE {tableName}
88+
SET {field.Name} = randomblob(8)
89+
WHERE rowid = NEW.rowid;
90+
END
91+
"
92+
};
93+
94+
foreach (var sql in sqlStrings)
95+
{
96+
using var command = _connection.CreateCommand();
97+
command.CommandText = sql;
98+
command.ExecuteNonQuery();
99+
}
100+
}
101+
}
102+
}
103+
104+
private void ReleaseUnmanagedResources()
105+
{
106+
_connection.Dispose();
107+
}
108+
109+
private void Dispose(bool disposing)
110+
{
111+
ReleaseUnmanagedResources();
112+
113+
if (disposing)
114+
{
115+
}
116+
}
117+
118+
~EfCoreSqliteInMemoryDbFactory()
119+
{
120+
Dispose(false);
121+
}
122+
}
123+
124+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using Microsoft.EntityFrameworkCore;
2+
3+
namespace LinqToDB.EntityFrameworkCore.SQLite.Tests.Models.Identity
4+
{
5+
public class IdentityDbContext : DbContext
6+
{
7+
public IdentityDbContext(DbContextOptions<DbContext> options) : base(options)
8+
{
9+
}
10+
11+
public DbSet<Person> People { get; set; } = null!;
12+
}
13+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System;
2+
3+
namespace LinqToDB.EntityFrameworkCore.SQLite.Tests.Models.Identity
4+
{
5+
public class Person
6+
{
7+
public Guid Id { get; set; }
8+
public string Name { get; set; } = null!;
9+
}
10+
}

Tests/LinqToDB.EntityFrameworkCore.SQLite.Tests/SQLiteTests.cs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1-
using System.Linq;
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using FluentAssertions;
24
using LinqToDB.Data;
35
using LinqToDB.EntityFrameworkCore.BaseTests;
46
using LinqToDB.EntityFrameworkCore.BaseTests.Models.Northwind;
7+
using LinqToDB.EntityFrameworkCore.SQLite.Tests.Models.Identity;
58
using LinqToDB.EntityFrameworkCore.SQLite.Tests.Models.Northwind;
69
using Microsoft.EntityFrameworkCore;
710
using NUnit.Framework;
@@ -53,5 +56,30 @@ public void TestIdentityMapping()
5356
}
5457

5558

59+
[Test]
60+
public void TestSqliteDbCreation()
61+
{
62+
var dbFactory = new EfCoreSqliteInMemoryDbFactory();
63+
64+
using var context = dbFactory.CreateDbContext<IdentityDbContext>();
65+
66+
context.AddRange(new List<Person>
67+
{
68+
new() {Name = "John Doe"},
69+
new() {Name = "Jane Doe"}
70+
});
71+
72+
context.SaveChanges();
73+
74+
var people = context.People.ToList();
75+
76+
var connection = context.CreateLinqToDbConnection();
77+
78+
var tempTable = connection.CreateTempTable(people, new BulkCopyOptions {KeepIdentity = true});
79+
80+
tempTable.ToList().Should().BeEquivalentTo(people);
81+
}
82+
83+
5684
}
5785
}

0 commit comments

Comments
 (0)