Skip to content

Commit ab39675

Browse files
authored
fix: Fix issues 564 (#566)
* pref: Optimize exception information * fix: fix issues-564 * test: Add UnitTest * fix: Fix the problem that the creation time and the creator cannot be preset * test: Complementary unit tests * fix: Fix issues 564 * chore: Modify code smell * test: Fix unit test errors
1 parent 6cd8676 commit ab39675

File tree

9 files changed

+297
-57
lines changed

9 files changed

+297
-57
lines changed

src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore.Tests/DbContextTest.cs

+153-41
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,7 @@ public void TestDbContext4()
5252
[TestMethod]
5353
public void TestDbContext5()
5454
{
55-
Services.Configure<AppConfigOptions>(options =>
56-
{
57-
options.DbConnectionString = MemoryConnectionString;
58-
});
55+
Services.Configure<AppConfigOptions>(options => { options.DbConnectionString = MemoryConnectionString; });
5956
var dbContext = CreateDbContext<CustomDbContext5>(null);
6057
var student = GenerateStudent();
6158
dbContext.Set<Student>().Add(student);
@@ -79,10 +76,7 @@ public void TestDbContext6()
7976
[TestMethod]
8077
public void TestDbContext7()
8178
{
82-
Services.Configure<AppConfigOptions>(options =>
83-
{
84-
options.DbConnectionString = MemoryConnectionString;
85-
});
79+
Services.Configure<AppConfigOptions>(options => { options.DbConnectionString = MemoryConnectionString; });
8680
var dbContext = CreateDbContext<CustomDbContext7>(null);
8781
var student = GenerateStudent();
8882
dbContext.Set<Student>().Add(student);
@@ -128,9 +122,9 @@ private static Student GenerateStudent()
128122

129123
private static Student VerifyStudent(DbContext dbContext, Guid id, bool isTracking = false)
130124
{
131-
var student = isTracking ?
132-
dbContext.Set<Student>().AsTracking().Include(s => s.Hobbies).FirstOrDefault(s => s.Id == id) :
133-
dbContext.Set<Student>().AsNoTracking().Include(s => s.Hobbies).FirstOrDefault(s => s.Id == id);
125+
var student = isTracking
126+
? dbContext.Set<Student>().AsTracking().Include(s => s.Hobbies).FirstOrDefault(s => s.Id == id)
127+
: dbContext.Set<Student>().AsNoTracking().Include(s => s.Hobbies).FirstOrDefault(s => s.Id == id);
134128
Assert.IsNotNull(student);
135129
return student;
136130
}
@@ -202,6 +196,83 @@ public async Task TestSoftDeleteAsync()
202196
}
203197
}
204198

199+
[TestMethod]
200+
public async Task TestModifierBySoftDeleteAsync()
201+
{
202+
var creatorUserContext = new CustomUserContext("1");
203+
Services.Configure<AuditEntityOptions>(options => options.UserIdType = typeof(int));
204+
Services.AddSingleton<IUserContext>(creatorUserContext);
205+
206+
IServiceProvider serviceProvider = default!;
207+
var dbContext = await CreateDbContextAsync<CustomDbContext>(dbContextBuilder =>
208+
{
209+
dbContextBuilder.UseFilter();
210+
dbContextBuilder.UseInMemoryDatabase(MemoryConnectionString);
211+
}, sp => serviceProvider = sp);
212+
var goods = new Goods()
213+
{
214+
Name = "masa",
215+
Logs = new List<OperationLog>()
216+
{
217+
new("initialize"),
218+
new("initialize2"),
219+
}
220+
};
221+
await dbContext.Set<Goods>().AddAsync(goods);
222+
await dbContext.SaveChangesAsync();
223+
224+
var goodsCreate = await dbContext.Set<Goods>().Include(g => g.Logs).FirstOrDefaultAsync(g => g.Name == "masa");
225+
Assert.IsNotNull(goodsCreate);
226+
227+
var modifierByCrate = goodsCreate.Modifier;
228+
var modificationTimeByCreate = goodsCreate.ModificationTime;
229+
230+
Assert.AreEqual(2, goodsCreate.Logs.Count);
231+
var log1 = goods.Logs.FirstOrDefault(log => log.Name == "initialize");
232+
Assert.IsNotNull(log1);
233+
var log2 = goods.Logs.FirstOrDefault(log => log.Name == "initialize2");
234+
Assert.IsNotNull(log2);
235+
236+
creatorUserContext.SetUserId("2");
237+
238+
var inputModificationTime = DateTime.UtcNow.AddDays(-1);
239+
log1.SetDeleted(true, 3, inputModificationTime);
240+
goods.Logs.Clear();
241+
dbContext.Set<Goods>().Update(goods);
242+
await dbContext.SaveChangesAsync();
243+
244+
var goodsByUpdate = await dbContext.Set<Goods>().IgnoreQueryFilters().Include(g => g.Logs).FirstOrDefaultAsync();
245+
Assert.IsNotNull(goodsByUpdate);
246+
247+
Assert.AreEqual(2, goodsByUpdate.Logs.Count);
248+
var log1ByUpdate = goodsByUpdate.Logs.FirstOrDefault(log => log.Name == "initialize");
249+
Assert.IsNotNull(log1ByUpdate);
250+
251+
Assert.AreEqual(true, log1ByUpdate.IsDeleted);
252+
Assert.AreEqual(3, log1ByUpdate.Modifier);
253+
Assert.AreEqual(inputModificationTime, log1ByUpdate.ModificationTime);
254+
255+
var log1ByUpdate2 = goodsByUpdate.Logs.FirstOrDefault(log => log.Name == "initialize2");
256+
Assert.IsNotNull(log1ByUpdate2);
257+
258+
Assert.AreEqual(true, log1ByUpdate2.IsDeleted);
259+
Assert.AreEqual(2, log1ByUpdate2.Modifier);
260+
Assert.AreNotEqual(modificationTimeByCreate, log1ByUpdate.ModificationTime);
261+
262+
var goodsByUpdateAgain = await dbContext.Set<Goods>().AsTracking().IgnoreQueryFilters().Include(g => g.Logs).FirstOrDefaultAsync();
263+
Assert.IsNotNull(goodsByUpdateAgain);
264+
var modificationTime = DateTime.Parse("2022-01-01 00:00:00");
265+
goodsByUpdateAgain.SetDeleted(true, 10, modificationTime);
266+
dbContext.Set<Goods>().Remove(goodsByUpdateAgain);
267+
await dbContext.SaveChangesAsync();
268+
269+
var goodsByDelete = await dbContext.Set<Goods>().AsNoTracking().IgnoreQueryFilters().FirstOrDefaultAsync(g => g.Name == "masa");
270+
Assert.IsNotNull(goodsByDelete);
271+
272+
Assert.AreEqual(10, goodsByDelete.Modifier);
273+
Assert.AreEqual(modificationTime, goodsByDelete.ModificationTime);
274+
}
275+
205276
#endregion
206277

207278
#region Test ConnectionString
@@ -215,10 +286,8 @@ public async Task TestModifyConnectionString()
215286
Services.AddSingleton<IConfiguration>(configuration);
216287

217288
IServiceProvider serviceProvider = default!;
218-
var dbContext = await CreateDbContextAsync<CustomDbContext>(optionsBuilder =>
219-
{
220-
optionsBuilder.UseSqlite();
221-
}, sp => serviceProvider = sp);
289+
var dbContext =
290+
await CreateDbContextAsync<CustomDbContext>(optionsBuilder => { optionsBuilder.UseSqlite(); }, sp => serviceProvider = sp);
222291

223292
var connectionStringProvider = serviceProvider.GetService<IConnectionStringProvider>();
224293
Assert.IsNotNull(connectionStringProvider);
@@ -269,31 +338,20 @@ public async Task TestUseConfigurationAndSpecify()
269338
.Build();
270339
Services.AddSingleton<IConfiguration>(configuration);
271340

272-
await CreateDbContextAsync<CustomDbContext>(optionsBuilder =>
273-
{
274-
optionsBuilder.UseSqlite();
275-
});
341+
await CreateDbContextAsync<CustomDbContext>(optionsBuilder => { optionsBuilder.UseSqlite(); });
276342
await Assert.ThrowsExceptionAsync<ArgumentException>(async () =>
277343
{
278-
await CreateDbContextAsync<CustomDbContext2>(optionsBuilder =>
279-
{
280-
optionsBuilder.UseSqlite(SqliteConnectionString);
281-
});
344+
await CreateDbContextAsync<CustomDbContext2>(optionsBuilder => { optionsBuilder.UseSqlite(SqliteConnectionString); });
282345
});
283346
}
284347

285348
[TestMethod]
286349
public async Task TestAddMultiDbContextAsync()
287350
{
288-
Services.AddMasaDbContext<CustomDbContext>(dbContextBuilder =>
289-
{
290-
dbContextBuilder.UseInMemoryDatabase(MemoryConnectionString);
291-
});
351+
Services.AddMasaDbContext<CustomDbContext>(dbContextBuilder => { dbContextBuilder.UseInMemoryDatabase(MemoryConnectionString); });
292352
IServiceProvider serviceProvider = default!;
293-
var customDbContext2 = await CreateDbContextAsync<CustomDbContext2>(dbContextBuilder =>
294-
{
295-
dbContextBuilder.UseInMemoryDatabase(MemoryConnectionString);
296-
}, sp => serviceProvider = sp);
353+
var customDbContext2 = await CreateDbContextAsync<CustomDbContext2>(
354+
dbContextBuilder => { dbContextBuilder.UseInMemoryDatabase(MemoryConnectionString); }, sp => serviceProvider = sp);
297355

298356
var customDbContext = serviceProvider.GetService<CustomDbContext>();
299357
Assert.IsNotNull(customDbContext);
@@ -352,8 +410,9 @@ await dbContext.Set<Goods>().AddAsync(new Goods()
352410
var creationTimeByCreate = goodsByCreate.CreationTime;
353411
var modifierByCreate = goodsByCreate.Modifier;
354412
var modificationTimeByCreate = goodsByCreate.ModificationTime;
355-
Assert.AreEqual(expectedCreator, goodsByCreate.Creator);
356-
Assert.AreEqual(expectedCreator, goodsByCreate.Modifier);
413+
414+
Assert.AreEqual(expectedCreator, creatorByCreate);
415+
Assert.AreEqual(expectedCreator, modifierByCreate);
357416

358417
customUserContext.SetUserId(modifier?.ToString());
359418
goodsByCreate.Name = "masa1";
@@ -453,17 +512,73 @@ await dbContext.Set<Goods2>().AddAsync(new Goods2()
453512
Assert.AreNotEqual(modificationTimeByUpdate, goodsByUpdate.ModificationTime);
454513
}
455514

515+
[DataRow(1, 2, 1, "2023-01-01 00:00:00", "2023-01-01 00:00:00", 3, 3, "2023-01-02 00:00:00", "2023-01-02 00:00:00")]
516+
[DataTestMethod]
517+
public async Task TestAddOrUpdateOrDeleteWhenUserIdIsIntAsyncBySpecifyUserIdAndTime(
518+
int inputCreator, int currentCreator, int expectedCreator,
519+
string inputCreationTimeStr, string expectedCreationTimeStr,
520+
int inputModifier, int expectedModifier,
521+
string inputModificationTimeStr, string expectedModificationTimeStr)
522+
{
523+
DateTime inputCreationTime = DateTime.Parse(inputCreationTimeStr), expectedCreationTime = DateTime.Parse(expectedCreationTimeStr);
524+
DateTime inputModificationTime = DateTime.Parse(inputModificationTimeStr),
525+
expectedModificationTime = DateTime.Parse(expectedModificationTimeStr);
526+
Services.Configure<AuditEntityOptions>(options => options.UserIdType = typeof(int));
527+
528+
var customUserContext = new CustomUserContext(currentCreator.ToString());
529+
Services.AddSingleton<IUserContext>(customUserContext);
530+
531+
var connectionString = MemoryConnectionString;
532+
var dbContext = await CreateDbContextAsync<CustomDbContext>(dbContextBuilder =>
533+
{
534+
dbContextBuilder
535+
.UseInMemoryDatabase(connectionString)
536+
.UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll)
537+
.UseFilter();
538+
});
539+
Assert.IsNotNull(dbContext);
540+
var goods = new Goods("masa", inputCreator, inputCreationTime, inputModifier, inputModificationTime);
541+
await dbContext.Set<Goods>().AddAsync(goods);
542+
await dbContext.SaveChangesAsync();
543+
544+
var goodsByCreate = await dbContext.Set<Goods>().FirstOrDefaultAsync();
545+
Assert.IsNotNull(goodsByCreate);
546+
var creatorByCreate = goodsByCreate.Creator;
547+
var creationTimeByCreate = goodsByCreate.CreationTime;
548+
var modifierByCreate = goodsByCreate.Modifier;
549+
var modificationTimeByCreate = goodsByCreate.ModificationTime;
550+
Assert.AreEqual(expectedCreator, creatorByCreate);
551+
Assert.AreEqual(expectedCreationTime, creationTimeByCreate);
552+
Assert.AreEqual(expectedModifier, modifierByCreate);
553+
Assert.AreEqual(expectedModificationTime, modificationTimeByCreate);
554+
555+
customUserContext.SetUserId((currentCreator + 5).ToString());
556+
goodsByCreate.UpdateName("masa1", inputCreator + 2, inputCreationTime.AddDays(1), inputModifier + 3,
557+
inputModificationTime.AddDays(2));
558+
dbContext.Set<Goods>().Update(goodsByCreate);
559+
await dbContext.SaveChangesAsync();
560+
561+
var goodsByUpdate = await dbContext.Set<Goods>().FirstOrDefaultAsync();
562+
Assert.IsNotNull(goodsByUpdate);
563+
var creatorByUpdate = goodsByUpdate.Creator;
564+
var creationTimeByUpdate = goodsByUpdate.CreationTime;
565+
var modifierByUpdate = goodsByUpdate.Modifier;
566+
var modificationTimeByUpdate = goodsByUpdate.ModificationTime;
567+
Assert.AreEqual(inputCreator + 2, creatorByUpdate);
568+
Assert.AreEqual(inputCreationTime.AddDays(1), creationTimeByUpdate);
569+
Assert.AreEqual(currentCreator + 5, modifierByUpdate);
570+
Assert.AreNotEqual(expectedModificationTime, modificationTimeByUpdate);
571+
Assert.AreNotEqual(inputModificationTime.AddDays(2), modificationTimeByUpdate);
572+
}
573+
456574
#endregion
457575

458576
#region Test Model Mapping
459577

460578
[TestMethod]
461579
public void TestCustomTableName()
462580
{
463-
var dbContext = CreateDbContext<CustomDbContext>(dbContext =>
464-
{
465-
dbContext.UseInMemoryDatabase(MemoryConnectionString);
466-
});
581+
var dbContext = CreateDbContext<CustomDbContext>(dbContext => { dbContext.UseInMemoryDatabase(MemoryConnectionString); });
467582
var entityTableName = dbContext.Model.FindEntityType(typeof(Student))?.GetTableName();
468583

469584
Assert.AreEqual("masa_students", entityTableName);
@@ -476,10 +591,7 @@ public void TestCustomTableName()
476591
[TestMethod]
477592
public void TestQueryTrackingBehaviorByDefault()
478593
{
479-
var dbContext = CreateDbContext<CustomDbContext>(dbContext =>
480-
{
481-
dbContext.UseInMemoryDatabase(MemoryConnectionString);
482-
});
594+
var dbContext = CreateDbContext<CustomDbContext>(dbContext => { dbContext.UseInMemoryDatabase(MemoryConnectionString); });
483595
Assert.AreEqual(QueryTrackingBehavior.NoTracking, dbContext.ChangeTracker.QueryTrackingBehavior);
484596
}
485597

src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore.Tests/Entities/Goods.cs

+37
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,43 @@ namespace Masa.Contrib.Data.EFCore.Tests.Entities;
66
public class Goods : FullAggregateRoot<Guid, int>
77
{
88
public string Name { get; set; }
9+
10+
public List<OperationLog> Logs { get; set; }
11+
12+
public Goods()
13+
{
14+
}
15+
16+
public Goods(string name, int creator, DateTime creationTime, int modifier, DateTime modificationTime) : this()
17+
{
18+
Name = name;
19+
Creator = creator;
20+
CreationTime = creationTime;
21+
Modifier = modifier;
22+
ModificationTime = modificationTime;
23+
}
24+
25+
public void UpdateName(string name, int creator, DateTime creationTime, int modifier, DateTime modificationTime)
26+
{
27+
Name = name;
28+
Creator = creator;
29+
CreationTime = creationTime;
30+
Modifier = modifier;
31+
ModificationTime = modificationTime;
32+
}
33+
34+
public void UpdateModifier(int modifier, DateTime modificationTime)
35+
{
36+
Modifier = modifier;
37+
ModificationTime = modificationTime;
38+
}
39+
40+
public void SetDeleted(bool isDeleted, int modifier, DateTime modificationTime)
41+
{
42+
IsDeleted = isDeleted;
43+
Modifier = modifier;
44+
ModificationTime = modificationTime;
45+
}
946
}
1047

1148
public class Goods2 : FullAggregateRoot<Guid, int?>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright (c) MASA Stack All rights reserved.
2+
// Licensed under the MIT License. See LICENSE.txt in the project root for license information.
3+
4+
namespace Masa.Contrib.Data.EFCore.Tests.Entities;
5+
6+
public class OperationLog : FullEntity<Guid, int>
7+
{
8+
public string Name { get; set; }
9+
10+
public Guid GoodsId { get; private set; } = default!;
11+
12+
public Goods Goods { get; private set; } = default!;
13+
14+
public OperationLog()
15+
{
16+
}
17+
18+
public OperationLog(string name) : this()
19+
{
20+
Name = name;
21+
}
22+
23+
public void SetDeleted(bool isDeleted, int modifier, DateTime modificationTime)
24+
{
25+
IsDeleted = isDeleted;
26+
Modifier = modifier;
27+
ModificationTime = modificationTime;
28+
}
29+
}

src/Contrib/Data/Orm/EFCore/Masa.Contrib.Data.EFCore.Tests/Infrastructure/EntityConfigurations/GoodsEntityTypeConfiguration.cs

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ public class GoodsEntityTypeConfiguration : IEntityTypeConfiguration<Goods>
1010
public void Configure(EntityTypeBuilder<Goods> builder)
1111
{
1212
builder.Property(s => s.Id).IsRequired();
13+
14+
builder
15+
.HasMany(u => u.Logs)
16+
.WithOne(ur => ur.Goods).HasForeignKey(ur => ur.GoodsId);
1317
}
1418
}
1519

0 commit comments

Comments
 (0)