在MVC中,在域模型中移动逻辑是否有意义?我正在尝试减少太多的类,因为我的项目使用几个API来显示页面上显示的每个小数据。 例如
public class AccountModel{
public int Id {get;set;} //property
....
public List<AccountModel> GetAccounts(){ //method
....
}
}
否则,涉及太多API调用的好习惯是什么?
添加类似于我在项目中执行的示例代码结构。
public class TestController : Controller
{
public ActionResult Index(string id)
{
var testService = new TestService();
var testModel = new TestModel();
testModel.UserData = testService.GetTestData(id);
testModel.MenuList = testService.GetMenu(id);
testModel.UserItems = testService.GetItems(id);
return View(testModel);
}
}
-----------------------------------------------------------------
public class TestService
{
public TestModel GetTestData(string id)
{
TestModel testData = null;
try
{
using (HttpClient httpClient = new HttpClient())
{
string requestUri = "http://10.8.200.1/test/" + id;
HttpRequestMessage testRequest = new HttpRequestMessage(HttpMethod.Get, requestUri);
HttpResponseMessage response = httpClient.SendAsync(testRequest).Result;
if (response.IsSuccessStatusCode)
{
var jsonData = response.Content.ReadAsStringAsync().Result;
testData = JsonConvert.DeserializeObject<TestModel>(jsonData);
}
}
}
catch (Exception ex)
{
}
return testData;
}
public List<Menu> GetMenu(string id)
{
List<Menu> menus = null;
try
{
using (HttpClient httpClient = new HttpClient())
{
string requestUri = "http://10.8.200.1/menu/" + id;
HttpRequestMessage testRequest = new HttpRequestMessage(HttpMethod.Get, requestUri);
HttpResponseMessage response = httpClient.SendAsync(testRequest).Result;
if (response.IsSuccessStatusCode)
{
var jsonData = response.Content.ReadAsStringAsync().Result;
menus = JsonConvert.DeserializeObject<List<Menu>>(jsonData);
}
}
}
catch (Exception ex)
{
}
return menus;
}
public List<UserItem> GetItems(string id)
{
List<UserItem> items = null;
try
{
using (HttpClient httpClient = new HttpClient())
{
string requestUri = "http://10.8.200.1/items/" + id;
HttpRequestMessage testRequest = new HttpRequestMessage(HttpMethod.Get, requestUri);
HttpResponseMessage response = httpClient.SendAsync(testRequest).Result;
if (response.IsSuccessStatusCode)
{
var jsonData = response.Content.ReadAsStringAsync().Result;
items = JsonConvert.DeserializeObject<List<Menu>>(jsonData);
}
}
}
catch (Exception ex)
{
}
return items;
}
}
-----------------------------------------------------------------
public class TestModel
{
public string Name{get;set;}
public string PublisherId{get;set;}
public string AccountType{get;set;}
public UserData UserData {get;set;} //There will be a UserData model
public List<Menu> MenuList {get;set;} //There will be a Menu model
public List<UserItem> UserItems {get;set;} //There will be a UserItem model
}
----------------------------------------------------------------
同样,我有多个控制器和多个API以及各自的模型和服务类。你能建议一个更好的方法吗?
答案 0 :(得分:3)
有太多的课程不应该比你定义的课程中有太多的复杂性更重要。保持课程的职责简单。
理想情况下,您的POCO不应包含CRUD方法。在您的示例中,您的crud方法有一个实例方法,这意味着您必须实例化AccountModel
才能获得List<AccountModel>
。它没有多大意义吗?
构建一个名为IAccountModelBusiness
的单独界面以获取您的实体。该接口的实现接受IAccountModelDataAccess
或IAccountModelRepository
将数据访问的关注与数据管理和规则实施的关注分开。
<强>更新强>
根据添加到问题中的示例代码,以下是该示例的一个版本,其中应用了此答案中提到的一些技术:
这第一个类是控制器类(可能是为ASP.Net MVC构建的):
public class TestController : Controller
{
private ITestAPI api;
#warning If you want, implement and register an IHttpControllerActivator to inject the IAPI implementation and remove this constructor if you want to have DI all the way up your stack.
public TestController() : this(new TestAPI(new UserDataService("http://10.8.200.1/test/"), new MenuService("http://10.8.200.1/menu/"), new UserItemsService("http://10.8.200.1/items/"))) {}
public TestController(ITestAPI api) { this.api = api; }
public ActionResult Index(string id)
{
return View(this.api.GetTestModel(id));
}
}
上面的控制器类有一个默认构造函数,我建议使用IHttpControllerActivator
实现删除并将默认值参数移动到组合根。查看this article了解有关如何执行此操作的详细信息。此类旨在成为一个瘦控制器,用于托管封装在另一个类中的实际功能。我们这样做的原因是因为.Net Web服务技术随着时间而变化。在过去的15年中,我们拥有主动服务方法服务(ASMX),Windows通信(WCF),ASP.Net MVC和ASP.Net Web API。这不包括第三方服务库,例如ServiceStack和NancyFX。此设计将主机责任与主查询和汇编逻辑分开。
下一个界面用于定义控制器托管和公开的方法:
public interface ITestAPI
{
TestModel GetTestModel(string id);
}
下一课程是提供要托管和公开的方法的实现:
public class TestAPI : ITestAPI
{
private IUserDataService userDataService;
private IMenuService menuService;
private IUserItemsService userItemsService;
public TestAPI(IUserDataService userDataService, IMenuService menuService, IUserItemsService userItemsService)
{
this.userDataService = userDataService;
this.menuService = menuService;
this.userItemsService = userItemsService;
}
public TestModel GetTestModel(string id)
{
var testModel = new TestModel();
testModel.UserData = this.userDataService.GetUserData(id);
testModel.MenuList = this.menuService.GetMenus(id);
testModel.UserItems = this.userItemsService.GetUserItems(id);
return testModel;
}
}
请注意,此实现依赖于服务代理来获取用户数据,菜单和用户项,因此使用三个构造函数参数定义它们以注入这些依赖项。
以下是TestAPI
类所需的依赖项的接口:
public interface IUserDataService { UserData GetUserData(string id); }
public interface IMenuService { List<Menu> GetMenus(string id); }
public interface IUserItemsService { List<UserItem> GetUserItems(string id); }
这是一个抽象服务客户端类,它以通用形式实现定期的惯用服务客户端代码:
public abstract class BaseHttpServiceClient<TEntity, TPrimaryKey> where TEntity : class
{
private string remoteUri;
protected BaseHttpServiceClient(string remoteUri) { this.remoteUri = remoteUri; }
protected virtual TEntity GetRemoteItem(TPrimaryKey id)
{
TEntity testData = null;
try
{
using (HttpClient httpClient = new HttpClient())
{
string requestUri = this.remoteUri + id.ToString();
HttpRequestMessage testRequest = new HttpRequestMessage(HttpMethod.Get, requestUri);
HttpResponseMessage response = httpClient.SendAsync(testRequest).Result;
if (response.IsSuccessStatusCode)
{
var jsonData = response.Content.ReadAsStringAsync().Result;
testData = JsonConvert.DeserializeObject<TEntity>(jsonData);
}
}
}
catch (Exception ex)
{
}
return testData;
}
}
这里有三个API
类所需依赖项的实现类。请注意,它们都派生自BaseHttpServiceClient<TEntity, TPrimaryKey>
抽象类,并提供它们所服务的类型以及每种类型的主要id属性的数据类型作为基类的类型参数。
public class UserDataService : BaseHttpServiceClient<UserData, string>, IUserDataService
{
public UserDataService(string remoteUri) : base(remoteUri) {}
public UserData GetUserData(string id) { return this.GetRemoteItem(id); }
}
public class MenuService : BaseHttpServiceClient<List<Menu>, string>, IMenuService
{
public MenuService(string remoteUri) : base(remoteUri) {}
public List<Menu> GetMenus(string id) { return this.GetRemoteItem(id); }
}
public class UserItemsService : BaseHttpServiceClient<List<UserItem>, string>, IUserItemsService
{
public UserItemsService(string remoteUri) : base(remoteUri) {}
public List<UserItem> GetUserItems(string id) { return this.GetRemoteItem(id); }
}
每个Service类使用GetRemoteItem
方法检索并返回绑定数据对象类型的实例。
以下是其他课程:
public class TestModel
{
public string Name{get;set;}
public string PublisherId{get;set;}
public string AccountType{get;set;}
public UserData UserData {get;set;} //There will be a UserData model
public List<Menu> MenuList {get;set;} //There will be a Menu model
public List<UserItem> UserItems {get;set;} //There will be a UserItem model
}
public class UserData {}
public class Menu {}
public class UserItem {}
总的来说,上面的代码网在您的示例版本中添加了大约8行代码,但它增加了两层依赖注入,并且能够根据需要交换不同的实现,例如用于单元测试的模拟。如果您使用 mockist 按照 solitary tests 样式进行单元测试,这将非常有用。
以下是显示此代码的可编译版本的dotnetfiddle。