在ASP.NET实际开发中,C#扩展方法使用非常普遍,但是很少有熟练使用它进行数据模型转化。类似这样情形比较常见,即实体数据(通常为数据表)模型User与参数(通常为方法参数)模型UserModel映射,常见的做法是,在每个(模型转化)方法中将User的属性跟UserModel的属性进行一一赋值映射,但此法存在诸多弊端,主要是重复赋值繁琐,不便于系统后期维护扩展。

显然,上文描述过于抽象,下面举例说明。假设存在两个实体数据模型User和UserProfile,其中User为用户基本信息,UserProfile为用户详细信息,模型其它属性从略,如下代码:

namespace Demo
{
    public class User
    {
        public string Name { get; set; }
        public string Surname { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public Guid Uid { get; set; }
    }
}

namespace Demo
{
    public class UserProfile
    {
        public Guid Id { get; set; }
        public string IdNumber { get; set; }
        public int? CompanyId { get; set; }
        public Address Address { get; set; }
    }
}

现有业务是这样的,需要对User和UserProfile同时进行CURD,并且输入输出参数均为UserInfo,参数模型如下:

namespace Demo
{
    public class UserInfo
    {
        public string Name { get; set; }
        public string Surname { get; set; }
        public string UserName { get; set; }
        public string Password { get; set; }
        public Guid Uid { get; set; }
        public string IdNumber { get; set; }
        public int? CompanyId { get; set; }
        public Address Address { get; set; }
    }
}

通常情况下,在进行CURD时,会是这样做:

namespace Demo
{
    public class UserStore
    {
        public virtual async Task CreateAsync(UserInfo userInfo)
        {
            var user = new User()
            {
                Uid = userInfo.Uid,
                Name = userInfo.Name,
                Password = userInfo.Password,
                UserName = userInfo.UserName,
                Surname = userInfo.Surname,
            };

            var userProfile = new UserProfile()
            {
                CompanyId = userInfo.CompanyId,
                Address = userInfo.Address,
                Id = userInfo.Uid,
                IdNumber = userInfo.IdNumber
            };

            await _userStore.CreateAsync(user);
            await _userProfileStore.CreateAsync(userProfile);
        }
    }
}

即对每一个实体数据模型属性分别赋值映射,对一个这样的操作无可厚非,但进行多个此类操作时,就会出现上述弊端。为了解决这个问题,我们可以建立一个UserExtensions扩展方法,如下:

namespace Demo
{
    internal static class UserExtensions
    {
        public static UserProfile ToUserProfile(this UserInfo userInfo)
        {
            return userInfo == null
                ? null : new UserProfile()
                {
                    CompanyId = userInfo.CompanyId,
                    Address = userInfo.Address,
                    Id = userInfo.Uid,
                    IdNumber = userInfo.IdNumber
                };
        }

        public static User ToUser(this UserInfo userInfo)
        {
            return userInfo == null
                ? null : new User()
                {
                    Uid = userInfo.Uid,
                    Name = userInfo.Name,
                    Password = userInfo.Password,
                    UserName = userInfo.UserName,
                    Surname = userInfo.Surname,
                };
        }

        public static UserInfo AssignTo(this User user, UserInfo userInfo)
        {
            if (userInfo == null)
            {
                userInfo = new UserInfo();
            }
            if (user == null)
            {
                return userInfo;
            }
            userInfo.Uid = user.Uid.HasValue ? user.Uid.Value : Guid.Empty;
            userInfo.Name = user.Name;
            userInfo.Password = user.Password;
            userInfo.UserName = user.UserName;
            userInfo.Surname = user.Surname;

            return userInfo;
        }

        public static UserInfo AssignTo(this UserProfile userProfile, UserInfo userInfo)
        {
            if (userInfo == null)
            {
                userInfo = new UserInfo();
            }
            if (userProfile == null)
            {
                return userInfo;
            }
            userInfo.CompanyId = userProfile.CompanyId;
            userInfo.Address = userProfile.Address;
            userInfo.Uid = userProfile.Id;
            userInfo.IdNumber = userProfile.IdNumber;

            return userInfo;
        }

        public static UserInfo AssignTo(this UserProfile userProfile)
        {
            return AssignTo(userProfile, null);
        }

        public static UserInfo AssignTo(this User user)
        {
            return AssignTo(user, null);
        }
    }
}

那么上述实例中CreateAsync变成如下方法:

namespace Demo
{
    public class UserStore
    {
        ……            
        public virtual async Task CreateAsync(UserInfo userInfo)
        {
            var user = userInfo.ToUser();
            var userProfile = userInfo.ToUserProfile();
            await _userStore.CreateAsync(user);
            await _userProfileStore.CreateAsync(userProfile);
        }
    }
}

是不是很简洁?该做法优点远不止这些,最为典型的是,若后期需要对User和UserProfile进行扩展,添加(或减少)字段,只需要维护UserExtensions扩展方法,而不需要去维护每个CURD方法。

读者再思考一下UserExtensions扩展方法中的AssignTo有什么作用?

最后,提供今天项目开发进程中相关应用代码(点击下载)供大家参考,耐心看完者会得到意外的收获。

科兴科学园

空非易文章均为原创,转载请以链接形式注明本文地址

本文地址:http://pengzhiyong.com/archives/csharp-extension-mapping.html

作者:空非易 | 标签: , | 浏览:2249