具有计算属性的实体框架

时间:2012-12-14 16:47:39

标签: c# entity-framework

我希望确定完成以下内容的最干净/最佳做法:

我使用EF,代码优先,存储库模式。我有一个名为Project的实体,它有一个描述项目的标签或关键字列表:

public class Tag {
   public int TagID { get; set; }
   public int ProjectID { get; set; }
   public string TagValue { get; set; }
}  

我在强类型Razor视图中使用此数据,并在Project标题下方显示Tags。执行此操作时,我希望每个标记指示它在数据库中出现的次数,如:

  

一些项目标题

     

C#(5),实体框架(17)

在我看来,最好的计划是向实体添加计算属性:

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() }}
}

或者,我正在考虑在控制器中填充TagCount属性,调用TagRepository中的方法,如:

public int countTags(string TagValue);

意识到我可能完全忽视了这里的大局,我的问题是:这些方法中的任何一种都在正确的轨道上吗?

更新:按要求添加项目模型。

摘要项目:

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; }
}

已实施项目类型:

public class Scientific : Project {
    public int ScientificProjectNumber { get; set; }
    public string FileNumber { get; set; }
    public int Year { get; set; }
    public DateTime StartDate { get; set; }

}

更新 我的解决方案基于Brian Cauthon的回答是:

- 我继承了我正在扩展的类,而不是创建一个新的网络类: 注意:我必须创建一个额外的属性TagsView,因为我无法覆盖Scientific:Project类的虚拟属性。

public class ScientificView : Scientific {
    public virtual ICollection<TagView> TagsView { get; set; }
}

- 我为TagView做了同样的事情,扩展了类而不是复制它 - 希望在这些对象发展过程中为我节省一些努力:

    public class TagView : Tag {
    public int TagCount { get; set; }
}

- 增加了建议的TagRepository方法:

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全部从Controller执行。感谢您提供优秀AutoMapper项目的链接:

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);
    }

2 个答案:

答案 0 :(得分:1)

对于实际问题,我个人会说知道如何实现业务逻辑并不是模型的业务,而是让模型上的方法执行业务逻辑。如果标签计数是从模型外部计算的(即从存储过程中),那么我可能会沿着你已经开始的路线前进,但是将其定义为计算列(discussed here

然而,对于您要尝试实现的目标可能会产生问题,因为您可能会为数据库调用数百个时间来获取相同数据_context.Tags("").Count(),因为每个项目中加载的每个Tag都将执行此调用。

答案 1 :(得分:1)

我会从标记库中提取计数。由于您可能会一次为多个标签提取计数,因此我会将您的存储库方法更改为同时提取所有标记。

我会为您的视图创建特定的视图模型,然后使用AutomapperProjectTag映射到它们。

public class ProjectView {
    public int ProjectID { get; set; }
    public string Title { get; set; }
    public string Description { get; set; }
    public virtual ICollection<TagView> Tags { get; set; }
}

public class ProjectTagView {
   public int TagID { get; set; }
   public string TagValue { get; set; }
   public int TagCount { get; set; }
}

然后在你的控制器中,你会有这样的东西。

var model = AutoMapper.Map<Project,ProjectView>(project);
Dictionary<string,int> tagCounts = tagRepository.getCounts(model.Tags.Select(t=>t.TagValue));
foreach(var t in model.Tags){
    t.TagCount = tagCounts[t.TagValue];
}
相关问题