Reputation: 143
I have a class named Question in which I would like to have some tags from dropdown list for easier searching, tags would be in separate class because I would like to add them separately.
First class Question:
public int Id { get; set; }
public string Description { get; set; }
Second class Tag:
public int Id { get; set; }
public string Name { get; set; }
What is the best way to implement that? Something like when posting question here on SO and adding tags below.
I'm using C# MVC5 with entity-framework for now. Many-to-many relationship table maybe?
Upvotes: 2
Views: 667
Reputation: 30474
So you have some Questions, and every Question has zero or more Tags. Similarly, the same Tag may be used by zero or more Questions.
This is a true many-to-many relationship. Normally you would configure an entity framework many-to-many relationship as following
class Question
{
public int Id {get; set;} // will become primary key
// every Question has zero or more Tags
public virtual ICollection<Tag> Tags {get; set;}
// other properties:
public string Text {get; set;}
...
}
class Tag
{
public int Id {get; set;} // will become primary key
// every Tag is used by zero or more Questions
public virtual ICollection<Question> Questions {get; set;}
// other properties:
public string Text {get; set;}
...
}
public MyDbContext : DbContext
{
public DbSet<Question> Questions {get; set;}
public DbSet<Tag> Tags {get; set;}
}
This is all that entity framework needs to know that you meant to configure a many-to-many relationship. Entity Framework will create and maintain the proper junction table with the correct foreign keys for you. You won't use this junction table, you will use the ICollections.
One extra: you probably don't want two Tags with the same name, so let's guarantee uniqueness by putting them in an Index.
in your DbContext.OnModelCreating:
public overrid void OnModelCreating(...)
{
// The Tag.Text must be unique, we put it in an Index
modelBuilder.Entity<Tag>()
.Property(tag => tag.Text)
.HasUniqueIndexAnnotation("IndexTagText", 0)
}
Every time the Tag.Text changes, the index is updated and it is checked whether the Updated Text is not already used. Another advantage: lookup by Text has become faster
Now you want a Dropdown list that contains all existing Tags:
var allTags = myDbContext.Tags
.Select(tag => new DisplayableTag()
{
TagId = tag.Id,
Display = tag.Text,
})
.OrderBy(tag => tag.Text) // an ordered drop down look nicer
.ToList();
The creatin of the DrowDown is probably done by visual studio designer. Code will be like:
// create a BindingSource that binds DisplayableTag objects:
BindingSource bindingSource1 = new BindingSource(this.components);
bindingSource1.DataSource = typeof(DisplayableTag);
// create a ListBox (or if you want: a dropdown ComboBox)
// bind it to the bindingSource
ListBox listBox = new ListBox();
listBox.DataSource = bindingSource1;
listBox.DisplayMember = "Display";
listBox.SelectionMode = SelectionMode.MultiSimple; // multiple tags can be selected
Now, whenever an item from the BindingSource needs to be displayed, the value of DisplayableTag.Display will be shown, which is the original Tag.Text
Now suppose the operator selects several Displayed tags and presses the Questions button to display all Questions with these Tags:
private void button1_Click(object sender, EventArgs e)
{
IEnumerable<int> selectedTagIds = this.ListBox1.SelectedItems
.Select(item => item.TagId);
this.DisplayQuestionsWithTags(selectedTagIds);
}
Upvotes: 1
Reputation: 469
Modify your Question
class as below:
public int Id { get; set; }
public string Description { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
By adding public virtual ICollection<Tag> Tags { get; set; }
in the class, you can add the list of selected tags against any question.
Upvotes: 2