Пакетная вставка — внешний ключ не работает

Я пытаюсь сделать пакетную вставку, и это не работает. Я думал, что у меня это работает, но что-то, кажется, сломалось, и я был бы признателен, если бы кто-нибудь мог показать мне, что.

Изменить. Вот схема базы данных:

CREATE TABLE [dbo].[Categories](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](100) NOT NULL,
    CONSTRAINT [PK_Categories] PRIMARY KEY CLUSTERED ([Id])
)

CREATE TABLE [dbo].[ProductTopSellersCategory](
    [ProductId] [int] NOT NULL,
    [CategoryId] [int] NOT NULL,
    [Order] [int] NOT NULL,
    CONSTRAINT [PK_ProductTopSellersCategory]
          PRIMARY KEY CLUSTERED ([ProductId], [CategoryId])
)

ALTER TABLE [dbo].[ProductTopSellersCategory] ADD
    CONSTRAINT [FK_ProductTopSellersCategory_Products]
        FOREIGN KEY ([ProductId]) REFERENCES [dbo].[Products] ([Id]),
    CONSTRAINT [FK_ProductTopSellersCategory_Categories]
        FOREIGN KEY ([CategoryId]) REFERENCES [dbo].[Categories] ([Id])

У меня есть следующие сущности:

public class Category {
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
}

public class ProductTopSellerCategory {
    public virtual ProductTopSellerCategoryIdentifier Id { get; set; }

    private Product _product;
    public virtual Product Product {
        get { return _product; }
        set { _product = value; Id.ProductId = _product.Id; }
    }

    private Category _category;
    public virtual Category Category {
        get { return _category; }
        set { _category = value; Id.CategoryId = _category.Id; }
    }

    [Required]
    public virtual int Order { get; set; }

    public ProductTopSellerCategory() {
        Id = new ProductTopSellerCategoryIdentifier();
    }
}

[Serializable]
public class ProductTopSellerCategoryIdentifier {
    public virtual int ProductId { get; set; }
    public virtual int CategoryId { get; set; }

    #region Composite Id Members

    public override bool Equals(object obj) {
        if (obj == null || !(obj is ProductTopSellerCategoryIdentifier))
            return false;

        var i = (ProductTopSellerCategoryIdentifier)obj;

        return ProductId == i.ProductId && CategoryId == i.CategoryId;
    }

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

    public override string ToString() {
        return ProductId + "|" + CategoryId;
    }

    #endregion
}

С соответствующими беглыми отображениями:

public class CategoryMap : ClassMap<Category> {
    public CategoryMap() {
        Table("Categories");
        Id(x => x.Id);
        Map(x => x.Name);
    }
}

public class ProductTopSellerCategoryMap : ClassMap<ProductTopSellerCategory> {
    public ProductTopSellerCategoryMap() {
        Table("ProductTopSellersCategory");
        CompositeId(x => x.Id)
            .KeyProperty(x => x.ProductId)
            .KeyProperty(x => x.CategoryId);
        References(x => x.Product).ReadOnly();
        References(x => x.Category).ReadOnly();
        Map(x => x.Order, "[Order]");
    }
}

Теперь, когда я говорю:

var category = new Category() { Name = "Test 1" };
var product = session.Get<Product>(1);
var topSeller = new ProductTopSellerCategory() { Product = product, Category = category };

session.SaveOrUpdate(category);

session.SaveOrUpdate(topSeller);

session.Transaction.Commit();

Выдает ошибку:

Оператор INSERT конфликтовал с ограничением FOREIGN KEY «FK_ProductTopSellersCategory_Categories». Конфликт произошел в базе данных "xxx", таблица "dbo.Categories", столбец "Id". Заявление было прекращено.

Я попытался максимально упростить этот пример. Я был бы очень признателен за помощь. Спасибо


person nfplee    schedule 15.07.2013    source источник
comment
Откуда взялся внешний ключ? Вы создали его вручную или он был сгенерирован Nhibernate? если вы создали его самостоятельно, опубликуйте DDL для ключа.   -  person TedOnTheNet    schedule 19.07.2013
comment
Я обновил свой вопрос схемой базы данных.   -  person nfplee    schedule 19.07.2013


Ответы (2)


У вас есть отношение «один ко многим» между Category и ProductTopSellerCategory с сопоставлением только многих сторон. Обычно вы должны использовать атрибут inverse в коллекции, сопоставленной с одной стороны, но у вас нет этого сопоставления, поэтому я предлагаю:

using (var txn = session.BeginTransaction())
{
   var category = new Category() { Name = "Test 1" };
   session.Save(category);
   session.Flush();

   var product = session.Get<Product>(1);
   var productTopSellerCategory = new ProductTopSellerCategory() { Product = product, Category = category };
   session.Save(productTopSellerCategory);
   txn.Commit();
}

Проблема с исходным кодом заключается в том, что NHibernate пытается вставить новую категорию ProductTopSellerCategory, а затем обновить категорию. Это происходит потому, что атрибут inverse не установлен. Принуждение NHibernate к вставке новой категории путем сброса сеанса должно решить проблему.

person Jamie Ide    schedule 20.07.2013
comment
Спасибо, я попытался выполнить сброс после вставки категории, но у меня возникла та же проблема. - person nfplee; 20.07.2013
comment
Я только что заметил настройки ReadOnly для ProductTopSellerCategory .Category и .Product. ReadOnly не позволит установить значение, и ваш исходный код может работать, когда вы его удалите. Если это не решит проблему, профилируйте свое приложение, чтобы точно видеть, что происходит. - person Jamie Ide; 20.07.2013
comment
Спасибо! Попробую в понедельник, пока занят. К вашему сведению, структура, которую я использую, взята из блога NHibernate. nhforge.org/blogs/nhibernate/ архив/2010/06/30/ - person nfplee; 20.07.2013
comment
Если я снимаю ReadOnly, я получаю сообщение об ошибке: Invalid index 3 for this SqlParameterCollection with Count=3. . Я также попытался добавить коллекцию TopSellers в категорию (с многократным сопоставлением и обратным), но у меня возникла та же проблема. Глядя на SQL, когда он пытается вставить лучший продавец, он имеет идентификатор категории 0. Дайте мне знать, если вам нужна дополнительная информация, чтобы помочь еще раз. Спасибо - person nfplee; 22.07.2013

Думаю, я нашел решение. Это немного хак, но это означало, что мне не нужно было менять свои сущности и сопоставления. Проблема возникает из-за того, что CategoryId в типе удостоверения не указывает на ту же ссылку, что и Category.Id в сущности самых продаваемых товаров. Чтобы решить эту проблему, мне нужно добавить следующее непосредственно перед тем, как я вставлю лидера продаж:

topSeller.Id.CategoryId = topSeller.Category.Id;
person nfplee    schedule 22.07.2013