0

I can't wrap my mind around this issue and haven't found the correct search keys yet:

I would like to have several categories of items in which all items have specific attributes. Those attributes (text fields, dropdowns, or checkboxes) should be added to a category and I want to edit and save those attributes for each item.

I'm working with MVC 4 and code-first EF5. How can I implement this?

My first approach were several classes like Text, Dropdown that were inherited from an abstract Attribute class and a Category class like this:

public class Category
{
    [Key]
    public int CategoryId { get; set; }

    public string Name { get; set; }
    public string Description { get; set; }

    public virtual ICollection<Item> Items { get; set; }
    public virtual ICollection<Attribute> Attributes { get; set; } 
}

But then I had no idea to proceed. Am I on the right way or completely wrong? Can someone give me a few hints I can search for?

Edit

Ultimately I'm trying to build a list of hifi devices. Speakers have different attributes than amplifier and those have different attributes to tape recorders. I would like to give a unified look for the details of each device and pre-define specific attributes to that category with an additional free-for-all text area. Speaker XYZ is my item, Speaker my category and dB an attribute.

3
  • Am I the only one who did not understand your question? I don't quite understand what is the attribute or what is item or what is the relation between each other and with the category Commented Apr 11, 2013 at 13:19
  • @Fendy: I edited my question, does that make it clearer? Commented Apr 11, 2013 at 13:33
  • I have tried to answer it. Hope that my understanding is good enough. Commented Apr 11, 2013 at 13:54

3 Answers 3

1

Ok so this question is basically about the data design.

First, I assume that the rule is:

  1. One item has one category
  2. One category has many attributes
  3. One item has many attributes associated with the category

For rule no.1, it is good enough in your design. (simplified example)

public class Category{
  public IEnumerable<Item> Items{get;set;}
}
public class Item{
  public Category Category{get;set;}
}

Its clear enough.

For rule no.2, I think you should make a CategoryAttribute class. It holds the relation between one to many Category and Attribute. Basically, CategoryAttribute is a master, whereas the children will be ItemAttribute.

public class Category{
  public IEnumerable<CategoryAttribute> CategoryAttributes{get;set;}
}
public class CategoryAttribute{
  public Category Category{get;set;}
  public string CategoryName{get;set;}
  public string DefaultValue{get;set;} // maybe a default value for specific 
                                       // attribute, but it's up to you

  public IEnumerable<ItemAttribute> ItemAttributes{get;set;}
}

The IEnumerable<ItemAttribute> is the one to many relation between category attribute and item attribute.

For rule no.3, the the ItemAttribute described in rule no.2 will be represented attribute owned by each item.

public class Item{
  public IEnumerable<ItemAttribute> ItemAttributes{get;set;}
}
public class ItemAttribute{
  public Item Item {get;set;} // it owned by one attribute
  public CategoryAttribute{get;set;} // it owned by one category attribute
}

I don't quite sure about how to represent relation or primary and foreign key in code first. Hopefully I can enhance my answer if needed (and if I able). But hopefully my illustration about relations and the class designs for each objects.

Sign up to request clarification or add additional context in comments.

Comments

1

I think something like this may work for you...

public class Category
{
    public int CategoryId { get; set; }

    public string Name { get; set; }
    public string Description { get; set; }

    public virtual ICollection<Item> Items { get; set; }
    public virtual ICollection<Attribute> Attributes { get; set; }
}

public class Item
{
    public int ItemId { get; set; }

    public string Name { get; set; }
    public string Description { get; set; }

    public int CategoryId { get; set; }
    public Category Category { get; set; }
    public virtual ICollection<ItemAttribute> ItemAttributes { get; set; }
}

public class Attribute
{
    public int AttributeId { get; set; }

    public string Name { get; set; }
    public string Description { get; set; }

    public virtual ICollection<Category> Categories { get; set; }
    public virtual ICollection<ItemAttribute> ItemAttributes { get; set; }
}

public class ItemAttribute
{
    public int ItemId { get; set; }
    public int AttributeId { get; set; }

    public Item Item { get; set; }
    public Attribute Attribute { get; set; }

    public string Value { get; set; }
    public int ValueInt{ get; set; }
    // etc. 
}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<ItemAttribute>()
        .HasKey(x => new { x.ItemId, x.AttributeId });
    modelBuilder.Entity<ItemAttribute>()
        .HasRequired(x => x.Item)
        .WithMany(x => x.ItemAttributes)
        .HasForeignKey(x => x.ItemId)
        .WillCascadeOnDelete(false);
    modelBuilder.Entity<ItemAttribute>()
        .HasRequired(x => x.Attribute)
        .WithMany(x => x.ItemAttributes)
        .HasForeignKey(x => x.AttributeId)
        .WillCascadeOnDelete(false);
    // AttributeCategories is created for you - but you can do the same as ^ above to customize
    // just change 'ICollection<Category> Categories' to collection of 'ItemAttribute'
}

// use it like e.g.
var item = new Item { Name = "ItemTest", };
var attribute = new Attribute { Name = "attributeTest", };
item.ItemAttributes = new List<ItemAttribute> 
{
    new ItemAttribute { Item = item, Attribute = attribute, Value = "test", },
};
var category = new Category
{
    Name = "cat1",
    Items = new[]
    {
        item,
        new Item{ Name = "Item1", },
        new Item{ Name = "Item2", },
        new Item{ Name = "Item3", },
        new Item{ Name = "Item4", },
        new Item{ Name = "Item5", },
    },
    Attributes = new[]
    {
        attribute,
        new Attribute{ Name = "att1", },
        new Attribute{ Name = "att2", },
    }
};
db.Categories.Add(category);
db.SaveChanges();
var categories = db.Categories.ToList();

ItemAttribute is used to connect and store values.

And you're going to need to further adjust as per your requirements.

3 Comments

Thanks, that worked fine so far. Now I would like to have not only string Attributes, but additionally Dropdowns and Checkboxes. How could I extend to that?
yes, you still have some work to do - this is to point you in the right direction I hope. There is no straight answer there. You can add multiple fields, as I hinted - and have text, int, bool in the ItemAttribute - and use whichever you need, based on the type of attribute. You could try inheritance also - i.e. make ItemAttr be base and extend with couple attr types - but that's bound to lead to some problems. May work though (but I have no time atm to try that). I'd recommend keep it simple - add multiple 'nullable' fields for each 'primitive type' and you should be fine.
If you do try inheritance - then I'd recommend to keep it in TPH (no [Table()] e.g.) - as that has more chance of working (as it's actually all in the same table, like it is now). Check this detailed example (more than you need) I made. stackoverflow.com/questions/15885279/…. Try it out yourself - and post what you have managed - and I'll try to take a look at it.
1

I actually never worked with code first approach, but I can give you some idea about how this scenario can be handled...To me, it looks that Item is the major one instead of Category. So you can have this structure...

public class Category
{
    [Key]
    public int CategoryId { get; set; }
    public string CategoryName { get; set; }
    public string CategoryDescription { get; set; }
    // use attributes here if you want them for Category
    //public Dictionary<string, string> ItemnAttributes { get; set; }
}

public class MyItem
{
    [Key]
    public int ItemId { get; set; }
    public string ItemName { get; set; }
    public string ItemDescription { get; set; }
    public Category ItemnCatagory { get; set; }
    public Dictionary<string, string> ItemnAttributes { get; set; }
}

Hope this helps..

3 Comments

But then the attributes were assigned to each item, not a category. Creating a new item for a existing category would mean to duplicate the attributes from another item in the same category, wouldn't it?
Because you mentioned in the second line 'all items have specific attributes.'...but if you want to assosiate attributes with category, just put this property in 'Category' and remove from 'MyItem'..
Hey, I made a mess... i think using "Dictionary<string, string>" for you attributes is enough, instead of "List<Dictionary<string, string>>"...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.