首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Bob叔叔用于数据对象/实体/ LINQ查询的干净架构重构技术

Bob叔叔用于数据对象/实体/ LINQ查询的干净架构重构技术
EN

Software Engineering用户
提问于 2018-11-28 00:11:02
回答 2查看 763关注 0票数 0

我有相对简单的控制器逻辑和丑陋的对象创建部分。有些对象非常庞大,尽管控制器所做的只是返回对象,但它看起来很混乱,很难读懂。因此,我想问一问,怎样才能使这些案件变得更干净。我给你们举几个例子:

例如,请注意视图模型的创建:

代码语言:javascript
复制
public async Task<ActionResult> Edit(Guid id)
{
    if (id.IsValidGuid())
    {
        // Get User Details
        var userTask = ApiGatewayService.GetUserDetail(id, AuthService.BearerToken);
        var nationalitiesTask = ApiGatewayService.GetNationalityList(
            new FilterParameters(), 
            AuthService.BearerToken
        );
        var countriesTask = ApiGatewayService.GetCountryList(
            new FilterParameters(), 
            AuthService.BearerToken
        );
        await Task.WhenAll(userTask, nationalitiesTask, countriesTask);
        var user = userTask.Result;
        var nationalities = nationalitiesTask.Result;
        var countries = countriesTask.Result;
        if (user is null)
            return NotFound();
        var userAddress = user.PrimaryAddress;
        var userHomePhone = user.Phones.FirstOrDefault(
            x => x.Type == PhoneType.LandLine
        );
        var userMobilePhone = user.Phones.FirstOrDefault(
            x => x.Type == PhoneType.Mobile
        );

        // View Model
        var viewModel = new EditUserViewModel
        {
            User = user,
            Id = user.Id,
            FirstName = user.FirstName,
            LastName = user.LastName,
            BirthDay = user.Detail?.BirthDay,
            Email = user.Email,
            Nationality = user.Detail?.Nationality?.Id,
            Title = user.Detail != null ? user.Detail.Title : TitleType.Mr,
            ProfilePhotoName = user.Detail?.ProfilePhotoName,
            MarketingOptin = user.Detail != null 
                ? user.Detail.MarketingOptin 
                : false,
            ChangePassword = false,
            Password = null,
            Address_City = userAddress?.City,
            Address_Country = userAddress?.Country.Id,
            Address_HouseName = userAddress?.HouseName,
            Address_HouseNumber = userAddress?.HouseNumber,
            Address_Line = userAddress?.AddressLine,
            Address_PostCode = userAddress?.PostCode,
            Address_StreetName = userAddress?.StreetName,
            Home_CountryCode = userHomePhone?.CountryCode?.ToString(),
            Home_PhoneNumber = userHomePhone?.PhoneNumber,
            Mobile_CountryCode = userMobilePhone?.CountryCode?.ToString(),
            Mobile_PhoneNumber = userMobilePhone?.PhoneNumber,
            CountriesList = countries.List,
            NationalitiesList = nationalities.List
        };
        return View(viewModel);
    }
    else
        return RedirectToAction("Index");
}

或者另一个使用LINQ进行巨大选择的

代码语言:javascript
复制
public CompanyDetailModel GetCompanyDetails(Guid accountHolderId)
{
    CompanyDetailModel companyDetailModel = new CompanyDetailModel();

    var accountHolder = _dbContext.AccountHolders.FirstOrDefault(
        x => x.Id == accountHolderId
    );

    var companyId = accountHolder.ObjectId;
    var company = _dbContext.Companies.FirstOrDefault(x => x.Id == companyId);

    companyDetailModel.CompanyId = company.Id;
    companyDetailModel.Description = company.Description;
    companyDetailModel.Name = company.Name;
    companyDetailModel.RegistrationNumber = company.RegistrationNumber;
    companyDetailModel.VATNumber = company.VATNumber;

    var users = 
        from ah in _dbContext.AccountHolders
        join uc in _dbContext.UserCompanies 
            on ah.ObjectId 
            equals uc.CompanyId
        join u in _dbContext.Users 
            on uc.UserId 
            equals u.Id
        where ah.Id == accountHolderId
        select new CompanyUserModel
        {
            UserId = u.Id,
            CompanyId = (Guid)uc.CompanyId,
            FullName = u.FirstName + " " + u.LastName,
            Email = u.Email,
            PhoneNumber = u.PhoneNumber,
            UserCompanyRoleType = uc.Role
        }
    ;

    var invites = 
        from ci in _dbContext.UserCompanyInvites
        select new CompanyUserInviteModel
        {
            Id = ci.Id,
            CompanyId = ci.CompanyId,
            IsAccepted = ci.IsAccepted,
            UserEmail = ci.UserEmail,
            RoleType = ci.RoleType
        }
    ;

    companyDetailModel.CompanyUsers = users.ToList();
    companyDetailModel.CompanyUserInvites = invites.ToList();

    return companyDetailModel;
}

用私有方法创建对象就足够了,还是有更好的方法?设置一个映射器不是很乏味吗?

EN

回答 2

Software Engineering用户

回答已采纳

发布于 2018-11-28 13:54:59

无论将此逻辑移动到视图模型或数据映射器中,您都有数据映射逻辑。设置数据映射程序并不比控制器中已经存在的代码更繁琐。这似乎更多的是一个关于代码美学的问题。它在控制器“看起来”凌乱,你想要“干净”的外观控制器。这可以通过以下方式之一实现:

  1. 将此逻辑推送到视图模型中,在该视图模型中,视图模型包含对实体的引用。伊万已经提过了。这种方法的好处在于,将更改应用于实体的逻辑包含在视图模型中。缺点是将更改应用于实体的逻辑包含在视图模型中。有时,将视图模型中的数据与实体完全分离是很好的,这样它们就可以根据不同的原因自由发展。这导致:
  2. 使用视图模型中的构造函数进行映射,但是视图模型不保存对实体的引用,而是直接映射到其他属性。视图模型和实体之间仍然存在耦合,但只存在于构造函数中。是否有另一个需要映射到同一个视图模型的实体?添加构造函数。对于小型的、简单的视图模型来说,这是非常有效的。这导致了这种方法的缺点:构造器膨胀。视图模型变成90%的构造函数代码和10%的“视图使用的东西”。
  3. 使用“视图模型工厂”创建基于实体的视图模型。视图模型和实体是完全解耦的,可以单独进化。当任何一方更改时,只有视图模型工厂才会受到影响。对于复杂的视图模型,或者视图模型和/或实体经常发生更改的情况,由于不同的原因,这是很好的。视图模型工厂是视图模型、实体和存储库相遇的地方。这是另一层,它隔离了其他层,以抵御变化。最后一点可能是解决方案之间的临界点。当视图模型的构造变得复杂时,使用实现接口的视图模型工厂,然后将其注入控制器,可以在单元测试控制器时轻松地模拟这个复杂的逻辑。

您选择的解决方案将导致“更干净”的控制器代码,这似乎是您的最终目标。我以前使用过数字2和3的组合,其中2号用于简单视图模型,第3号用于复杂视图模型,或表示应用程序中整个“页面”或“屏幕”的视图模型。

使用方法2和3,您将需要一个额外的层来将对视图模型中的数据的更改应用到您的实体。您可以使用模糊的“服务类”,也可以使用干净的体系结构中的用例。

无论哪种方式,您都需要代码将数据从实体映射到查看模型,然后再返回。这不会改变的。你所能做的就是让它看起来更漂亮,并希望把有意义的名字应用于事物。

票数 0
EN

Software Engineering用户

发布于 2018-11-28 10:48:01

老实说我不觉得这太糟了。关键是控制器有一个单一的责任。

如果你想让它变得更干净,你可以移动映射,ViewModel本身。

代码语言:javascript
复制
public class EditUserViewModel
{
    private User user;
    EditUserViewModel(User user) {this.user = user;}

    public bool MarketingOptin 
    {
        get 
        {
            return user.Detail != null ? user.Detail.MarketingOptin : false,
        }
        set {...}
    }
}
票数 0
EN
页面原文内容由Software Engineering提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://softwareengineering.stackexchange.com/questions/382133

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档