Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/EFCore.Ydb/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
- Fixed bug: Error when saving an entity without specifying a default value ()
- Fixed bug: SqlQuery throws exception when using list parameters ([#540](https://github.com/ydb-platform/ydb-dotnet-sdk/issues/540)).
- Added support for the YDB retry policy (ADO.NET) and new configuration methods in `YdbDbContextOptionsBuilder`:
- `EnableRetryIdempotence()`: enables retries for errors classified as idempotent. You must ensure the operation itself is idempotent.
Expand Down
24 changes: 7 additions & 17 deletions src/EFCore.Ydb/src/Migrations/Internal/YdbHistoryRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public override async Task<IMigrationsDatabaseLock> AcquireDatabaseLockAsync(
{
await Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync(
AcquireDatabaseLockCommand(),
((IYdbRelationalConnection)Dependencies.Connection).Clone(), // TODO usage ExecutionContext
Dependencies.Connection,
new MigrationExecutionState(),
commitTransaction: true,
cancellationToken: cancellationToken
Expand Down Expand Up @@ -85,9 +85,7 @@ private async Task ReleaseDatabaseLockAsync()
try
{
await Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync(
ReleaseDatabaseLockCommand(),
((IYdbRelationalConnection)Dependencies.Connection).Clone()
).ConfigureAwait(false);
ReleaseDatabaseLockCommand(), Dependencies.Connection).ConfigureAwait(false);

return;
}
Expand All @@ -98,10 +96,8 @@ await Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync(
}
}

private IReadOnlyList<MigrationCommand> ReleaseDatabaseLockCommand() =>
Dependencies.MigrationsSqlGenerator.Generate(new List<MigrationOperation>
{ new SqlOperation { Sql = GetDeleteScript(LockKey) } }
);
private IReadOnlyList<MigrationCommand> ReleaseDatabaseLockCommand() => Dependencies.MigrationsSqlGenerator
.Generate(new List<MigrationOperation> { new SqlOperation { Sql = GetDeleteScript(LockKey) } });

bool IHistoryRepository.CreateIfNotExists() => CreateIfNotExistsAsync().GetAwaiter().GetResult();

Expand Down Expand Up @@ -135,13 +131,7 @@ await Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync(

private IReadOnlyList<MigrationCommand> GetCreateIfNotExistsCommands() =>
Dependencies.MigrationsSqlGenerator.Generate(new List<MigrationOperation>
{
new SqlOperation
{
Sql = GetCreateIfNotExistsScript(),
SuppressTransaction = true
}
});
{ new SqlOperation { Sql = GetCreateIfNotExistsScript(), SuppressTransaction = true } });

public override string GetCreateIfNotExistsScript()
=> GetCreateScript().Replace("CREATE TABLE", "CREATE TABLE IF NOT EXISTS");
Expand Down Expand Up @@ -179,8 +169,8 @@ private IReadOnlyList<MigrationCommand> SelectHistoryTableCommand() =>
{
new SqlOperation
{
Sql = $"SELECT * FROM {SqlGenerationHelper.DelimitIdentifier(TableName, TableSchema)}" +
$" WHERE '{MigrationIdColumnName}' = '{LockKey}';"
Sql = $"SELECT * FROM {SqlGenerationHelper.DelimitIdentifier(TableName, TableSchema)} " +
$"WHERE '{MigrationIdColumnName}' = CAST(RandomUuid(0) AS Text)"
}
});

Expand Down
5 changes: 5 additions & 0 deletions src/EFCore.Ydb/src/Migrations/YdbMigrationsSqlGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ MigrationCommandListBuilder builder
.Append(" ")
.Append(columnType)
.Append(operation.IsNullable ? string.Empty : " NOT NULL");

if (autoincrement == true)
return;

DefaultValue(operation.DefaultValue, operation.DefaultValueSql, columnType, builder);
}

protected override void CreateTablePrimaryKeyConstraint(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,34 +1,21 @@
using System;
using Microsoft.EntityFrameworkCore.Storage;
using Ydb.Sdk.Ado.YdbType;

namespace EntityFrameworkCore.Ydb.Storage.Internal.Mapping;

// TODO: Await DateOnly support in Ydb.Sdk
public class YdbDateOnlyTypeMapping : RelationalTypeMapping
public class YdbDateOnlyTypeMapping : YdbTypeMapping
{
private const string DateOnlyFormatConst = "{0:yyyy-MM-dd}";

public YdbDateOnlyTypeMapping(string storeType)
: base(
new RelationalTypeMappingParameters(
new CoreTypeMappingParameters(typeof(DateOnly)),
storeType,
StoreTypePostfix.None,
System.Data.DbType.Date
)
)
public YdbDateOnlyTypeMapping(YdbDbType ydbDbType) : base(typeof(DateOnly), ydbDbType)
{
}

protected YdbDateOnlyTypeMapping(RelationalTypeMappingParameters parameters) : base(parameters)
private YdbDateOnlyTypeMapping(RelationalTypeMappingParameters parameters, YdbDbType ydbDbType)
: base(parameters, ydbDbType)
{
}

protected override YdbDateOnlyTypeMapping Clone(RelationalTypeMappingParameters parameters) => new(parameters);
protected override YdbDateOnlyTypeMapping Clone(RelationalTypeMappingParameters parameters) =>
new(parameters, YdbDbType);

protected override string GenerateNonNullSqlLiteral(object value)
{
var dateOnly = (DateOnly)value;
return $"Date('{dateOnly.ToString(DateOnlyFormatConst)}')";
}
protected override string SqlLiteralFormatString => $"{YdbDbType}('{{0:yyyy-MM-dd}}')";
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
using System.Data;
using Microsoft.EntityFrameworkCore.Storage;
using System;
using Ydb.Sdk.Ado.YdbType;

namespace EntityFrameworkCore.Ydb.Storage.Internal.Mapping;

public class YdbDateTimeTypeMapping(
string storeType,
DbType? dbType
) : DateTimeTypeMapping(storeType, dbType)
public class YdbDateTimeTypeMapping : YdbTypeMapping
{
private const string DateTimeFormatConst = @"{0:yyyy-MM-dd HH\:mm\:ss.fffffff}";
public YdbDateTimeTypeMapping(YdbDbType ydbDbType) : base(typeof(DateTime), ydbDbType)
{
}

private string StoreTypeLiteral { get; } = storeType;
private YdbDateTimeTypeMapping(RelationalTypeMappingParameters parameters, YdbDbType ydbDbType)
: base(parameters, ydbDbType)
{
}

protected override string SqlLiteralFormatString
=> "CAST('" + DateTimeFormatConst + $"' AS {StoreTypeLiteral})";
protected override YdbDateTimeTypeMapping Clone(RelationalTypeMappingParameters parameters) =>
new(parameters, YdbDbType);

protected override string SqlLiteralFormatString => YdbDbType switch
{
YdbDbType.Timestamp or YdbDbType.Timestamp64 => $@"{YdbDbType}('{{0:yyyy-MM-ddTHH\:mm\:ss.ffffffZ}}')",
YdbDbType.Datetime or YdbDbType.Datetime64 => $@"{YdbDbType}('{{0:yyyy-MM-ddTHH\:mm\:ssZ}}')",
YdbDbType.Date or YdbDbType.Date32 => $"{YdbDbType}('{{0:yyyy-MM-dd}}')",
_ => throw new ArgumentOutOfRangeException(nameof(YdbDbType), YdbDbType, null)
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Xml;
using Ydb.Sdk.Ado.YdbType;

namespace EntityFrameworkCore.Ydb.Storage.Internal.Mapping;

public class YdbTimeSpanTypeMapping : YdbTypeMapping
{
public YdbTimeSpanTypeMapping(YdbDbType ydbDbType) : base(typeof(TimeSpan), ydbDbType)
{
}

private YdbTimeSpanTypeMapping(RelationalTypeMappingParameters parameters, YdbDbType ydbDbType)
: base(parameters, ydbDbType)
{
}

protected override YdbTimeSpanTypeMapping Clone(RelationalTypeMappingParameters parameters) =>
new(parameters, YdbDbType);

protected override string GenerateNonNullSqlLiteral(object value) =>
$"{YdbDbType}('{XmlConvert.ToString((TimeSpan)value)}')";
}
41 changes: 41 additions & 0 deletions src/EFCore.Ydb/src/Storage/Internal/Mapping/YdbTypeMapping.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using System;
using System.Data.Common;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Storage.Json;
using Ydb.Sdk.Ado;
using Ydb.Sdk.Ado.YdbType;

namespace EntityFrameworkCore.Ydb.Storage.Internal.Mapping;

public abstract class YdbTypeMapping(
RelationalTypeMapping.RelationalTypeMappingParameters parameters,
YdbDbType ydbDbType
) : RelationalTypeMapping(parameters), IYdbTypeMapping
{
public YdbDbType YdbDbType { get; } = ydbDbType;

protected YdbTypeMapping(
Type clrType,
YdbDbType ydbDbType,
JsonValueReaderWriter? jsonValueReaderWriter = null
) : this(
new RelationalTypeMappingParameters(
new CoreTypeMappingParameters(clrType, jsonValueReaderWriter: jsonValueReaderWriter),
ydbDbType.ToString()
), ydbDbType
)
{
}

protected override void ConfigureParameter(DbParameter parameter)
{
if (parameter is not YdbParameter ydbParameter)
{
throw new InvalidOperationException(
$"Ydb-specific type mapping {GetType().Name} being used with non-Ydb parameter type {parameter.GetType().Name}");
}

base.ConfigureParameter(parameter);
ydbParameter.YdbDbType = YdbDbType;
}
}
40 changes: 25 additions & 15 deletions src/EFCore.Ydb/src/Storage/Internal/YdbTypeMappingSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,26 @@ RelationalTypeMappingSourceDependencies relationalDependencies

private static readonly YdbDecimalTypeMapping Decimal = YdbDecimalTypeMapping.Default;

private static readonly GuidTypeMapping Guid = YdbGuidTypeMapping.Default;
private static readonly GuidTypeMapping Uuid = YdbGuidTypeMapping.Default;

private static readonly YdbTextTypeMapping Text = YdbTextTypeMapping.Default;
private static readonly YdbBytesTypeMapping Bytes = YdbBytesTypeMapping.Default;
private static readonly YdbJsonTypeMapping Json = new("Json", typeof(JsonElement), null);

private static readonly YdbDateOnlyTypeMapping Date = new("Date");
private static readonly DateTimeTypeMapping DateTime = new("DateTime");
private static readonly YdbDateOnlyTypeMapping DateDateOnly = new(YdbDbType.Date);
private static readonly YdbDateOnlyTypeMapping Date32DateOnly = new(YdbDbType.Date32);

private static readonly YdbDateTimeTypeMapping Timestamp = new("Timestamp", DbType.DateTime);
private static readonly YdbDateTimeTypeMapping DateDateTime = new(YdbDbType.Date);
private static readonly YdbDateTimeTypeMapping Date32DateTime = new(YdbDbType.Date32);

// TODO: Await interval in Ydb.Sdk
private static readonly TimeSpanTypeMapping Interval = new("Interval", DbType.Object);
private static readonly YdbDateTimeTypeMapping Datetime = new(YdbDbType.Datetime);
private static readonly YdbDateTimeTypeMapping Datetime64 = new(YdbDbType.Datetime64);

private static readonly YdbDateTimeTypeMapping Timestamp = new(YdbDbType.Timestamp);
private static readonly YdbDateTimeTypeMapping Timestamp64 = new(YdbDbType.Timestamp64);

private static readonly YdbTimeSpanTypeMapping Interval = new(YdbDbType.Interval);
private static readonly YdbTimeSpanTypeMapping Interval64 = new(YdbDbType.Interval64);

#endregion

Expand All @@ -72,17 +79,20 @@ RelationalTypeMappingSourceDependencies relationalDependencies
{ "Float", [Float] },
{ "Double", [Double] },

{ "Guid", [Guid] },

{ "Date", [Date] },
{ "DateTime", [DateTime] },
{ "Timestamp", [Timestamp] },
{ "Interval", [Interval] },
{ "Guid", [Uuid] },

{ "Text", [Text] },
{ "Bytes", [Bytes] },

{ "Json", [Json] }
{ "Date", [DateDateTime, DateDateOnly] },
{ "DateTime", [Datetime] },
{ "Timestamp", [Timestamp] },
{ "Interval", [Interval] },

{ "Date32", [Date32DateTime, Date32DateOnly] },
{ "Datetime64", [Datetime64] },
{ "Timestamp64", [Timestamp64] },
{ "Interval64", [Interval64] }
};

private static readonly Dictionary<Type, RelationalTypeMapping> ClrTypeMapping = new()
Expand All @@ -102,13 +112,13 @@ RelationalTypeMappingSourceDependencies relationalDependencies
{ typeof(float), Float },
{ typeof(double), Double },

{ typeof(Guid), Guid },
{ typeof(Guid), Uuid },

{ typeof(string), Text },
{ typeof(byte[]), Bytes },
{ typeof(JsonElement), Json },

{ typeof(DateOnly), Date },
{ typeof(DateOnly), DateDateOnly },
{ typeof(DateTime), Timestamp },
{ typeof(TimeSpan), Interval }
};
Expand Down
Loading
Loading