在MVC控制器中添加linq-entities关联

时间:2009-03-31 16:43:10

标签: asp.net-mvc entity-framework

在这个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参数传递给服务层并在那里做所有事情?验证怎么样?

2 个答案:

答案 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服务

最后,为了解决原始问题“获取用户,然后添加产品”,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规则是:如果您有两个实体,但需要对两者执行共同操作,请使用服务对两个实体进行操作。