Last active
March 11, 2025 20:28
-
-
Save to11mtm/5fc04f7409a0d156c2fd36794617191e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using Linq2Db.EfCore.Samples.Tests.Microsoft.EntityFrameworkCore.Infrastructure; | |
using LinqToDB.Data; | |
using LinqToDB.EntityFrameworkCore; | |
using LinqToDB.Mapping; | |
using Microsoft.Data.Sqlite; | |
using Microsoft.EntityFrameworkCore; | |
using Microsoft.EntityFrameworkCore.ChangeTracking; | |
using Microsoft.EntityFrameworkCore.Diagnostics; | |
using Microsoft.EntityFrameworkCore.Infrastructure; | |
using Microsoft.EntityFrameworkCore.Internal; | |
using Microsoft.EntityFrameworkCore.Metadata; | |
using Microsoft.EntityFrameworkCore.ValueGeneration; | |
using Microsoft.Extensions.Logging; | |
using Xunit; | |
using Xunit.Abstractions; | |
namespace Linq2Db.EfCore.Samples.Tests | |
{ | |
public class XunitLogger : ILogger, IDisposable | |
{ | |
private ITestOutputHelper _output; | |
public XunitLogger(ITestOutputHelper output) | |
{ | |
_output = output; | |
} | |
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) | |
{ | |
_output?.WriteLine(state.ToString()); | |
} | |
public bool IsEnabled(LogLevel logLevel) | |
{ | |
return true; | |
} | |
public IDisposable BeginScope<TState>(TState state) | |
{ | |
return this; | |
} | |
public void Dispose() | |
{ | |
} | |
} | |
class TestOutputHelperLoggerFactory : ILoggerFactory | |
{ | |
private readonly ITestOutputHelper _output; | |
public TestOutputHelperLoggerFactory(ITestOutputHelper output) | |
{ | |
_output = output; | |
} | |
public void Dispose() | |
{ | |
} | |
public ILogger CreateLogger(string categoryName) | |
{ | |
return new XunitLogger(output: _output); | |
} | |
public void AddProvider(ILoggerProvider provider) | |
{ | |
throw new NotImplementedException(); | |
} | |
} | |
namespace Microsoft.EntityFrameworkCore.Infrastructure | |
{ | |
public class AwesomeModelCustomizer : RelationalModelCustomizer | |
{ | |
public AwesomeModelCustomizer(ModelCustomizerDependencies dependencies) | |
: base(dependencies) { } | |
public override void Customize(ModelBuilder modelBuilder, DbContext context) | |
{ | |
foreach (var mutableEntityType in modelBuilder.Model.GetEntityTypes()) | |
{ | |
if (mutableEntityType.IsKeyless) | |
continue; | |
var keys = mutableEntityType.GetKeys().ToList(); | |
foreach (var k in keys) | |
{ | |
foreach (var mutableProperty in k.Properties) | |
{ | |
if (keys.Count==1 && mutableProperty.ClrType == typeof(decimal) && mutableProperty.IsPrimaryKey()) | |
{ | |
mutableProperty.SetColumnType("INTEGER"); | |
mutableProperty.ValueGenerated = ValueGenerated.OnAdd; | |
mutableProperty.SetValueGeneratorFactory((p,a)=> new TestDecimalValueGeneratorFactory(p)); | |
} | |
} | |
} | |
} | |
base.Customize(modelBuilder, context); | |
} | |
} | |
} | |
public abstract class EFCoreSqliteTestBase<TCtx> where TCtx : DbContext | |
{ | |
private readonly Func<TCtx> _factory; | |
private readonly SqliteConnection _connection; | |
private readonly DbContextOptions<TCtx> _contextOptions; | |
/// <summary> | |
/// Use this when logic in your constructor needs to be created. | |
/// </summary> | |
protected readonly bool Created; | |
private readonly SqliteConnectionStringBuilder _cstr; | |
private readonly TestHarnessIDbContextFactory _contextFact; | |
private static int counter; | |
protected EFCoreSqliteTestBase(string testClassName, ITestOutputHelper logOutput = null, | |
string forceFile = null) | |
{ | |
DataConnection.TurnTraceSwitchOn(); | |
// Create and open a connection. This creates the SQLite in-memory database, which will persist until the connection is closed | |
// at the end of the test (see Dispose below). | |
_cstr = new SqliteConnectionStringBuilder() | |
{ | |
Mode = string.IsNullOrWhiteSpace(forceFile)? SqliteOpenMode.Memory :SqliteOpenMode.ReadWriteCreate, Cache = SqliteCacheMode.Shared, | |
DataSource = string.IsNullOrWhiteSpace(forceFile)? $"{testClassName}-{Interlocked.Increment(ref counter)}": forceFile, Pooling = true | |
}; | |
_connection = new SqliteConnection(_cstr.ConnectionString); | |
_connection.Open(); | |
// These options will be used by the context instances in this test suite, including the connection opened above. | |
_contextOptions = new DbContextOptionsBuilder<TCtx>() | |
.ReplaceService<IModelCustomizer, AwesomeModelCustomizer>() | |
.UseSqlite(_cstr.ConnectionString) | |
.UseLoggerFactory(new TestOutputHelperLoggerFactory(logOutput)) | |
.EnableSensitiveDataLogging() | |
.Options; | |
LinqToDB.EntityFrameworkCore.LinqToDBForEFTools.Initialize(); | |
// Create the schema and seed some data | |
using var context = CreateDbContext(); | |
_contextFact = new TestHarnessIDbContextFactory(this); | |
Created = context.Database.EnsureCreated(); | |
} | |
protected TCtx CreateDbContext() | |
{ | |
return ((TCtx)Activator.CreateInstance(typeof(TCtx),_contextOptions)); | |
} | |
protected IDbContextFactory<TCtx> CreateDbContextFactory() | |
{ | |
return _contextFact; | |
} | |
public class TestHarnessIDbContextFactory : IDbContextFactory<TCtx> | |
{ | |
private readonly EFCoreSqliteTestBase<TCtx> _parent; | |
public TestHarnessIDbContextFactory(EFCoreSqliteTestBase<TCtx> options) | |
{ | |
_parent = options; | |
} | |
public TCtx CreateDbContext() | |
{ | |
return _parent.CreateDbContext(); | |
} | |
} | |
} | |
} | |
namespace Linq2Db.EfCore.Samples.Tests.Microsoft.EntityFrameworkCore.Infrastructure | |
{ | |
public class TestDecimalValueGeneratorFactory : ValueGenerator | |
{ | |
private static long _counter; | |
public TestDecimalValueGeneratorFactory(IProperty property) | |
{ | |
} | |
protected override object NextValue(EntityEntry entry) | |
{ | |
return (decimal)Interlocked.Increment(ref _counter); | |
} | |
public override bool GeneratesTemporaryValues => false; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.ComponentModel.DataAnnotations.Schema; | |
using System.Text.Json; | |
using LinqToDB; | |
using LinqToDB.EntityFrameworkCore; | |
using Microsoft.EntityFrameworkCore; | |
using Xunit.Abstractions; | |
namespace Linq2Db.EfCore.Samples.Tests; | |
public class UnitTest1 | |
{ | |
public class TestTable | |
{ | |
public decimal Id { get; set; } | |
public string What { get; set; } | |
public Decimal? NullableDec { get; set; } | |
} | |
public class TestContext : DbContext | |
{ | |
public TestContext(DbContextOptions<TestContext> options) : base(options) | |
{ | |
} | |
public DbSet<TestTable> TableTest { get; set; } | |
protected override void OnModelCreating(ModelBuilder modelBuilder) | |
{ | |
modelBuilder.Entity<TestTable>().Property(x => x.Id).ValueGeneratedOnAdd(); | |
modelBuilder.Entity<TestTable>().HasKey(x => x.Id); | |
modelBuilder.Entity<TestTable>().Property(x => x.NullableDec).HasMaxLength(10); | |
modelBuilder.Entity<TestTable>().Property(x => x.What) | |
.HasColumnType("VARCHAR").HasMaxLength(100); | |
base.OnModelCreating(modelBuilder); | |
} | |
} | |
public class TestBaseTest : EFCoreSqliteTestBase<TestContext> | |
{ | |
private readonly ITestOutputHelper outLogger; | |
public TestBaseTest(ITestOutputHelper logOutput = null, string forceFile = null) : base(nameof(TestBaseTest), logOutput, forceFile) | |
{ | |
outLogger = logOutput; | |
using (var ctx = CreateDbContext()) | |
{ | |
using (var l2dbCtx = ctx.CreateLinqToDBConnection()) | |
{ | |
l2dbCtx.DropTable<TestTable>(ctx.TableTest.ToLinqToDBTable(l2dbCtx).TableName); | |
l2dbCtx.CreateTable<TestTable>(ctx.TableTest.ToLinqToDBTable(l2dbCtx).TableName); | |
} | |
} | |
} | |
[Fact] | |
public void ThisWorks() | |
{ | |
using (var ctx = CreateDbContext()) | |
{ | |
ctx.TableTest.Add(new TestTable{ What="hello"}); | |
ctx.SaveChanges(); | |
Assert.True(ctx.TableTest.Any()); | |
outLogger.WriteLine(JsonSerializer.Serialize(ctx.TableTest.FirstOrDefault())); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment