Пакетно вмъкване - Външен ключ не работи

Опитвам се да направя пакетно вмъкване и не работи. Мислех, че това работи, но нещо изглежда се е повредило и ще съм благодарен, ако някой може да ми покаже какво.

Редактиране - Ето схемата на базата данни:

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 само с нанесена многостранна карта. Обикновено бихте използвали обратния атрибут на колекцията, нанесена от едната страна, но нямате това нанесено, така че предлагам:

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, след което актуализира категорията. Прави това, защото обратният атрибут не е зададен. Принуждаването на NHibernate да вмъкне новата категория чрез изчистване на сесията трябва да разреши проблема.

person Jamie Ide    schedule 20.07.2013
comment
Благодаря, опитах да направя флъш след вмъкване на категорията, но получавам същия проблем. - person nfplee; 20.07.2013
comment
Току-що забелязах настройките Само за четене на ProductTopSellerCategory .Category и .Product. ReadOnly няма да позволи стойността да бъде зададена и вашият оригинален код може да работи, когато го премахнете. Ако това не реши проблема, профилирайте приложението си, за да можете да видите точно какво се случва. - person Jamie Ide; 20.07.2013
comment
Благодаря, ще опитам в понеделник, тъй като съм вързан дотогава. FYI структурата, която използвам, идва от блога на 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