【问题标题】:Where to put methods that access the database在哪里放置访问数据库的方法
【发布时间】:2013-04-11 06:48:15
【问题描述】:

我目前正在开发一个 ASP .NET MVC 3 Web 应用程序作为原型,其中 Code First 作为数据访问层。现在我有点困惑把我的代码放在哪里。

我有一个班级客户和一个班级项目。项目对其客户具有导航属性。

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Project
{
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual Customer Customer { get; set; }
}

我使用这两个类通过 codefirst 进行数据访问。

public class MyContext : DbContext
{
    public DbSet<Customer> Customers { get; set; }
    public DbSet<Project> Projects { get; set; }
}

我了解到最好在我的 MVC 视图中直接使用这些数据访问类 - 所以我为单个客户创建了一个 ViewModel (CustomerListModel) 和一个 DTO。

public class CustomerDto
{        
    public int Id { get; set; }
    public string Name { get; set; }
    public int ProjectCount { get; set; } <== this property is in question
}

public class CustomerListModel
{
    public List<CustomerDto> Customers;
}

在我的控制器中,我从执行实际数据访问的(服务)类中获取客户。

public class CustomerService : IDisposable
{
    private MyContext db;
    public CustomerService()
    {
        db = new MyContext();
    }

    public IEnumerable<Customer> GetAllCustomers()
    {
        return db.Customers.ToList<Customer>();
    }
}

在我的控制器中,我调用获取所有客户的方法。

    public ViewResult Index()
    {
        //Mapper.CreateMap<Customer, CustomerDto>();

        var customerList = new List<CustomerDto>();
        foreach (var customer in rep.GetAllCustomers())
        {
            var cust = new CustomerDto();
            cust.Id = customer.Id;
            cust.Name = customer.Name;
            cust.Rate = customer.Rate;
==> cust.ProjectCount = customer.ProjectCount; <=====
            customerList.Add(cust);
        }

        var viewModel = new CustomerListModel()
        {                
            Customers = customerList //Mapper.Map<IEnumerable<Customer>, List<CustomerDto>>(rep.GetAllCustomers())
        };
        return View(viewModel);            
    }

我要问的是 - 例如,将单个客户的 ProjectCount 放在哪里。我可以把它放到客户类中

    public int ProjectCount
    {
       var db = new MyContext();
       return db.Projects.Where(x => x.Customer.Id == this.Id).Count();
    }

...但是我会有两个可以访问数据的地方 - 服务类和客户类。

我也可以将此代码放在我的 ServiceClass 中:

    public int GetProjectCount(Customer customer)
    {
        return db.Projects.Where(x => x.Customer.Id == customer.Id).Count();
    }

...但后来我不得不从控制器调用它:

        foreach (var customer in rep.GetAllCustomers())
        {
            var cust = new CustomerDto();
            cust.Id = customer.Id;
            cust.Name = customer.Name;
            cust.Rate = customer.Rate;
            cust.ProjectCount = rep.GetProjectCount(customer); <==
            customerList.Add(cust);
        }

...我也可以从客户类的 getter 中从我的服务类调用此方法。

    public int ProjectCount
    {
        get
        {
            return new CustomerService().GetProjectCount(this);
        }
    }

所有描述的方法都有效并给了我正确的结果 - 但是我想以正确的方式做 - 将如何做 - 或者我完全偏离轨道;-)?

谢谢!

【问题讨论】:

  • 我会说 - 通常可以,最好创建一个接口ICustomerService(CustomerService 将实现该接口),然后将此接口与 DI 一起使用。如果您正在使用非通用存储库方法(如果您有大量表,这可能会有点麻烦,但对于较小的数据库,它非常简单,易于管理和调试),我会将GetProjectCount(int customerId) 作为接口中的方法。
  • 只是为了澄清我对您的理解正确:从 ICustomerService 实现 CustomerService - 在 CustomerService 中实现 GetProjectCount 方法,然后注入什么(DbContext?)?然后使用我的控制器中的 GetProjectCount 我提取数据?
  • 注入整个ICustomerService。在 Autofac 中,它看起来像这样(Global.asax):var builder = new ContainerBuilder(); builder.Register(service =&gt; new CustomerService()).As&lt;ICustomerService&gt;().InstancePerHttpRequest(); ... 。然后在控制器的构造函数中传递ICustomerService 作为参数,将其分配给控制器的成员,瞧,您可以使用存储库。 :) 是的,那么您可以使用该成员在控制器中调用 GetProjectCount。

标签: c# asp.net-mvc model-view-controller architecture code-first


【解决方案1】:

以下是我对它的看法,只是为了回顾一下我在 cmets 中写的内容。我不能说这是否是“可行的方法”,但我想这是一个可行的解决方案。

因此,如果您使用的是非泛型方法(正如我所提到的),对于具有少量表的小型数据库来说,这种方法简单、易于管理和调试,您可以按照以下方式进行:

public interface ICustomerService
{
    IEnumerable<Customer> GetAllCustomers();
    // and other methods you'd like to use
}

public sealed class CustomerService : ICustomerService
{
    private YourContext context = new YourContext();
    // or, alternatively, even something along these lines
    private YourContext context;
    public CustomerService()
    {
        context = ContextFactory.GetDBContext();
    }
    //----

    public IEnumerable<Customer> GetAllCustomers()
    {
         // here goes the implementation
    }
}

public class YourController : Controller
{
    private ICustomerService customerService;
    public YourController(ICustomerService service)
    {
          customerService = service;
    }
    // and now you can call customerService.GetAllCustomers() or whatever other methods you put in the interface. 
}

题外话 - 数据库上下文工厂可能看起来像这样,以防你想例如稍后更改数据库连接:

public static ContextFactory
{
    public static YourContext GetDBContext()
    {
         return new YourContext();
    }        
}

当然,这种简单的设置适用于任何选择的 IoC 容器,我的是 Autofac for web-apps,它位于 Global.asax Application_Start

var builder = new ContainerBuilder();
    builder.Register(service => new CustomerService()).As<ICustomerService>().InstancePerHttpRequest();
    builder.RegisterControllers(typeof(MvcApplication).Assembly);
    var container = builder.Build();
    DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

依赖注入使得稍后测试控制器变得更容易,因为您可以使用许多模拟助手之一模拟数据库访问(例如准备好的项目列表)。

但是,如果您的大多数存储库的方法都相似,您可能需要查看更多 general solution (it's for database-first, but can be applied to code-first too),这将更适合您的需求,因此您不必重复代码,但是 -代码可能看起来有点复杂。 :)

【讨论】:

  • 正确的 SoC,IoC,都在这里。这也是我构建的方式,它还没有让我失望。当您需要添加交叉关注的功能时,我也保存了几次培根。
  • 感谢您的 cmets 以及您在回答中的准确描述 - 我会按照您描述的方式尝试。
猜你喜欢
  • 1970-01-01
  • 2010-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-12
  • 2010-12-10
  • 1970-01-01
  • 2012-11-11
相关资源
最近更新 更多