在哪里放置访问数据库的方法

时间:2012-05-11 11:31:27

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

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

我有一个Customer类和一个类项目。项目为其客户提供导航属性。

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);
        }
    }

所有描述的方法都有效并给我正确的结果 - 但是我想以正确的方式做到这一点 - 怎么做 - 或者我完全是离开赛道; - )?

谢谢!

1 个答案:

答案 0 :(得分:3)

以下是我的看法,只是为了回顾一下我在评论中所写的内容。我不能说这是“走的路”,但我想认为这是一个可行的解决方案。

因此,如果您使用非通用方法(对于我提到的)直接,易于管理和调试具有少量表的小型数据库,您可以按照以下方式执行:

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. 
}

Digression - 数据库上下文工厂看起来像这样,如果您愿意,例如稍后更改数据库连接:

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

当然,这样的简单设置适用于任何选择的IoC容器,我的是用于web应用程序的Autofac,其中包含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),这样你就不必重复代码,但是然后 - 代码可能看起来有点复杂。 :)

相关问题