none
EF Code First и проектирование интернет магазина RRS feed

  • Вопрос

  • Добрый день.

    В моем интернет магазине работает не замысловатая схема классов Сущность продукты и категории.

     public class Product
        {    
            public int ProductID { get; set; }
            [Display(Name="Наименование продукта")]
            public string Name { get; set; }
            [DisplayName("Описание")]
            [DataType(DataType.MultilineText)]
            public string Description { get; set; }
            [DisplayName("Стоимость")]
            public decimal Price { get; set; }
          ...
    
            [DisplayName("Категория")]
            public int CategoryID { get; set; }
            public virtual Category Category { get; set; }
    
        }
        public class Category
        {
            [ScaffoldColumn(false)]
            public int CategoryID { get; set; }
            ...
    
            public virtual ICollection<Product> Products { get; set; }
            public virtual ICollection<Documents> Documents { get; set; }
        }

    Все бы хорошо, но ВНЕЗАПНО, возникла необходимость в описание каждого товара отображать список "Необходимых" и "Дополнительных продуктов.

    Т.е по сути нужно что то в стиле  

     public class Product
        { 
         ....
     Product<List> DependProducts;
     Product<List> AdditionallyProducts}
    }

        Но. как это реализовать с учетом Code First не могу придумать, может, кто посоветует,  решение.

    20 июля 2012 г. 11:39

Ответы

  • Тогда подойдём с другой стороны. В C#, как сказал высше, моделирование множественных циклических связей на себя - не проблема. Проблемы возникают с физическим хранилищем, в данном случае это БД SQL Server. В реляционной базе нельзя смоделировать отношение многие ко многим без промежуточной таблицы, в C# можно. Поэтому нужно использовать отображение. "Или есть другой "Best Practices"?" - есть. "Единственный выход пока вижу в предложенной мной таблице и соответственно сущности для с набором следующих свойств." - нужна не одна, а две таблицы, одна на пару ключей, но сущностный класс будет один. Почему нужны две? Чтобы не иметь дублирование связей:

    ProductID       ProductDependID    ProductAdditionallyID

        1                             2                                4

        1                             2                               5  

    во второй строчке связь дублируется. Так как у одного продукта может быть только один ProductDepend и несколько ProductAdditionally или наоборот. А если сделать две таблицы, то этого не будет. В конкчном счёте всё будет выглядеть примерно так:

    namespace EFCodeFirstCircularReference
    {
      class Program
      {
        static void Main(string[] args)
        {
          Database.SetInitializer(new Initializer());
          MyDbContext db = new MyDbContext();
          db.Categories.Add(new Category { CategoryId = 1 });
          db.SaveChanges();
          var products = db.Products.Select(p => p).ToArray();
          foreach(var prod in products)
          {
            Console.WriteLine("Продукт: {0}\n", prod.ProductId);
            foreach (var add in prod.AdditionallyProducts)
            {
              Console.WriteLine("---Дополнительный: {0}\n", add.ProductId);
            }
    
          }
        }
      }
      public class Product
      {
        public int ProductId { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
        public int CategoryId { get; set; }
        public virtual Category Category { get; set; }
    
        //Список товаров которые являются необходимыми для текущего.
        public virtual List<Product> ParentDependProducts { get; set; }
        //Список товаров для которых теущий является необходимым.
        public virtual List<Product> DependProducts { get; set; }
    
        //Список товаров которые являются дополнительными для текущего.
        public virtual List<Product> ParentAdditionallyProducts { get; set; }
        //Список товаров для которых теущий является дополнительным.
        public virtual List<Product> AdditionallyProducts { get; set; }
      }
      public class Category
      {
        public int CategoryId { get; set; }
        public virtual ICollection<Product> Products { get; set; }
      }
      public class MyDbContext : DbContext
      {
        public DbSet<Product> Products { get; set; }
        public DbSet<Category> Categories { get; set; }
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
          modelBuilder.Entity<Product>().HasKey(t => t.ProductId);
          modelBuilder.Entity<Product>().HasMany(t => t.AdditionallyProducts)
            .WithMany(t => t.ParentAdditionallyProducts)
            .Map(c => { c.ToTable("ProdAddProd"); c.MapLeftKey("ProductId"); c.MapRightKey("AdditionalProductId"); });
          modelBuilder.Entity<Product>().HasMany(t => t.DependProducts)
            .WithMany(t => t.ParentDependProducts)
            .Map(c => { c.ToTable("ProdDepProd"); c.MapLeftKey("ProductId"); c.MapRightKey("DependProductId"); });
          base.OnModelCreating(modelBuilder);
        }
      }
      public class Initializer : IDatabaseInitializer<MyDbContext>
      {
    
        #region IDatabaseInitializer<MyDbContext> Members
    
        public void InitializeDatabase(MyDbContext context)
        {
          bool dbExist = context.Database.Exists();
          if (!dbExist)
          {
            context.Database.Create();
            context.SaveChanges();
          }
          else
            return;
        }
    
        #endregion
      }
    }

    А база такая:

    • Помечено в качестве ответа Shidlat1819 21 июля 2012 г. 9:29
    21 июля 2012 г. 8:51
    Модератор

Все ответы

  • В коде реализация подобных вещей не проблема, ведь это C#, Вы уже это сделали. У Вас не получается реализовать схему в БД?
    20 июля 2012 г. 11:51
    Модератор
  • Извиняюсь, но не совсем понял...я создаю сущности объектной модели а EF CF на основе моих классов и создает схем БД.

    Мне не понятно, во, что бы превратился бы (таблицы в бд) выше упомянутый код, если бы работал.

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

    Если бы я делал таблицу, то сделал бы так

     ProductID       ProductDependID    ProductAdditionallyID

        1                             2                                4

        1                             3                                5       

    Итого получили бы: продукт с IDшником 1 имеет зависимые продукты 2 и 3 и дополнительные 4 и 5

                   

    20 июля 2012 г. 12:11
  • Да, вопрос интересный получился, не часто бывают такие. "Мне не понятно, во, что бы превратился бы (таблицы в бд) выше упомянутый код, если бы работал." - в таблицу с циклическим внешним ключём. Вот реализация:

    using System.Collections.Generic;
    using System.Data.Entity;
    
    namespace EFCodeFirstCyclicalReference
    {
      class Program
      {
        static void Main(string[] args)
        {
          MyDbContext db = new MyDbContext();
          db.Categories.Add(new Category { CategoryId = 1 });
          db.SaveChanges();
        }
      }
      public class Product
      {
        public int ProductId { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
        public int CategoryId { get; set; }
        public virtual Category Category { get; set; }
    
        public int? ParentDependProductId { get; set; }
        public Product ParentDependProduct { get; set; }
        public virtual List<Product> DependProducts {get;set;}
    
        public int? ParentAdditionalProductId { get; set; }
        public Product ParentAdditionalProduct { get; set; }
        public virtual List<Product> AdditionallyProducts { get; set; }
    
      }
      public class Category
      {
        public int CategoryId { get; set; }
        public virtual ICollection<Product> Products { get; set; }
      }
      public class MyDbContext : DbContext
      {
        public DbSet<Product> Products { get; set; }
        public DbSet<Category> Categories { get; set; }
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
          modelBuilder.Entity<Product>().HasKey(t => t.ProductId);
          modelBuilder.Entity<Product>().HasOptional(t => t.ParentAdditionalProduct).WithMany(t => t.AdditionallyProducts).HasForeignKey(t => t.ParentAdditionalProductId);
          modelBuilder.Entity<Product>().HasOptional(t => t.ParentDependProduct).WithMany(t => t.DependProducts).HasForeignKey(t => t.ParentDependProductId);
          base.OnModelCreating(modelBuilder);
        }
      }

    А вот то, что получится:

    20 июля 2012 г. 13:43
    Модератор
  • Весьма интересное решение, но имеет один весомый недостаток - нельзя смоделировать следующие систему

    продукт - Астрал Отчет Оптимальный

    продукт - Астрал Отчет Максимальный

    для работы как первого так и второго нужен Крипто Про

    т.е. 2 продукта не могут одновременно зависеть от одного продукта т.к. ParentDependProductId перезаписывается.

    Единственный выход пока вижу в предложенной мной таблице и соответственно сущности для с набором следующих свойств.

    int ProductID {get;set;}
    int ProductDependID {get;set;}
    Int ProductAdditionall{get;set;}
    Или есть другой "Best Practices"?

    20 июля 2012 г. 19:56
  • Тогда подойдём с другой стороны. В C#, как сказал высше, моделирование множественных циклических связей на себя - не проблема. Проблемы возникают с физическим хранилищем, в данном случае это БД SQL Server. В реляционной базе нельзя смоделировать отношение многие ко многим без промежуточной таблицы, в C# можно. Поэтому нужно использовать отображение. "Или есть другой "Best Practices"?" - есть. "Единственный выход пока вижу в предложенной мной таблице и соответственно сущности для с набором следующих свойств." - нужна не одна, а две таблицы, одна на пару ключей, но сущностный класс будет один. Почему нужны две? Чтобы не иметь дублирование связей:

    ProductID       ProductDependID    ProductAdditionallyID

        1                             2                                4

        1                             2                               5  

    во второй строчке связь дублируется. Так как у одного продукта может быть только один ProductDepend и несколько ProductAdditionally или наоборот. А если сделать две таблицы, то этого не будет. В конкчном счёте всё будет выглядеть примерно так:

    namespace EFCodeFirstCircularReference
    {
      class Program
      {
        static void Main(string[] args)
        {
          Database.SetInitializer(new Initializer());
          MyDbContext db = new MyDbContext();
          db.Categories.Add(new Category { CategoryId = 1 });
          db.SaveChanges();
          var products = db.Products.Select(p => p).ToArray();
          foreach(var prod in products)
          {
            Console.WriteLine("Продукт: {0}\n", prod.ProductId);
            foreach (var add in prod.AdditionallyProducts)
            {
              Console.WriteLine("---Дополнительный: {0}\n", add.ProductId);
            }
    
          }
        }
      }
      public class Product
      {
        public int ProductId { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
        public int CategoryId { get; set; }
        public virtual Category Category { get; set; }
    
        //Список товаров которые являются необходимыми для текущего.
        public virtual List<Product> ParentDependProducts { get; set; }
        //Список товаров для которых теущий является необходимым.
        public virtual List<Product> DependProducts { get; set; }
    
        //Список товаров которые являются дополнительными для текущего.
        public virtual List<Product> ParentAdditionallyProducts { get; set; }
        //Список товаров для которых теущий является дополнительным.
        public virtual List<Product> AdditionallyProducts { get; set; }
      }
      public class Category
      {
        public int CategoryId { get; set; }
        public virtual ICollection<Product> Products { get; set; }
      }
      public class MyDbContext : DbContext
      {
        public DbSet<Product> Products { get; set; }
        public DbSet<Category> Categories { get; set; }
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
          modelBuilder.Entity<Product>().HasKey(t => t.ProductId);
          modelBuilder.Entity<Product>().HasMany(t => t.AdditionallyProducts)
            .WithMany(t => t.ParentAdditionallyProducts)
            .Map(c => { c.ToTable("ProdAddProd"); c.MapLeftKey("ProductId"); c.MapRightKey("AdditionalProductId"); });
          modelBuilder.Entity<Product>().HasMany(t => t.DependProducts)
            .WithMany(t => t.ParentDependProducts)
            .Map(c => { c.ToTable("ProdDepProd"); c.MapLeftKey("ProductId"); c.MapRightKey("DependProductId"); });
          base.OnModelCreating(modelBuilder);
        }
      }
      public class Initializer : IDatabaseInitializer<MyDbContext>
      {
    
        #region IDatabaseInitializer<MyDbContext> Members
    
        public void InitializeDatabase(MyDbContext context)
        {
          bool dbExist = context.Database.Exists();
          if (!dbExist)
          {
            context.Database.Create();
            context.SaveChanges();
          }
          else
            return;
        }
    
        #endregion
      }
    }

    А база такая:

    • Помечено в качестве ответа Shidlat1819 21 июля 2012 г. 9:29
    21 июля 2012 г. 8:51
    Модератор
  • Благодарю за содействие
    21 июля 2012 г. 9:30
  • Спасибо за интересный вопрос. Если что, обращайтесь.
    21 июля 2012 г. 9:38
    Модератор