域模型/类是否可以返回self

本文关键字:返回 self 是否 模型 | 更新日期: 2023-09-27 18:05:22

在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以及各自的模型和服务类。你能建议一个更好的方法吗?

域模型/类是否可以返回self

拥有太多的类不应该比在您所定义的类中拥有太多的复杂性更令人担忧。保持类的职责简单。

理想情况下,poco不应该包含CRUD方法。在你的例子中,你的crud方法有一个实例方法,这意味着你必须实例化一个AccountModel来得到一个List<AccountModel>。这没什么意义,不是吗?

建立一个单独的接口,命名为IAccountModelBusiness之类的东西来获取你的实体。让该接口的实现接受IAccountModelDataAccessIAccountModelRepository,以将数据访问的关注点与数据管理和规则实施的关注点分开。

根据添加到问题中的示例代码,下面是该示例的一个版本,其中应用了此回答中提到的一些技术:

第一个类是控制器类(大概是为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实现将其移除并将默认值参数移动到组合根。有关如何做到这一点的详细信息,请参阅本文。这个类是一个瘦控制器,用于承载封装在另一个类中的实际功能。我们这样做的原因是因为。net web服务技术随着时间的推移而变化。在过去的15年里,我们有了主动服务方法服务(ASMX)、Windows通信(WCF)、ASP。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风格的单元测试,这将特别有用。

这里是一个dotnetfiddle,显示了该代码的可编译版本。