NHibernate:如何将 C# [Guid] 插入 MySQL [BINARY(16) DEFAULT (uuid_to_bin(uuid(),1))] 列?

发布于 2025-01-16 21:35:08 字数 1884 浏览 0 评论 0原文

环境: MySQL Server 8.0、.NET Core 3.1、MySql.Data 8.0.28、NHibernate 5.3.11

我有下表:

CREATE TABLE `Master` (
  `Row_Id` char(36) NOT NULL DEFAULT (uuid()),
  `Path` varchar(1000) NOT NULL,
  PRIMARY KEY (`Row_Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

以下是实体定义和映射:

public class MasterEntity
{
    public virtual Guid RowId { get; set; }
    public virtual string Path { get; set; }
}

internal sealed class MasterMap : ClassMapping<MasterEntity>
{
    public MasterMap()
    {
        Table("Master");

        Id
        (
            x => x.RowId,
            map =>
            {
                map.Column("Row_Id");
                map.Generator(Generators.GuidComb);
            }
        );

        Property(x => x.Path, map => { map.Column("Path"); map.NotNullable(true); map.Type(TypeFactory.GetAnsiStringType(1000)); });
    }
}

以下是我INSERT< /code> 该实体使用 NHibernate:

using(ISession session = SessionFactory.OpenSession())
{
    MasterEntity entity = new MasterEntity();
    entity.Path = "c:\whatever";
    session.Save(entity);
    session.Flush();
}

这会正确插入记录。到这里,一切都很好。

现在,我更改了 Row_Id 列的定义,如下所示:

`Row_Id` binary(16) NOT NULL DEFAULT (uuid_to_bin(uuid(),1)),

我没有更改 C# 代码中的任何内容。现在,session.Flush(); 调用会抛出以下异常:

NHibernate.Exceptions.GenericADOException: could not execute batch command.[SQL: SQL not available]
 ---> MySql.Data.MySqlClient.MySqlException (0x80004005): Data too long for column 'Row_Id' at row 1

该错误看起来很明显。 C# 中的 Guid 为 32,列长度为 16。
我需要在映射或实体定义(或代码的其他部分)中进行哪些更改才能将 C# Guid 插入 BINARY(16) DEFAULT (uuid_to_bin(uuid(),1))< /代码> 列?

Environment: MySQL Server 8.0, .NET Core 3.1, MySql.Data 8.0.28, NHibernate 5.3.11

I have following table:

CREATE TABLE `Master` (
  `Row_Id` char(36) NOT NULL DEFAULT (uuid()),
  `Path` varchar(1000) NOT NULL,
  PRIMARY KEY (`Row_Id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

Following is entity definition and mapping:

public class MasterEntity
{
    public virtual Guid RowId { get; set; }
    public virtual string Path { get; set; }
}

internal sealed class MasterMap : ClassMapping<MasterEntity>
{
    public MasterMap()
    {
        Table("Master");

        Id
        (
            x => x.RowId,
            map =>
            {
                map.Column("Row_Id");
                map.Generator(Generators.GuidComb);
            }
        );

        Property(x => x.Path, map => { map.Column("Path"); map.NotNullable(true); map.Type(TypeFactory.GetAnsiStringType(1000)); });
    }
}

Following is how I INSERT this entity using NHibernate:

using(ISession session = SessionFactory.OpenSession())
{
    MasterEntity entity = new MasterEntity();
    entity.Path = "c:\whatever";
    session.Save(entity);
    session.Flush();
}

This inserts the record correctly. Up to here, everything is fine.

Now, I change the definition of Row_Id column as below:

`Row_Id` binary(16) NOT NULL DEFAULT (uuid_to_bin(uuid(),1)),

I do not change anything in my C# code. Now, the session.Flush(); call throws below exception:

NHibernate.Exceptions.GenericADOException: could not execute batch command.[SQL: SQL not available]
 ---> MySql.Data.MySqlClient.MySqlException (0x80004005): Data too long for column 'Row_Id' at row 1

The error looks obvious. The Guid in C# is 32 and column length is 16.
What changes I need to make in my mapping or entity definition (or other part of code) to insert C# Guid into BINARY(16) DEFAULT (uuid_to_bin(uuid(),1)) column?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

疧_╮線 2025-01-23 21:35:08

默认情况下,MySql.Data 会将 Guid 存储为 CHAR(36)。您可以通过在连接字符串中指定 Old Guids = True; 来使用 BINARY(16)

来自 连接器/NET 8.0 连接选项参考

GUID 类型的后端表示形式已从 BINARY(16) 更改为 CHAR(36)。这样做是为了允许开发人员使用服务器函数 UUID() 填充 GUID 表 - UUID() 生成 36 个字符的字符串。旧版应用程序的开发人员可以将 'Old Guiids=true' 添加到连接字符串,以使用数据类型 BINARY(16) 的 GUID。

By default, MySql.Data will store a Guid as CHAR(36). You can use BINARY(16) instead by specifying Old Guids = True; in your connection string.

From Connector/NET 8.0 Connection Options Reference:

The back-end representation of a GUID type was changed from BINARY(16) to CHAR(36). This was done to allow developers to use the server function UUID() to populate a GUID table - UUID() generates a 36-character string. Developers of older applications can add 'Old Guids=true' to the connection string to use a GUID of data type BINARY(16).

徒留西风 2025-01-23 21:35:08

接受的答案中建议的方式有效;但它有一个问题。

问题中的代码使用 uuid_to_bin(uuid(),1);第二个 swap 参数设置为 1。这样,INSERT 就可以很好地工作了;但是当您选择该行时,您会得到完全不同的 UUID。这是因为,数据库驱动程序不知道 UUID 是否被交换。

更好的解决方案是使用 MySqlConnector 而不是 Oracle 的 Connector/NET (MySql.Data.dll )。

对于 ADO.NET:

按照此处的说明进行配置。

对于 .NET Core 2.1 或更高版本,请在应用程序启动期间调用 DbProviderFactories.RegisterFactory("MySqlConnector", MySqlConnectorFactory.Instance)。这将在中央 DbProviderFactories 注册表中注册 MySqlConnector 的 DbProviderFactory 实现。

我的观察是,不需要调用DbProviderFactories.RegisterFactory。它只是通过添加 MySqlConnector.dll 的引用并删除 MySql.Data.dll 的引用来工作。

使用 MySqlConnectorOldGuids=True; 设置可用,但过时的;避免它。
使用 GuidFormat=TimeSwapBinary16; 表示 uuid_to_bin(uuid(),1)(交换参数设置为 1)。
此处提到了其他可能的值

确定应将哪个列类型(如果有)读取为 System.Guid。选项包括:

Char36
所有 CHAR(36) 列均使用带连字符的小写十六进制作为 Guid 进行读取/写入,与 UUID() 匹配。
< br>
Char32
所有 CHAR(32) 列都使用不带连字符的小写十六进制作为 Guid 进行读取/写入。

Binary16
所有 BINARY(16) 列均使用大端字节顺序作为 Guid 进行读取/写入,与 UUID_TO_BIN(x) 匹配。

TimeSwapBinary16
所有 BINARY(16) 列均作为 Guid 读取/写入,使用大端字节顺序并交换时间部分,与 UUID_TO_BIN(x,1)< /代码>。

LittleEndianBinary16
所有 BINARY(16) 列均使用小端字节顺序(即 Guid.ToByteArray()Guid 进行读取/写入code> 和 Guid(byte[]) 构造函数。


没有列类型会自动读取为 Guid

默认
如果 OldGuids=False,则与 Char36 相同; 如果 OldGuids=True,则与 LittleEndianBinary16 相同。


对于NHibernate:

  • 安装NHibernate.MySqlConnector 来自 nuget 包
  • 在会话工厂配置中添加 configuration.DataBaseIntegration(c => c.MySqlConnectorDriver());
  • 如上所述,在连接字符串中设置 GuidFormat

对于其他ORM:

使用请参考与其他 ORM 一起使用。

The way suggested in accepted answer works; but it has a problem.

The code in the question uses uuid_to_bin(uuid(),1); the second swap parameter is set to 1. With this, the INSERT works great; but when you SELECT the row, you get entirely different UUID. This is because, database drivers do not know whether the UUID is swapped or not.

Better solution is to use MySqlConnector instead of Oracle's Connector/NET (MySql.Data.dll).

For ADO.NET:

Configure it as explained here.

For .NET Core 2.1 or later, call DbProviderFactories.RegisterFactory("MySqlConnector", MySqlConnectorFactory.Instance) during application startup. This will register MySqlConnector’s DbProviderFactory implementation in the central DbProviderFactories registry.

My observation is that, call to DbProviderFactories.RegisterFactory is not needed. It just works by adding reference of MySqlConnector.dll and removing reference of MySql.Data.dll.

With MySqlConnector, the OldGuids=True; setting is available but obsolete; avoid it.
Use GuidFormat=TimeSwapBinary16; for uuid_to_bin(uuid(),1) (swap parameter set to 1).
Other possible values are mentioned here:

Determines which column type (if any) should be read as a System.Guid. The options include:

Char36:
All CHAR(36) columns are read/written as a Guid using lowercase hex with hyphens, which matches UUID().

Char32:
All CHAR(32) columns are read/written as a Guid using lowercase hex without hyphens.

Binary16:
All BINARY(16) columns are read/written as a Guid using big-endian byte order, which matches UUID_TO_BIN(x).

TimeSwapBinary16:
All BINARY(16) columns are read/written as a Guid using big-endian byte order with time parts swapped, which matches UUID_TO_BIN(x,1).

LittleEndianBinary16:
All BINARY(16) columns are read/written as a Guid using little-endian byte order, i.e. the byte order used by Guid.ToByteArray() and the Guid(byte[]) constructor.

None:
No column types are automatically read as a Guid.

Default:
Same as Char36 if OldGuids=False; same as LittleEndianBinary16 if OldGuids=True.

For NHibernate:

  • Install NHibernate.MySqlConnector from nuget package.
  • Add configuration.DataBaseIntegration(c => c.MySqlConnectorDriver()); in Session Factory configuration.
  • Set GuidFormat in connection string as explained above.

For other ORMs:

Please refer to this for usage with other ORMs.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文