Свободный Nhibernate: Попытка создать сущность с составным ключом, который также является ключами для двух ссылок - PullRequest
2 голосов
/ 29 августа 2011

Ссылки являются однонаправленными.Таблица (StoreProduct) для этого объекта на самом деле является таблицей соединений, которая имеет следующие поля:

  • Store_id
  • Product_id
  • ExtraBit

Итак, я пошел с сущностью, имеющей составной идентификатор (store_id и product_id), а ExtraBit - это просто строка:

public class StoreProduct
{
    protected StoreProduct():this(null,null,null){ }
    public StoreProduct(Store c_Store, Product c_Product, String c_ExtraBit)
    {
        Store = c_Store;
        Product = c_Product;
        ExtraBit = c_ExtraBit;
    } 
    public virtual int Product_id { get; set; }
    public virtual int Store_id { get; set; }
    public virtual Store Store { get; set; }
    public virtual Product Product { get; set; }
    public virtual String ExtraBit { get; set; }


    public override int GetHashCode()
    {
        return Store.GetHashCode() + Product.GetHashCode();
    }

    public override bool Equals(object obj)
    {
        StoreProduct obj_StoreProduct;
        obj_StoreProduct = obj as StoreProduct;
        if (obj_StoreProduct == null)
        {
            return false;
        }
        if (obj_StoreProduct.Product != this.Product && obj_StoreProduct.Store != this.Store)
        {
            return false;
        }
        return true;
    }

}

И отображение:

public class Order_DetailMap : ClassMap<StoreProduct>
{
    public Order_DetailMap()
    {
        Table("StoreProduct");
        LazyLoad();
        CompositeId().KeyProperty(x => x.Store_id).KeyProperty(x => x.Product_id);
        References(x => x.Store).ForeignKey("Store_id").Cascade.All();
        References(x => x.Product).ForeignKey("Product_id").Cascade.All();
        Map(x => x.ExtraBit);
    }
}

Это не работает, хотя, когда я попытался сохранить StoreProduct и его недавно созданный Магазин и продукт.Кто-нибудь может помочь?Вот некоторые результаты:

 Unhandled Exception: System.ArgumentOutOfRangeException: Index was out of range.
 Must be non-negative and less than the size of the collection.
Parameter name: index
   at System.ThrowHelper.ThrowArgumentOutOfRangeException()
   at System.Data.SQLite.SQLiteParameterCollection.GetParameter(Int32 index)
   at System.Data.Common.DbParameterCollection.System.Collections.IList.get_Item
(Int32 index)
   at NHibernate.Type.Int32Type.Set(IDbCommand rs, Object value, Int32 index) in
 d:\CSharp\NH\NH\nhibernate\src\NHibernate\Type\Int32Type.cs:line 60
   at NHibernate.Type.NullableType.NullSafeSet(IDbCommand cmd, Object value, Int
32 index) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Type\NullableType.cs:line
 180
   at NHibernate.Type.NullableType.NullSafeSet(IDbCommand st, Object value, Int3
2 index, ISessionImplementor session) in d:\CSharp\NH\NH\nhibernate\src\NHiberna
te\Type\NullableType.cs:line 139
   at NHibernate.Type.ComponentType.NullSafeSet(IDbCommand st, Object value, Int
32 begin, ISessionImplementor session) in d:\CSharp\NH\NH\nhibernate\src\NHibern
ate\Type\ComponentType.cs:line 221
   at NHibernate.Persister.Entity.AbstractEntityPersister.Dehydrate(Object id, O
bject[] fields, Object rowId, Boolean[] includeProperty, Boolean[][] includeColu
mns, Int32 table, IDbCommand statement, ISessionImplementor session, Int32 index
) in d:\CSharp\NH\NH\nhibernate\src\NHibernate\Persister\Entity\AbstractEntityPe
rsister.cs:line 2418

Редактировать: Благодаря приведенной ниже помощи у меня, кажется, есть достойное решение:

Отображение магазина и класс:

namespace compoundIDtest.Domain.Mappings
{
    public class StoreMap : ClassMap<Store>
    {
        public StoreMap()
        {
            Id(x => x.Id).Column("Store_id");
            Map(x => x.Name);
            HasMany(x => x.Staff)
              .Inverse()
              .Cascade.All();
            HasManyToMany(x => x.Products)
             .Cascade.All()
             .Table("StoreProduct");
        }
    }
}


namespace compoundIDtest.Domain.Entities
{
    public class Store
    {
        public virtual int Id { get; private set; }
        public virtual string Name { get; set; }
        public virtual IList<Product> Products { get; set; }
        public virtual IList<Employee> Staff { get; set; }
        public virtual IList<StoreProduct> StoreProducts { get; set; }

        public Store()
        {
            Products = new List<Product>();
            Staff = new List<Employee>();
        }

        public virtual void AddProduct(Product product)
        {
            product.StoresStockedIn.Add(this);
            Products.Add(product);
        }

        public virtual void AddEmployee(Employee employee)
        {
            employee.Store = this;
            Staff.Add(employee);
        }

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

        public override bool Equals(object obj)
        {
            Store obj_Store;
            obj_Store = obj as Store;
            if (obj_Store == null)
            {
                return false;
            }
            if (obj_Store.Name != this.Name)
            {
                return false;
            }
            return true;
        }




    }
}

Отображение продукта и класс

    namespace compoundIDtest.Domain.Mappings
    {
        public class ProductMap : ClassMap<Product>
        {
            public ProductMap()
            {
                Id(x => x.Id).Column("Product_id");
                Map(x => x.Name);
                Map(x => x.Price);
                HasManyToMany(x => x.StoresStockedIn)
                  .Cascade.All()
                  .Inverse()
                  .Table("StoreProduct");
            }
        }
    }


namespace compoundIDtest.Domain.Entities
{
    public class Product
    {
        public virtual int Id { get; private set; }
        public virtual string Name { get; set; }
        public virtual double Price { get; set; }
        public virtual IList<Store> StoresStockedIn { get; set; }
        public virtual IList<StoreProduct> StoreProducts { get; set; }
        public Product()
        {
            StoresStockedIn = new List<Store>();
            StoreProducts = new List<StoreProduct>();
        }


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

            public override bool Equals(object obj)
            {
                Product obj_Product;
                obj_Product = obj as Product;
                if (obj_Product == null)
                {
                    return false;
                }
                if (obj_Product.Name != this.Name)
                {
                    return false;
                }
                return true;
            }


        }
    }

И StoreProduct

namespace compoundIDtest.Domain.Mappings
{
    public class Order_DetailMap : ClassMap<StoreProduct>
    {
        public Order_DetailMap()
        {
            Table("StoreProduct");
            LazyLoad();
            CompositeId().KeyReference(x => x.Store, "Store_id").KeyReference(x => x.Product, "Product_id");
            References(x => x.Store, "Store_id").Not.Update().Not.Insert().Cascade.All();
            References(x => x.Product, "Product_id").Not.Update().Not.Insert().Cascade.All();
            Map(x => x.ExtraBit);
        }
    }

}

namespace compoundIDtest.Domain.Entities
{
    public class StoreProduct
    {
        public StoreProduct(){}

        public virtual Store Store { get; set; }
        public virtual Product Product { get; set; }
        public virtual String ExtraBit { get; set; }

        public override int GetHashCode()
        {

            if (this.ExtraBit != null)
            {
                return Store.GetHashCode() + Product.GetHashCode() + ExtraBit.GetHashCode();
            }

            return Store.GetHashCode() + Product.GetHashCode();

        }

        public override bool Equals(object obj)
        {
            StoreProduct obj_StoreProduct;
            obj_StoreProduct = obj as StoreProduct;
            if (obj_StoreProduct == null)
            {
                return false;
            }
            if (obj_StoreProduct.Product != this.Product && obj_StoreProduct.Store != this.Store && obj_StoreProduct.ExtraBit != this.ExtraBit)
            {
                return false;
            }
            return true;
        }

    }
}

А вот код для приложения, чтобы проверить выше:

using System;
using System.IO;
using compoundIDtest.Domain.Entities;
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;
using FluentNHibernate.Conventions;


namespace compoundIDtest
{
    class Program
    {
        private const string DbFile = "firstProgram.db";

        static void Main()
        {
            // create our NHibernate session factory
            var sessionFactory = CreateSessionFactory();

            using (var session = sessionFactory.OpenSession())
            {
                // populate the database
                using (var transaction = session.BeginTransaction())
                {
                    // create a couple of Stores each with some Products and Employees
                    var barginBasin = new Store { Name = "Bargin Basin" };
                    var superMart = new Store { Name = "SuperMart" };
                    var CornerShop = new Store { Name = "Corner Shop" };


                    var potatoes = new Product { Name = "Potatoes", Price = 3.60 };
                    var fish = new Product { Name = "Fish", Price = 4.49 };
                    var milk = new Product { Name = "Milk", Price = 0.79 };
                    var bread = new Product { Name = "Bread", Price = 1.29 };
                    var cheese = new Product { Name = "Cheese", Price = 2.10 };
                    var waffles = new Product { Name = "Waffles", Price = 2.41 };
                    var poison = new Product { Name = "Poison", Price = 1.50 };


                    var daisy = new Employee { FirstName = "Daisy", LastName = "Harrison" };
                    var jack = new Employee { FirstName = "Jack", LastName = "Torrance" };
                    var sue = new Employee { FirstName = "Sue", LastName = "Walkters" };
                    var bill = new Employee { FirstName = "Bill", LastName = "Taft" };
                    var joan = new Employee { FirstName = "Joan", LastName = "Pope" };

                    var storeproduct = new StoreProduct { Store = CornerShop, Product = poison, ExtraBit = "Extra Bit"}; 

                    //session.SaveOrUpdate(CornerShop);
                    //session.SaveOrUpdate(poison);

                    session.Save(storeproduct);


                    // add products to the stores, there's some crossover in the products in each
                    // store, because the store-product relationship is many-to-many
                    AddProductsToStore(barginBasin, potatoes, fish, milk, bread, cheese);
                    AddProductsToStore(superMart, bread, cheese, waffles);

                    // add employees to the stores, this relationship is a one-to-many, so one
                    // employee can only work at one store at a time
                    AddEmployeesToStore(barginBasin, daisy, jack, sue);
                    AddEmployeesToStore(superMart, bill, joan);

                    // save both stores, this saves everything else via cascading
                    session.SaveOrUpdate(barginBasin);
                    session.SaveOrUpdate(superMart);
                    //session.SaveOrUpdate(CornerShop);
                    //session.SaveOrUpdate(poison);
                    //session.SaveOrUpdate(storeproduct);

                    transaction.Commit();

                }
            }

            using (var session = sessionFactory.OpenSession())
            {
                // retreive all stores and display them
                using (var transaction = session.BeginTransaction())
                {
                    var products = session.CreateCriteria(typeof(Product))
                        .List<Product>();

                    foreach (var product in products)
                    {
                        product.Price = 100;
                        session.SaveOrUpdate(product);

                    }



                    var storeproducts = session.CreateCriteria(typeof(StoreProduct)).List<StoreProduct>();


                    foreach (StoreProduct storeproduct in storeproducts)
                    {

                        if (storeproduct.Store.Name == "SuperMart")
                        {
                            storeproduct.ExtraBit = "Thank you, come again";
                        }

                        session.SaveOrUpdate(storeproduct);

                    }

                    transaction.Commit();

                }
            }



            Console.ReadKey();
        }

        private static ISessionFactory CreateSessionFactory()
        {
            return Fluently.Configure()
                .Database(SQLiteConfiguration.Standard
                    .UsingFile(DbFile))
                .Mappings(m =>
                    m.FluentMappings.AddFromAssemblyOf<Program>())
                .ExposeConfiguration(BuildSchema)
                .BuildSessionFactory();
        }

        private static void BuildSchema(Configuration config)
        {
            // delete the existing db on each run
            if (File.Exists(DbFile))
                File.Delete(DbFile);

            // this NHibernate tool takes a configuration (with mapping info in)
            // and exports a database schema from it
            new SchemaExport(config)
                .Create(false, true);
        }

        private static void WriteStorePretty(Store store)
        {
            Console.WriteLine(store.Name);
            Console.WriteLine(" Products:");

            foreach (var product in store.Products)
            {
                Console.WriteLine(" " + product.Name);
            }

            Console.WriteLine(" Staff:");

            foreach (var employee in store.Staff)
            {
                Console.WriteLine(" " + employee.FirstName + " " + employee.LastName);
            }

            Console.WriteLine();
        }

        public static void AddProductsToStore(Store store, params Product[] products)
        {
            foreach (var product in products)
            {
                store.AddProduct(product);
            }
        }

        public static void AddEmployeesToStore(Store store, params Employee[] employees)
        {
            foreach (var employee in employees)
            {
                store.AddEmployee(employee);
            }
        }
    }
}

1 Ответ

4 голосов
/ 29 августа 2011

У меня было отображение, почти идентичное этому, и способ, которым я закончил отображение, был таким:

public class Order_DetailMap : ClassMap<StoreProduct>
{
    public Order_DetailMap()
    {
        Table("StoreProduct");
        CompositeId()
            .KeyReference(x => x.Store, "Store_id")
            .KeyReference(x => x.Product, "Product_id");

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

Внутри моих классов Store и Product у меня есть методы добавления и удаления, которые делают создание этого среднего класса почти невидимым. Пример ниже:

public class Store
{
    public IList<StoreProduct> StoreProducts { get; set; }

    //Other properties and Constructors     

    public virtual void AddProduct(Product productToAdd, string extraBit)
    {
        StoreProduct newStoreProduct = new StoreProduct(this, productToAdd, extraBit);
        storeProducts.Add(newStoreProduct);
    }
}

В дополнение к вышесказанному у меня были коллекции HasMany StoreProduct в моих классах Store и Product, для которых установлено значение Cascade.AllDeleteOrphan()

Мне никогда не удавалось отобразить StoreProduct так, чтобы, когда он был сохранен сам по себе, он создавал новый Store и новый Product. Я должен был в конечном итоге сопоставить это, как указано выше. Таким образом, ваши Store или Product должны будут существовать, прежде чем вы фактически создадите отношения (StoreProduct) между ними в зависимости от того, с какой стороны вы создаете свой новый StoreProduct.

Edit:

Вы также можете отобразить это так, чтобы достичь желаемого:

public class Order_DetailMap : ClassMap<StoreProduct>
{
    public Order_DetailMap()
    {
        Table("StoreProduct");
        CompositeId()
            .KeyReference(x => x.Store, "Store_id")
            .KeyReference(x => x.Product, "Product_id");

        References(x => x.Store, "Store_id")
            .Not.Update()
            .Not.Insert()
            .Cascade.All();

        References(x => x.Product, "Product_id")
            .Not.Update()
            .Not.Insert()
            .Cascade.All();

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