实体存储库/服务模式,在哪里放置需要2个或更多服务的功能

时间:2014-04-14 09:18:15

标签: c# entity-framework entity repository-pattern

我为我的实体类型创建了一个通用存储库,用于处理检索,添加和删除数据。每个实体类型都有一个相应的Service类,它与通用存储库交互以处理所有数据访问。

然而,很多时候我需要根据多个服务检索数据,而且我永远不知道在哪里放置此代码。例如,下面是一些代码,它返回一个使用3种不同服务的电子邮件地址列表(“GetEmailTrackingAddressGroup”函数)。我把它放在“GroupService”中,但它也可以很容易地进入“UserService”。

 public class GroupService
{
    IRepository<Group> groupRepository;

    public GroupService(IRepository<Group> groupRepository)
    {
        this.groupRepository = groupRepository;
    }

    public Group GetById(int id)
    {
        return groupRepository.GetSingle(g => g.Id == id); 
    }

     public static List<string> GetEmailTrackingAddressesGroup(int instanceId, int groupId)
    {

        MyEntities entityContext = new MyEntities();

        UserGroupService userGroupService =
         new UserGroupService(new BaseRepoistory<UserGroup>(entityContext ));


        UserService userService =
         new UserService(new BaseRepoistory<User>(entityContext ));

        List<string> emails = new List<string>();

        Group productGroup = GetById(groupId);

        foreach (UserGroup userGroup in userGroupService.GetByGroupId(productGroup.Id))
        {
            if (userGroup.EmailTracking)
                emails.Add(userService.GetByUserId(userGroup.UserId).UserName);
        }

        return emails;
    }
}

我的问题是,如果您只是尝试选择最相关的服务并将代码放在那里并调用其中的其他相关服务,或者我应该创建一个处理数据访问的新类,当涉及多个服务时。例如,我已经为此类可能如下所示放置了代码。

 public class DataFunctions
{

     public static List<string> GetEmailTrackingAddressesGroup(int instanceId, int groupId)
    {
        MyEntities entityContext = new MyEntities();

        GroupService userGroupService =
         new GroupService(new BaseRepoistory<Group>(entityContext ));

        UserGroupService userGroupService =
         new UserGroupService(new BaseRepoistory<UserGroup>(entityContext ));

        UserService userService =
         new UserService(new BaseRepoistory<User>(entityContext ));

        List<string> emails = new List<string>();

        Group productGroup = GetById(groupId);

        foreach (UserGroup userGroup in userGroupService.GetByGroupId(productGroup.Id))
        {
            if (userGroup.EmailTracking)
                emails.Add(userService.GetByUserId(userGroup.UserId).UserName);
        }

        return emails;
    }
}

第二种方法似乎更有意义,因为这意味着每项服务将永远不会依赖其他服务,但我不确定我是否正确地采用这种方式。我对使用这个单独的类的一个担忧是它会变得非常大而且难以管理。

编辑 - 目前我已经提出了第三个解决方案,我认为它比我之前的两个更好但是我仍然不确定我是否正确管理这个问题。我创建了一个单独的“EmailService”,它将处理在我的主ASP.Net Web项目中处理电子邮件功能时所需的所有数据查询。

以下是此新课程的代码

  //Functionality realting to data needed when handling emails
public class EmailService
{
    MyEntities entityContext;

    AspUserService aspUserService;
    GroupService groupService;
    UserGroupService userGroupService;

    public EmailService()
    {
        entityContext = new MyEntities ();

        aspUserService = new AspUserService(new RepositoryBase<aspnet_Users>(entityContext));
        groupService = new GroupService(new RepositoryBase<Group>(entityContext));
        userGroupService = new UserGroupService(new RepositoryBase<UserGroup>(entityContext));
    }

    public List<string> GetEmailsForProductGroup(int groupId)
    {
        List<string> emails = new List<string>();

        Group productGroup = groupService.GetById(groupId);

        foreach (UserGroup userGroup in userGroupService.GetByGroupId(productGroup.Id))
        {
            if (userGroup.EmailTracking)
                emails.Add(aspUserService.GetByUserId(userGroup.UserId).UserName);
        }

        return emails;
    }
}

3 个答案:

答案 0 :(得分:0)

如果你要对以前从未见过的应用程序进行维护,你会期望它在哪里?由于它是“属于”一个群体的东西,我认为把它放在群组回购/服务中将是正确的方法。在您的示例中,我建议创建一个新的组存储库,扩展组的IRepository,并创建一个获取组对象的方法,包括连接实体。在扩展应用程序时,这将产生巨大的差异,因为您不必为每个子对象查询数据库(n + 1问题)。

答案 1 :(得分:0)

没有冒犯,但是你所有的方法都是因为以下原因而吮吸:紧密耦合和纠缠责任。

通用存储库是一种反模式,远离它。你的第一种方法几乎完成了存储库的工作,它是一个静态函数(为什么?!)。

服务不应与具体存储库耦合。所有依赖项都应该通过构造函数作为抽象注入。我觉得aspUsersService,GroupService和UserGroupService(差别????)实际上像存储库一样实现,因此它们没用。

实际上从我看来,你所有的服务都是实际的存储库。削减无用的代码,拥有一个直接使用EF的服务/存储库。

答案 2 :(得分:0)

看起来你在存储库和服务类之间有点混淆。您的服务类只是存储库。由于通用存储库模式,您最终必须以这种方式工作。这种模式实际上是一种反模式。一开始看起来很不错,直到你到达目的地为止。相反,您应该为处理该实体的所有CRUD操作的每个实体创建存储库。在这些存储库中,您将放置“GetEmailTrackingAddressesGroup”类型方法。然后,如果需要,您的服务层可以处理与多个存储库的交互。您的服务类不应具有存储库的硬编码实例。相反,您应该将存储库接口注入服务的构造函数。

以下是我将如何设置存储库以及与2个存储库交互的简单服务的示例。

public interface IUserRepository
{
    void Insert(User user);
    ...
    IEnumerable<User> GetByDepartmentId(int deptId);
}

public interface IContactLogRepository
{
    void Insert(ContactLog contactLog); 
}

public class EmailService
{
    private readonly IUserRepository _userRepo;
    private readonly IContactLogRepository _contactLogRepo;

    public EmailService(IUserRepository userRepo, IContactLogRepository contLogRepo) {
        _userRepo = userRepo;
        _contactLogRepo = contLogRepo;
    }

    public void EmailDepartment(int deptId, string message) {
        var employees = _userRepo.GetByDepartmentId(deptId);

        foreach (var emp in employees) {
            Email(emp.Email, message);

            _contactLogRepo.Insert(new ContactLog {
                EmployeeId = emp.Id,
                Message = message
            });
        }
    }

    private void Email(string address, string message) {
        ...
    }
}

因此,我们的存储库可以处理特定实体的CRUD操作 - 而不是我们的服务层。通用存储库模式迫使我们在服务中进行CRUD(至少是检索)。