我目前正在开发一个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);
}
}
所有描述的方法都有效并给我正确的结果 - 但是我想以正确的方式做到这一点 - 你怎么做 - 或者我完全是离开赛道; - )?
谢谢!
答案 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),这样你就不必重复代码,但是然后 - 代码可能看起来有点复杂。 :)