I am looking to determine the cleanest / best practice way to complete the following:
I am using EF, code first, repository pattern. I have an Entity called Project which in turn has a list of Tags or Keywords which describe the project:
public class Tag {
public int TagID { get; set; }
public int ProjectID { get; set; }
public string TagValue { get; set; }
}
I am using this data in a strongly typed Razor view, and am displaying the Tags below the Project title. When doing this I want each tag to indicate how many times it appears in the data base like:
Some Project Title
C# (5), Entity Framework (17)
It seemed to me the best plan would be to add a calculated property to the entity:
public class Tag {
public int TagID { get; set; }
public int ProjectID { get; set; }
public string TagValue { get; set; }
public int TagCount { get { _context.Tags("Some Filter on TagValue").Count() }}
}
Alternately, I was considering populating the TagCount property in the controller calling a method in the TagRepository like:
public int countTags(string TagValue);
Realizing I may be missing the bigger picture here altogether, my question is: Are either of these approaches on the right track?
Update: Adding the Project model as requested.
Abstract Project:
public abstract class Project {
public int ProjectID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
Implemented Type of Project:
public class Scientific : Project {
public int ScientificProjectNumber { get; set; }
public string FileNumber { get; set; }
public int Year { get; set; }
public DateTime StartDate { get; set; }
}
UPDATE:
My solution based on Brian Cauthon’s answer was to do the following:
-Instead of creating a net-new class, I inherited the ones I was extending:
Note: I had to create an additional property TagsView as I could not override the virtual property on the Scientific : Project class.
public class ScientificView : Scientific {
public virtual ICollection<TagView> TagsView { get; set; }
}
-I did the same for TagView, extending the class rather than copying it – hopefully saving me some effort as these object evolve:
public class TagView : Tag {
public int TagCount { get; set; }
}
-Added the suggested TagRepository Method:
public Dictionary<string, int> countTags(IEnumerable<string> tags) {
Dictionary<string, int> result = new Dictionary<string, int>();
var query = from o in _context.Tags
group o by o.TagValue into tagGroup
select new {TagText = tagGroup.Key,
TagCount = tagGroup.Count()};
foreach (var tagGroup in query) {
result.Add(tagGroup.TagText, tagGroup.TagCount);
}
return result;
}
-Updated executed all from the Controller. Thank you for the link to the excellent AutoMapper project:
public ViewResult Scientific(int projectID) {
Scientific project= _scientificRepository.Scientific.FirstOrDefault(l => l.ProjectID == projectID);
Dictionary<string, int> tagCounts = _tagRepository.countTags(project.Tags.Select(t => t.TagValue));
Mapper.CreateMap<Scientific, ScientificView>();
ScientificView sv = Mapper.Map<Scientific, ScientificView>(project);
Mapper.CreateMap<Tag, TagView>();
sv.TagsView = new List<TagView>();
foreach (Tag t in project.Tags) {
TagView tv = Mapper.Map<Tag, TagView>(t);
tv.TagCount = tagCounts[tv.TagValue];
sv.TagsView.Add(tv);
}
return View(sv);
}
I would go with pulling the count from the tag repository. Since you are likely to be pulling counts for multiple tags at a time I would change your repository method to pull all at once.
I would create specific viewmodels for your view and then map your
ProjectandTagto them with Automapper.Then in your controller you would have something like this.