Skip to content

Commit 6dcdabe

Browse files
committed
implementation of schema generation (without scaffolding)
1 parent 462eb7f commit 6dcdabe

File tree

7 files changed

+199
-23
lines changed

7 files changed

+199
-23
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using EfCore.Ydb.Extensions;
2+
using Microsoft.EntityFrameworkCore.Design;
3+
using Microsoft.Extensions.DependencyInjection;
4+
5+
namespace EfCore.Ydb.Design.Internal;
6+
7+
public class YdbDesignTimeServices : IDesignTimeServices
8+
{
9+
public void ConfigureDesignTimeServices(IServiceCollection serviceCollection)
10+
{
11+
serviceCollection.AddEntityFrameworkYdb();
12+
13+
new EntityFrameworkRelationalDesignServicesBuilder(serviceCollection)
14+
.TryAddCoreServices();
15+
}
16+
}
Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,62 @@
11
using System;
2+
using System.Data;
3+
using System.Diagnostics;
4+
using System.Linq;
25
using System.Threading;
36
using System.Threading.Tasks;
7+
using EfCore.Ydb.Storage.Internal;
8+
using Microsoft.EntityFrameworkCore.Diagnostics;
49
using Microsoft.EntityFrameworkCore.Migrations;
10+
using Microsoft.EntityFrameworkCore.Storage;
11+
using Ydb.Sdk.Ado;
512

613
namespace EfCore.Ydb.Migrations.Internal;
714

8-
// TODO: For migrations only. Not required for mvp
915
public class YdbHistoryRepository : HistoryRepository
1016
{
11-
public YdbHistoryRepository(
12-
HistoryRepositoryDependencies dependencies
13-
) : base(dependencies)
17+
public YdbHistoryRepository(HistoryRepositoryDependencies dependencies) : base(dependencies)
1418
{
1519
}
1620

1721
protected override bool InterpretExistsResult(object? value)
18-
{
19-
throw new NotImplementedException();
20-
}
22+
=> throw new InvalidOperationException("Shouldn't be called");
2123

2224
public override IMigrationsDatabaseLock AcquireDatabaseLock()
2325
{
24-
throw new NotImplementedException();
26+
Dependencies.MigrationsLogger.AcquiringMigrationLock();
27+
return new YdbMigrationDatabaseLock(this, Dependencies.Connection);
2528
}
2629

2730
public override Task<IMigrationsDatabaseLock> AcquireDatabaseLockAsync(
28-
CancellationToken cancellationToken = new CancellationToken())
31+
CancellationToken cancellationToken = default
32+
)
2933
{
3034
throw new NotImplementedException();
3135
}
3236

3337
public override string GetCreateIfNotExistsScript()
38+
=> GetCreateScript().Replace("CREATE TABLE", "CREATE TABLE IF NOT EXISTS");
39+
40+
public override LockReleaseBehavior LockReleaseBehavior => LockReleaseBehavior.Transaction;
41+
42+
protected override string ExistsSql
43+
=> throw new UnreachableException("Shouldn't be called. We check if exists using different approach");
44+
45+
public override bool Exists()
46+
=> ExistsAsync().ConfigureAwait(false).GetAwaiter().GetResult();
47+
48+
public override Task<bool> ExistsAsync(CancellationToken cancellationToken = default)
3449
{
35-
throw new NotImplementedException();
50+
var connection = (YdbRelationalConnection)Dependencies.Connection;
51+
var schema = (YdbConnection)connection.DbConnection;
52+
var tables = schema.GetSchema("tables");
53+
54+
var foundTables =
55+
from table in tables.AsEnumerable()
56+
where table.Field<string>("table_type") == "TABLE"
57+
&& table.Field<string>("table_name") == TableName
58+
select table;
59+
return Task.FromResult(foundTables.Count() == 1);
3660
}
3761

3862
public override string GetBeginIfNotExistsScript(string migrationId)
@@ -50,6 +74,27 @@ public override string GetEndIfScript()
5074
throw new NotImplementedException();
5175
}
5276

53-
public override LockReleaseBehavior LockReleaseBehavior { get; }
54-
protected override string ExistsSql { get; }
77+
// TODO Implement lock
78+
private sealed class YdbMigrationDatabaseLock : IMigrationsDatabaseLock
79+
{
80+
private YdbRelationalConnection _connection;
81+
82+
public YdbMigrationDatabaseLock(
83+
IHistoryRepository historyRepository,
84+
IRelationalConnection connection
85+
)
86+
{
87+
HistoryRepository = historyRepository;
88+
_connection = (YdbRelationalConnection)connection;
89+
}
90+
91+
public void Dispose()
92+
{
93+
}
94+
95+
public ValueTask DisposeAsync()
96+
=> default;
97+
98+
public IHistoryRepository HistoryRepository { get; }
99+
}
55100
}
Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,85 @@
1+
using Microsoft.EntityFrameworkCore.Metadata;
12
using Microsoft.EntityFrameworkCore.Migrations;
3+
using Microsoft.EntityFrameworkCore.Migrations.Operations;
24

35
namespace EfCore.Ydb.Migrations;
46

57
public class YdbMigrationsSqlGenerator : MigrationsSqlGenerator
68
{
7-
public YdbMigrationsSqlGenerator(
8-
MigrationsSqlGeneratorDependencies dependencies
9-
) : base(dependencies)
9+
public YdbMigrationsSqlGenerator(MigrationsSqlGeneratorDependencies dependencies) : base(dependencies)
1010
{
1111
}
12+
13+
protected override void Generate(
14+
CreateTableOperation operation,
15+
IModel? model,
16+
MigrationCommandListBuilder builder,
17+
bool terminate = true
18+
)
19+
{
20+
if (!terminate && operation.Comment is not null)
21+
{
22+
// TODO: Handle comments
23+
}
24+
25+
builder.Append("CREATE ");
26+
// TODO: Support EXTERNAL tables?
27+
builder
28+
.Append("TABLE ")
29+
.Append(DelimitIdentifier(operation.Name, operation.Schema))
30+
.AppendLine(" (");
31+
32+
using (builder.Indent())
33+
{
34+
CreateTableColumns(operation, model, builder);
35+
CreateTableConstraints(operation, model, builder);
36+
builder.AppendLine();
37+
}
38+
39+
builder.Append(")");
40+
41+
// TODO: Support `WITH {}` block
42+
43+
if (terminate)
44+
{
45+
builder.Append(";");
46+
EndStatement(builder, suppressTransaction: true);
47+
}
48+
}
49+
50+
protected override void CreateTablePrimaryKeyConstraint(
51+
CreateTableOperation operation,
52+
IModel? model,
53+
MigrationCommandListBuilder builder
54+
)
55+
{
56+
if (operation.PrimaryKey == null) return;
57+
58+
builder.AppendLine(",");
59+
PrimaryKeyConstraint(operation.PrimaryKey, model, builder);
60+
}
61+
62+
protected override void PrimaryKeyConstraint(
63+
AddPrimaryKeyOperation operation,
64+
IModel? model,
65+
MigrationCommandListBuilder builder
66+
)
67+
{
68+
builder
69+
.Append("PRIMARY KEY (")
70+
.Append(ColumnList(operation.Columns))
71+
.Append(")");
72+
IndexOptions(operation, model, builder);
73+
}
74+
75+
protected override void Generate(SqlOperation operation, IModel? model, MigrationCommandListBuilder builder)
76+
{
77+
builder.AppendLine(operation.Sql);
78+
// TODO: Find out how to apply migration without suppressing transaction
79+
// We suppress transaction because CREATE/DROP operations cannot be executed during them
80+
EndStatement(builder, suppressTransaction: true);
81+
}
82+
83+
private string DelimitIdentifier(string name, string? schema)
84+
=> Dependencies.SqlGenerationHelper.DelimitIdentifier(name, schema);
1285
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
using Microsoft.EntityFrameworkCore.Design;
2+
3+
[assembly: DesignTimeProviderServices("EfCore.Ydb.Design.Internal.YdbDesignTimeServices")]

src/EfCore.Ydb/src/Storage/Internal/IYdbRelationalConnection.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@ namespace EfCore.Ydb.Storage.Internal;
44

55
public interface IYdbRelationalConnection : IRelationalConnection
66
{
7+
8+
IYdbRelationalConnection Clone();
79
}
Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,51 @@
11
using System;
2+
using System.Threading;
3+
using System.Threading.Tasks;
4+
using Microsoft.EntityFrameworkCore.Diagnostics;
25
using Microsoft.EntityFrameworkCore.Storage;
6+
using Ydb.Sdk.Ado;
37

48
namespace EfCore.Ydb.Storage.Internal;
59

610
public class YdbDatabaseCreator : RelationalDatabaseCreator
711
{
12+
private readonly IYdbRelationalConnection _connection;
13+
private readonly IRelationalConnectionDiagnosticsLogger _connectionLogger;
14+
815
public YdbDatabaseCreator(
9-
RelationalDatabaseCreatorDependencies dependencies
16+
RelationalDatabaseCreatorDependencies dependencies,
17+
IYdbRelationalConnection connection,
18+
IRawSqlCommandBuilder rawSqlCommandBuilder,
19+
IRelationalConnectionDiagnosticsLogger connectionLogger
1020
) : base(dependencies)
1121
{
22+
_connection = connection;
23+
_connectionLogger = connectionLogger;
1224
}
1325

1426
public override bool Exists()
27+
=> ExistsInternal().GetAwaiter().GetResult();
28+
29+
public override Task<bool> ExistsAsync(CancellationToken cancellationToken = new CancellationToken())
30+
=> ExistsInternal(cancellationToken);
31+
32+
private async Task<bool> ExistsInternal(CancellationToken cancellationToken = default)
1533
{
16-
throw new NotImplementedException();
34+
var connection = _connection.Clone();
35+
try
36+
{
37+
await connection.OpenAsync(cancellationToken, errorsExpected: true);
38+
return true;
39+
}
40+
catch (YdbException)
41+
{
42+
return false;
43+
}
44+
finally
45+
{
46+
await connection.CloseAsync().ConfigureAwait(false);
47+
await connection.DisposeAsync().ConfigureAwait(false);
48+
}
1749
}
1850

1951
public override bool HasTables()
@@ -22,12 +54,8 @@ public override bool HasTables()
2254
}
2355

2456
public override void Create()
25-
{
26-
throw new NotImplementedException();
27-
}
57+
=> throw new NotSupportedException("Ydb does not support database creation");
2858

2959
public override void Delete()
30-
{
31-
throw new NotImplementedException();
32-
}
60+
=> throw new NotSupportedException("Ydb does not support database deletion");
3361
}

src/EfCore.Ydb/src/Storage/Internal/YdbRelationalConnection.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System.Data.Common;
2+
using EfCore.Ydb.Extensions;
3+
using Microsoft.EntityFrameworkCore;
24
using Microsoft.EntityFrameworkCore.Storage;
35
using Ydb.Sdk.Ado;
46

@@ -24,4 +26,11 @@ protected override DbConnection CreateDbConnection()
2426
var connection = new YdbConnection(GetValidatedConnectionString());
2527
return connection;
2628
}
29+
30+
public IYdbRelationalConnection Clone()
31+
{
32+
var connectionStringBuilder = new YdbConnectionStringBuilder(GetValidatedConnectionString());
33+
var options = new DbContextOptionsBuilder().UseEfYdb(connectionStringBuilder.ToString()).Options;
34+
return new YdbRelationalConnection(Dependencies with { ContextOptions = options });
35+
}
2736
}

0 commit comments

Comments
 (0)