如何映射主键由 2 个字段组成的数据库(一组插入,一个标识)?

发布于 2024-10-06 05:10:09 字数 1767 浏览 2 评论 0原文

我目前正在开发一个带有 SQL Server 2000 数据库的旧 ASP 应用程序,我们正在尝试使用 .NET 和 NHibernate 将其移植到较新的技术。

在该数据库中,所有表都有一个如下所示的复合 ID:

CREATE TABLE [Languages](
    [languageIncId] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
    [languageSqlId] [smallint] NOT NULL,
    ...
    [createdByIncId] [int] NOT NULL,
    [createdBySqlId] [smallint] NOT NULL,
    ...
    [lastModifiedByIncId] [int] NULL,
    [lastModifiedBySqlId] [smallint] NULL,
    [rowguid] [uniqueidentifier] ROWGUIDCOL  NOT NULL,
        ...
 CONSTRAINT [PK_Languages] PRIMARY KEY CLUSTERED 
(
    [languageIncId] ASC,
    [languageSqlId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]

GO

也就是说,每个表的主键由以下内容组成:

  • XXXSqlId,它是创建该项目的 SQL Server 实例的 Id
  • XXXIncId,它是一个 IDENTITY<插入新行时 /code> 字段递增

SqlId 的要点是,当发生复制时,记录会从一个数据库移动到另一个数据库,并且可能会发生重复的 XXXIncId。不幸的是,没有更改数据库模式,因为许多应用程序依赖它(这确实是痛苦的)。

这也意味着只要表之间存在关系,就需要提供两个字段,如createdByIncId、createdBySqlId中所示。

我正在寻找用 NHibernate(流畅与否)映射此结构的最佳方法,但我被阻止了。我考虑过以下解决方案:

  • Composite-ID与SqlId和IncId一起使用(最自然的解决方案),但它不起作用,因为IncId是由数据库生成的,并且CompositeIDs不支持“ generated”属性
  • 完全忽略这些字段并将“rowguid”视为真正的ID:只要我不尝试处理实体之间的关系(应使用“复合ID”),这种方法就可以很好地工作也作为链接...
  • 使用自定义复合用户类型 (ICompositeUserType) :但这不能用作实体的 ID。

我的问题与问题非常相似 1615647,但答案并不令我满意。

知道还有其他线索吗?

I am currently working on an old ASP application with a SQL Server 2000 database which we are trying to port to newer technologies using .NET and NHibernate.

In that DB, all the tables have an composite ID made like this :

CREATE TABLE [Languages](
    [languageIncId] [int] IDENTITY(1,1) NOT FOR REPLICATION NOT NULL,
    [languageSqlId] [smallint] NOT NULL,
    ...
    [createdByIncId] [int] NOT NULL,
    [createdBySqlId] [smallint] NOT NULL,
    ...
    [lastModifiedByIncId] [int] NULL,
    [lastModifiedBySqlId] [smallint] NULL,
    [rowguid] [uniqueidentifier] ROWGUIDCOL  NOT NULL,
        ...
 CONSTRAINT [PK_Languages] PRIMARY KEY CLUSTERED 
(
    [languageIncId] ASC,
    [languageSqlId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]

GO

That is, each table's primary key is made of :

  • XXXSqlId which is the Id of the SQL Server instance where the item was created
  • XXXIncId which is an IDENTITY field incremented when a new row is inserted

The point of the SqlId is that when replication happens, records are moved from a database to another and duplicate XXXIncId might happen. There is unfortunately no changing the database schema as so many applications rely on it (which is, indeed, painful).

This also means that whenever relations exist between tables, both fields need to be provided as in createdByIncId , createdBySqlId.

I am looking for the best way to map this structure with NHibernate (Fluent or not) but I am blocked. I have considered the following solutions :

  • Using a Composite-ID with SqlId and IncId (the most natural solution) but it does not work because the IncId is generated by the database, and CompositeIDs do not support the "generated" attribute
  • Ignoring completely those fields and consider "rowguid" as the real ID : this works well as long as I do not try to play with the relation between entities, which should use the "composite ID" as the link too...
  • Using a custom Composite user type (ICompositeUserType) : but this can not be used as an ID for an entity.

My question is rather similar to question 1615647, but the answer is not satisfaying for me.

Any idea of another lead to follow ?

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

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

发布评论

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

评论(1

指尖上得阳光 2024-10-13 05:10:09

我们尝试了 2 个线索:

  • 自定义插入 SQL 查询 (),不包含 IDENTITY 字段,并在每次插入后运行额外的 SQL 查询以手动填充属性。这在简单的用例中工作得很好(简单的 SELECT/INSERT),但是当您引用其他实体和其他实体的集合时,它就不起作用了...

  • 使用另一个字段作为主键:每个表还有一个 rowguid 列。我们使用它作为 NHibernate 的真实主键。这在我们的级联插入/更新测试用例中效果很好。我们的复合 ID 只是常规组件,被标记为在插入时生成并且在更新期间不包含,如下所示:

    组件(e => e.Id,
        p =>
        {
            p.Map(i => i.SqlId, "语言SqlId")
                。插入()
                .Not.Update()
                .Not.Nullable();
            p.Map(i => i.IncId, "语言IncId")
                .Not.Update()
                .Not.Nullable()
                .Generate.Insert();
        });
    

然后,对于实体之间的引用,我们指定 2 列用于建立关系的:

多对一:

    References<LanguageEntity>(e => e.Language)
        .Columns("languageSqlId",
                 "languageIncId")
        .PropertyRef(l => l.Id)
        .Fetch.Join()
        .Cascade.None();

一对多:

    HasMany<InterfaceTranslationEntity>(e => e.Translations)
        .KeyColumns.Add("InterfaceSqlId",
                        "InterfaceIncId")
        .PropertyRef("Id") //name of the property in InterfaceTranslationEntity
        .Inverse();

We have tried 2 leads :

  • custom insert SQL queries (<sql-insert>) that do not include the IDENTITY field, and run an extra SQL query after each insert to populate the property manually. This worked fine in simple use cases (simple SELECT/INSERT), but did not work as soon as you are referencing other entities and collections of other entities...

  • use another field as the primary key : each table also has a rowguid column. We use this as the real primary key for NHibernate. This worked fine in our test cases of cascaded insert/updates. Our composite IDs are just regular components that are marked as generated on insert and not included during Update, like this :

    Component<CustomCompositeIdType>(e => e.Id,
        p =>
        {
            p.Map(i => i.SqlId, "languageSqlId")
                .Insert()
                .Not.Update()
                .Not.Nullable();
            p.Map(i => i.IncId, "languageIncId")
                .Not.Update()
                .Not.Nullable()
                .Generated.Insert();
        });
    

Then, for the references between entities, we specify the 2 columns that are used to establish the relationship :

Many-to-one :

    References<LanguageEntity>(e => e.Language)
        .Columns("languageSqlId",
                 "languageIncId")
        .PropertyRef(l => l.Id)
        .Fetch.Join()
        .Cascade.None();

One-to-many :

    HasMany<InterfaceTranslationEntity>(e => e.Translations)
        .KeyColumns.Add("InterfaceSqlId",
                        "InterfaceIncId")
        .PropertyRef("Id") //name of the property in InterfaceTranslationEntity
        .Inverse();
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文