Architecture 工作范围单位
我有一个解决方案,前端使用webforms,管理控制台使用mvc 这两个UI都通过Ninject使用服务层,我在解决一个微妙但相当重要的问题时遇到了困难 假设我有一个CourseService,它返回一个基于字符串搜索词的课程列表-该服务返回搜索结果,但出于管理信息的目的,我还需要记录所做的搜索以及与该词匹配的课程数量 我一开始的想法是,在请求结束时,工作单元将由UI在page方法中提交,例如button click事件。这同样适用于控制器 这里的问题是,我依靠UI开发人员在工作单元上调用Commit(),以便记录搜索。UI开发人员可以在不调用commit的情况下愉快地继续进行,结果将被返回,但搜索不会被记录。这使我决定让服务层控制工作单元的范围。Ninject将自动将工作单元传递给服务层和存储库实现,这实际上就是我告诉Ninject按请求范围创建它的同一个实例 下面是一个如何编写我的图层的示例Architecture 工作范围单位,architecture,repository-pattern,ninject,unit-of-work,service-layer,Architecture,Repository Pattern,Ninject,Unit Of Work,Service Layer,我有一个解决方案,前端使用webforms,管理控制台使用mvc 这两个UI都通过Ninject使用服务层,我在解决一个微妙但相当重要的问题时遇到了困难 假设我有一个CourseService,它返回一个基于字符串搜索词的课程列表-该服务返回搜索结果,但出于管理信息的目的,我还需要记录所做的搜索以及与该词匹配的课程数量 我一开始的想法是,在请求结束时,工作单元将由UI在page方法中提交,例如button click事件。这同样适用于控制器 这里的问题是,我依靠UI开发人员在工作单元上调用Com
public class CourseService
{
private readonly ICourseRepository _repo;
public CourseService(ICourseRepository repo)
{
_repo = repo;
}
public IEnumerable<Course> FindCoursesBy(string searchTerm)
{
var courses = _repo.FindBy(searchTerm);
var log = string.format("search for '{1}' returned {0} courses",courses.Count(),searchTerm);
_repo.LogCourseSearch(log);
//IMO the service layer should be calling Commit() on IUnitOfWork here...
return courses;
}
}
public class EFCourseRepository : ICourseRepository
{
private readonly ObjectContext _context;
public EFCourseRepository(IUnitOfWork unitOfWork)
{
_context = (ObjectContext)unitOfWork;
}
public IEnumerable<Course> FindBy(string text)
{
var qry = from c in _context.CreateObjectSet<tblCourse>()
where c.CourseName.Contains(text)
select new Course()
{
Id = c.CourseId,
Name = c.CourseName
};
return qry.AsEnumerable();
}
public Course Register(string courseName)
{
var c = new tblCourse()
{
CourseName = courseName;
};
_context.AddObject(c);
//the repository needs to call SaveChanges to get the primary key of the newly created entry in tblCourse...
var createdCourse = new Course()
{
Id = c.CourseId,
Name = c.CourseName;
};
return createdCourse;
}
}
public class EFUnitOfWork : ObjectContext, IUnitOfWork
{
public EFUnitOfWork(string connectionString) : base(connectionString)
{}
public void Commit()
{
SaveChanges();
}
public object Context
{
get { return this; }
}
}
公共类课程服务
{
专用只读ICourseRepository(U repo);
公共课程服务(ICO)
{
_回购=回购;
}
公共IEnumerable FindCoursesBy(字符串搜索术语)
{
var课程=_repo.FindBy(搜索术语);
var log=string.format(“搜索{1}”返回{0}个课程”,courses.Count(),searchTerm);
_报告日志搜索(日志);
//在我看来,服务层应该在这里对IUnitOfWork调用Commit()。。。
返回课程;
}
}
公共类EFCourseRepository:ICourseRepository
{
私有只读ObjectContext\u上下文;
公共EFCourseRepository(IUnitOfWork unitOfWork)
{
_上下文=(ObjectContext)unitOfWork;
}
公共IEnumerable FindBy(字符串文本)
{
var qry=来自_context.CreateObjectSet()中的c
其中c.CourseName.Contains(文本)
选择新课程()
{
Id=c.CourseId,
Name=c.CourseName
};
返回qry.AsEnumerable();
}
公共课程注册(字符串courseName)
{
var c=新的tblCourse()
{
CourseName=CourseName;
};
_AddObject(c);
//存储库需要调用SaveChanges来获取tblCourse中新创建的条目的主键。。。
var createdCourse=新课程()
{
Id=c.CourseId,
Name=c.CourseName;
};
返回createdCourse;
}
}
公共类EFUnitOfWork:ObjectContext,IUnitOfWork
{
公共EFUnitOfWork(字符串connectionString):基(connectionString)
{}
公共无效提交()
{
保存更改();
}
公共对象上下文
{
获取{返回此;}
}
}
在上面的评论中,您可以看到我认为我“应该”提交更改的地方,但我觉得我可能忽略了一个更大的问题,即允许服务层和存储库实现控制事务的范围
除此之外,当我的存储库需要保存一个新对象,并将其原封不动地返回时,如果在返回对象后从UI调用Commit,则不会发生这种情况。因此,存储库有时确实需要管理工作单元
你能看到我的方法有什么直接的问题吗?这都是关于你工作单元的“边界”。逻辑操作的边界是什么?是UI代码隐藏/控制器还是服务层?我所说的边界是指谁定义了什么是工作单元?UI开发人员的责任是编排对单个工作单元的多个服务调用,还是服务开发人员的责任是公开每个包装单个工作单元的服务操作?这些问题应该会立即为您提供答案,您可以在哪里调用工作单元上的Commit
如果您的逻辑操作的边界是由UI开发人员定义的,那么您就不能这样做—永远不能。UI开发人员可以在调用您的方法之前进行一些未提交的更改,但您将在登录搜索后以静默方式提交这些更改!在这种情况下,日志操作必须使用自己的上下文/工作单元(而且它应该在当前事务之外运行),这将需要单独的ninject配置为每个调用创建新的UoW实例。如果逻辑操作的边界在服务中,则不应向UI开发人员公开工作单元-他不应能够与活动UoW实例交互
在您的实现中,我不喜欢的是对工作单元调用Register
Commit。再说一遍,边界在哪里?存储库操作是独立的工作单元吗?在这种情况下,为什么要使用服务层?如果您希望在一个工作单元中注册多个诅咒,或者希望课程注册成为更大工作单元的一部分,会发生什么情况?服务层负责调用Commit
。这一切可能源于这样一种想法,即您的存储库将把实体投影到自定义类型/DTO中——看起来存储库的责任太大,复杂性太高。特别是如果您可以使用POCOs(EFv4.x)
最后要提到的是,一旦您将服务操作作为工作单元的边界,您就会发现每个请求实例都是不够的。您可以拥有将在内部执行多个工作单元的web请求
最后。您关心UI开发人员的职责