【问题标题】:(Fluent) NHibernate mapping with CompositeId(流利的)NHibernate 映射与 CompositeId
【发布时间】:2026-01-13 02:55:02
【问题描述】:

我有一个正在运行的数据库,其中包含大量产品数据,并且我正在尝试为以下结构获取 (Fluent) NHibernate 映射。

但是运行代码会报错: 外键 (FK1F94D86A1A0EC427:Product [ProductDet1_id])) 的列数必须与引用的主键 (ProductDet1 [ProductNumber, ProductionLine]) 的列数相同

所以在映射过程中出了点问题,但无法弄清楚它是什么。有没有人可以让我摆脱这个问题? :-)

(我在表格中列出了所有具体细节,只是为了便于阅读)

我们有一个产品表,其中产品编号与生产线的组合是唯一的。每个产品只能有一个 ProductDet1、ProductDet2 ...等

MSSQL 产品表:

CREATE TABLE [dbo].[Product] (
    [Id]             INT           NOT NULL IDENTITY,
    [ProductNumber]  INT           NOT NULL,
    [ProductionLine] INT           NOT NULL,
    CONSTRAINT [AK_Product_ProductNumber] UNIQUE ([ProductNumber], [ProductionLine]), 
    CONSTRAINT [PK_Product] PRIMARY KEY ([Id]) 
);

MSSQL ProductDet1 和 ProductDet2 表:

CREATE TABLE [dbo].[ProductDet1] (
    [ProductNumber]   INT           NOT NULL,
    [ProductionLine]  INT           NOT NULL, 
    [TheValue]        VARCHAR (15)  NULL,
    CONSTRAINT [FK_ProductDet1_Product] FOREIGN KEY ([ProductNumber], [ProductionLine]) REFERENCES [Product]([ProductNumber],[ProductionLine]), 
    CONSTRAINT [AK_ProductDet1_ProductNumber] UNIQUE ([ProductNumber], [ProductionLine])
);
GO
CREATE INDEX [IX_ProductDet1_ProductNumber] ON [dbo].[ProductDet1] ([ProductNumber])
GO
CREATE INDEX [IX_ProductDet1_ProductionLine] ON [dbo].[ProductDet1] ([ProductionLine])

C#产品类:

public class Product
{
    public Product ()
    {
        ProductDet1 = new ProductDet1();
        ProductDet2 = new ProductDet2();
    }

    public virtual int Id { get; set; }
    public virtual int ProductionLine { get; set; }
    public virtual int ProductNumber { get; set; }
    public virtual string ProductName { get; set; }

    public virtual ProductDet1 ProductDet1 { get; set; }
    public virtual ProductDet2 ProductDet2 { get; set; }

    public override bool Equals(object obj)
    {
        // If parameter is null return false.
        if (obj == null)
        {
            return false;
        }

        // If parameter cannot be cast to Reference return false.
        var product = obj as Product;

        if (product == null)
        {
            return false;
        }

        // Return true if the fields match:
        return this.ProductionLine == product.ProductionLine && this.ProductNumber == product.ProductNumber;
    }

    public override int GetHashCode()
    {
        return base.GetHashCode();
    }
}

C# 产品 Det1 和产品 Det2 类:

public class ProductDet1
{
    public virtual int Id { get; set; }
    public virtual int ProductionLine { get; set; }
    public virtual int ProductNumber { get; set; }

    public virtual string TheValue { get; set; }

    public virtual Product Product { get; set; }

    public override bool Equals(object obj)
    {
        // If parameter is null return false.
        if (obj == null)
        {
            return false;
        }

        // If parameter cannot be cast to Reference return false.
        var product = obj as ProductCRT;

        if (product == null)
        {
            return false;
        }

        // Return true if the fields match:
        return this.ProductionLine == product.ProductionLine && this.ProductNumber == product.ProductNumber;
    }

    public override int GetHashCode()
    {
        return base.GetHashCode();
    }
}

产品图:

public class ProductEntityMap : ClassMap<Product>
{
    public ProductEntityMap()
    {
        Id(x => x.Id);

        Map(x => x.ProductNumber);
        Map(x => x.ProductionLine);
        Map(x => x.ProductName);

        References(x => x.ProductDet1).Cascade.All().Not.LazyLoad();
        References(x => x.ProductDet2).Cascade.All().Not.LazyLoad();
    }
}

产品 Det 1 和 Det 2 地图:

public class ProductDet1EntityMap : ClassMap<ProductDet1>
{
    public ProductDet1EntityMap()
    {
        CompositeId().KeyProperty(x => x.ProductNumber).KeyProperty(x => x.ProductionLine);

        Map(x => x.TheValue);
    }
}

【问题讨论】:

    标签: c# sql-server nhibernate fluent-nhibernate nhibernate-mapping


    【解决方案1】:

    一个稍微不同的类结构和映射隐藏了 det 类的复杂性。它本质上设置了 References() 上使用的列

    public class Product
    {
        private ProductDet ProductDet1 { get; set; }
        private ProductDet ProductDet2 { get; set; }
    
        protected Product() { } // make NHibernate happy
    
        public Product(int productionLine, int productNumber) : this(new ProductKey(productionLine, productNumber)) { }
        public Product(ProductKey key)
        {
            Key = key;
            ProductDet1 = new ProductDet { ProductKey = Key };
            ProductDet2 = new ProductDet { ProductKey = Key };
        }
    
        public virtual int ID { get; protected set; }
        public virtual ProductKey Key { get; protected set; }
    
        public virtual string Det1
        {
            get { return ProductDet1.Value; }
            set { ProductDet1.Value = value; }
        }
        public virtual string Det2
        {
            get { return ProductDet2.Value; }
            set { ProductDet2.Value = value; }
        }
    }
    
    public class ProductKey
    {
        protected ProductKey() { } // make NHibernate happy
    
        public ProductKey(int productionLine, int productNumber)
        {
            ProductionLine = productionLine;
            ProductNumber = productNumber;
        }
        public virtual int ProductionLine { get; private set; }
        public virtual int ProductNumber { get; private set; }
    
        public override bool Equals(object obj)
        {
            var other = obj as ProductKey;
            return other != null && other.ProductionLine == this.ProductionLine && other.ProductNumber == this.ProductNumber;
        }
    
        public override int GetHashCode()
        {
            return (ProductionLine << 16) + ProductNumber;
        }
    }
    
    public class ProductDet
    {
        public virtual ProductKey ProductKey { get; set; }
        public virtual string Value { get; set; }
    }
    
    public class ProductMap : ClassMap<Product>
    {
        public ProductMap()
        {
            Id(x => x.ID);
    
            Component(x => x.Key, c =>
            {
                c.Map(k => k.ProductionLine).UniqueKey("product_key");
                c.Map(k => k.ProductNumber).UniqueKey("product_key");
            });
    
            MapProductDetReference("ProductDet1");
            MapProductDetReference("ProductDet2");
        }
    
        private void MapProductDetReference(string entityName)
        {
            References(Reveal.Member<Product, ProductDet>(entityName))
                .Columns("ProductionLine", "ProductNumber")
                .ReadOnly()
                .EntityName(entityName)
                .Cascade.All()
                .Fetch.Join()
                .Not.LazyLoad();
        }
    }
    
    public abstract class ProductDetMap : ClassMap<ProductDet>
    {
        public ProductDetMap()
        {
            CompositeId(x => x.ProductKey)
                .KeyProperty(k => k.ProductionLine)
                .KeyProperty(k => k.ProductNumber);
    
            Map(x => x.Value, "TheValue");
        }
    }
    
    public class ProductDet1Map : ProductDetMap
    {
        public ProductDet1Map()
        {
            EntityName("ProductDet1");
            Table("ProductDet1");
        }
    }
    public class ProductDet2Map : ProductDetMap
    {
        public ProductDet2Map()
        {
            EntityName("ProductDet2");
            Table("ProductDet2");
        }
    }
    

    【讨论】:

    • 在实时数据库中,我必须将“.NotFound.Ignore()”添加到 ProductMap 中的引用中,因为细节并不总是存在。 Tnx @Firo !