在这个MVC tutorial中,它展示了如何使用Repository和Service层将Controller与数据库逻辑分离。然而,这个例子是一个非常简单的纯属性模型。协会怎么样?
假设Product
模型通过所有者关联与Users
表关联。控制器调用服务或存储库以获取用户列表,将其传递给View,然后View将用户显示为下拉列表。
然后在标准POST:product / Create上,控制器需要获取所选的userID,然后获取该id的User实体,并将其与要创建的Product关联。
if (!String.IsNullOrEmpty(Request["SelectedUserId"]))
{
int selectedUserId = Convert.ToInt16(Request["SelectedUserId"]);
newProduct.Owner = _productService.GetUserById(ownerId);
}
_productService.AddProduct(newProduct);
但是这个过程使控制器逻辑复杂化。更糟糕的是,如果我们需要验证关联(因为关联不会有OnChanging事件,我们不能在Model部分类中执行)。
我的问题是,有没有更好的方法来处理协会?是否更好地将Request参数传递给服务层并在那里做所有事情?验证怎么样?
答案 0 :(得分:0)
更新:(评论之后不是linq2sql)
在您的特定场景中(基于您的desc +您发布的代码),我肯定会将负载转移到服务层。这并不意味着将请求移至它。我会重构为:
string userIdStr = Request["SelectedUserId"];
var userId = !String.IsNullOrEmpty(userIdStr) ?
Convert.ToInt16(userIdStr) :
default(int?);
_productService.AddProduct(userId, newProduct);
我通常还有一个像T oNullable<int>()
这样的扩展方法:
var userId = Request["SelectedUserId"].ToNullable<int>();
_productService.AddProduct(userId, newProduct);
根据您对模型的重点,您可以拥有包含两个字段的内容,例如CustomerProduct。
如果您坚持使用linq2sql,则无需让用户在此方案中执行此操作。 Linq2sql将生成一个OwnerId属性,您可以使用该属性而无需使用db来获取它。
在处理复杂模型时,将模型与不同的子系统联系起来并不总是最好的方法。如果系统的某些部分适合其自己的有界上下文,则这一点更为重要。在这种情况下,上述情况更为重要,因为您希望有一个非常清晰和具体的位置来整合2个有界的上下文。
除了上述情况之外,有时候为这些类型的过程添加一些额外的胶水是合适的。考虑添加一个服务类来帮助您。这并不意味着一切都需要通过服务类或域对象不具有逻辑,它只适用于您需要不适合域对象的额外操作的特定场景或控制器。
答案 1 :(得分:0)
This is the perfect book你应该阅读有关如何正确建模(MVC中的“M”)的存储库,模型,并且他包括直接的Linq关联。作为奖励,他解决了如何使用他们期望的存储库自动连接所有自定义控制器。是的,它是为一个版本的ASP.NET MVC编写的,它是4个版本(在发布之前),但概念坚持你。该信息适用于所有MVC(或任何具有Linq的DDD项目)项目。
简而言之,本书介绍了DDD概念,以便将存储库注入自定义控制器。
public class ContentController : Controller
{
IContentRepository _repository;
public ContentController(IContentRepository repo)
{
_repository = repo;
}
public ActionResult Add(Content c)
{
_repository.Add(c);
_repository.SubmitChanges();
return View(c);
}
}
通过强制存储库进入构造函数,请注意此处使用的依赖项注入概念。现在,您可以在没有构造函数的情况下自行连接它。但是,当你进入一个大型项目(50多个存储库)时,使用控制容器的反转有助于为你创建这些对象,例如Castle Windsor(再次,在上面的100页书中)。 / p>
对于您的用例,您可以从用户存储库中轻松访问User对象,然后让其余部分跟进。
本书介绍了如何使用LinqToSql与EntityRef和EntitySet对用户和产品实体进行建模,因此您的所有关联都得到了尊重。这里写的代码太多了。基本上,通过UserID将User()对象链接到Product()对象,并使用Linq属性装饰每个实体:
[Table(Name="ut_Content")]
public class Content : EntityBase
{
[Column(IsPrimaryKey = true
, IsDbGenerated = true
, AutoSync = AutoSync.OnInsert)]
public int ContentID { get; set; }
[Column(CanBeNull = false)]
public string ContentKey { get; set; }
[Column(CanBeNull = false)]
public string Title { get; set; }
[Column]
public string Description { get; set; }
[Column]
public string Body { get; set; }
[Column]
public string BodyFormatted { get; set; }
}
他处理了不同的LinqToSql用例,并说明了为什么“首先创建模型,不使用Schema-Generated Linq对象”在这里工作。
而且,我认为这是一个很好的衡量标准的存储库示例:
public class ContentRepository : IContentRepository
{
private Table<Content> _contents;
public ContentRepository(string connectionString)
{
DataContext db = new DataContext(connectionString);
_contents = db.GetTable<Content>();
}
public void Add(Content entity)
{
_contents.InsertOnSubmit(entity);
}
}
请记住,Linq最棒的一点就是延迟执行功能。您没有在此处运行整个Linq表。您的查询被延迟,因此您可以使用Where和Joins以及order bys过滤您的IQueryable。只有在您执行查询时才会请求.ToList()或类似的方法。
最后,为了解决原始问题“获取用户,然后添加产品”,DDD(域驱动设计)手册提供了完美的解决方案:服务。
您在域中创建了一个UserProductService()类,它完全按照您上面的操作执行。但它的优势在于将业务逻辑抽象到您的域中,因此其他位置可以访问和使用相同的业务规则。
public class UserProductService
{
private IProductRepository _productRepository;
private IUserRepository _userRepository;
public UserProductService()
{
// this is where CastleWindsor can really help
_productRepository = new ProductRepository();
_userRepository = new UserRepository();
}
public void AddProduct(int userID, Product product)
{
User user = userRepository.GetUserByID(userID);
// your logic above, abstracted into a service
if (user.IsValid && product.IsValid)
{
product.Owner = user.UserID;
_productRepository.AddProduct(product);
}
}
}
服务的DDD规则是:如果您有两个实体,但需要对两者执行共同操作,请使用服务对两个实体进行操作。