一篇文章,带你玩转MVVM,Dapper,AutoMapper

一、背景

由于现在做的项目都是采用WPF来进行UI设计,开发过程中都是基于MVVM来进行开发,但是项目中的MVVM并不是真正的把实体和视图进行解耦,而是将实体和视图完全融合起来,ViewModel只是用来实现View和Model的数据同步,违背了MVVM设计的数据双向绑定的初衷,完全没有发挥出MVVM的优势。

二、MVVM基本概念

1.M:表示Model,也就是一个实体对象。

2.V:表示VIew,也就是UI界面展示,即人机交互界面。

3.ViewModel:可以理解为搭建View和Model的一个业务逻辑桥梁。

三、Demo来说明

首先建立解决方案,方案框架如下:

在Models中创建一个Model实体对象Contacts

public class Contacts
    {
        public int ID { get; set; }

        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public string Company { get; set; }
        public string Title { get; set; }
    }

接下来我们在ViewModel中实现V和M的完全解耦,在ViewModels中添加ContractsViewModels

 public class ContractsViewModels : BaseViewModels
    {
       public  Contacts contracts = null;
        public ContractsViewModels(Contacts contracts)
        {
            this.contracts = contracts;
        }
        public ContractsViewModels()
        {
            contracts = new Contacts();
        }

        public int ID { get { return contracts.ID; } set { contracts.ID = value; OnPropertyChanged(this, nameof(ID)); } }

        public string FirstName { get { return contracts.FirstName; } set { contracts.FirstName = value; OnPropertyChanged(this, nameof(FirstName)); } }

        public string LastName { get { return contracts.LastName; } set { contracts.LastName = value; OnPropertyChanged(this, nameof(LastName)); } }

        public string Email { get { return contracts.Email; } set { contracts.Email = value; OnPropertyChanged(this, nameof(Email)); } }

        public string Company { get { return contracts.Company; } set { contracts.Company = value; OnPropertyChanged(this, nameof(Company)); } }

        public string Title { get { return contracts.Title; } set { contracts.Title = value; OnPropertyChanged(this, nameof(Title)); } }

    }

当数据双向绑定时,视图知道自己绑定了那个实体,为了让实体的属性改变中,能够通知绑定的View,我们需要实现INotifyPropertyChanged这个接口,具体实现如下

 public class BaseViewModels : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(object sender, string name)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(sender ?? this, new PropertyChangedEventArgs(name));
            }
        }
    }

以上实现,在ViewModel中改变属性,便可以轻松的通知到绑定时View控件,真正意义上实现了双向数据绑定。

接下来在ViewModel中添加ContractsVMServices来方便View的数据操作

代码如下:

  /// <summary>
    /// ContractsViewModels服务类
    /// </summary>
    public class ContractsVMServices
    {
        /// <summary>
        /// 加载所有数据的事件
        /// </summary>
        public event EventHandler OnLoadAllContracts;
        /// <summary>
        /// 数据集合对象
        /// </summary>
        public ObservableCollection<ContractsViewModels> ContractsViewModels { get; private set; }
        /// <summary>
        /// 单个实体的事件
        /// </summary>
        public event EventHandler OnLoadContracts;
        /// <summary>
        /// 单个实体
        /// </summary>
        public ContractsViewModels ContractsViewModel { get; private set; }

        private ContractsVMServices() { }

        public static ContractsVMServices Instances = new ContractsVMServices();
        /// <summary>
        /// 初始化
        /// </summary>
        public void Init()
        {
            ContractsViewModels = new ObservableCollection<ContractsViewModels>();
            ContractsViewModel = new ContractsViewModels(new Contacts());
            OnLoadAllContracts?.Invoke(this, EventArgs.Empty);
            OnLoadContracts?.Invoke(this, EventArgs.Empty);
            AutoMapperWrapper.Start();
            LoadAllContracts();
        }
        /// <summary>
        /// 定时模拟数据改变
        /// </summary>
        /// <param name="afterSecond"></param>
        /// <param name="freSecond"></param>
        public void ChangeByTime(int afterSecond = 5, int freSecond = 2)
        {
            Task.Run(async () =>
            {
                await Task.Delay(afterSecond * 1000);
                while (true)
                {
                    try
                    {
                        foreach (var item in ContractsViewModels)
                        {
                            item.Title = "Change" + DateTime.Now.ToString();
                            string update = "Update contacts set [email protected] where [email protected]";
                            List<KeyValuePair<string, object>> ls = new List<KeyValuePair<string, object>>();
                            ls.Add(new KeyValuePair<string, object>(nameof(item.Title), item.Title));
                            ls.Add(new KeyValuePair<string, object>(nameof(item.ID), item.ID));

                            DapperHelper.Update(update, ls);

                        }
                        //string insert = @"Update into contacts (Id,FirstName,LastName,Email,Company,Title) values(@Id,@FirstName,@LastName,@Email,@Company,@Title)";

                    }
                    catch (Exception ex)
                    {
                    }
                    finally
                    {
                        await Task.Delay(freSecond * 1000);
                    }
                }

            });
        }
        /// <summary>
        /// 从数据库中加载所有数据
        /// </summary>
        private void LoadAllContracts()
        {
            try
            {
                //Task.Run(() =>
                //{
                ContractsViewModels.Clear();
                List<Contacts> contracts = DBDapper.DapperHelper.Query<Contacts>("Contacts");

                // ContractsViewModels = AutoMapperWrapper.Map<List<Contacts>, ObservableCollection<ContractsViewModels>>(contracts);
                foreach (var item in contracts)
                {
                    //ContractsViewModels models= AutoMapperWrapper.Map<Contacts, ContractsViewModels>(item);

                    ContractsViewModels.Add(new ViewModels.ContractsViewModels(item));
                }
                //});
            }
            catch (Exception ex)
            {
                Console.WriteLine(  ex.ToString());

            }
        }
        /// <summary>
        /// 根据ID来加载指定数据
        /// </summary>
        /// <param name="id"></param>
        public void LoadOnContracts(int id)
        {
            ContractsViewModel = ContractsViewModels.Where(obj => obj.ID == id).FirstOrDefault();
        }
        /// <summary>
        /// 创建一个新的对象
        /// </summary>
        public void Add()
        {
            try
            {
                int id = ContractsViewModels.Count > 0 ? ContractsViewModels[ContractsViewModels.Count - 1].ID + 1 : 0;
                string insert = @"insert into contacts (Id,FirstName,LastName,Email,Company,Title) values(@Id,@FirstName,@LastName,@Email,@Company,@Title)";
                int res = DapperHelper.Insert<Contacts>(insert, new List<Contacts>() { new Contacts() { ID =id, FirstName = "123", LastName = "456", Email = "123", Company = "1324", Title = "444" } });
                if (res > 0)
                {
                    LoadAllContracts();
                }
            }
            catch (Exception ex)
            {

                Console.WriteLine(ex.ToString());
            }
        }
        /// <summary>
        /// 删除对象
        /// 为了方便演示我只删除数据集合中的第一个对象
        /// </summary>
        public void Delete()
        {
            try
            {
                if (ContractsViewModels.Count > 0)
                {
                    string delete = "delete from Contacts where ID= @ID";
                    int res = DapperHelper.Delete(delete, new { ID = ContractsViewModels[0].ID });
                    if (res > 0)
                    {
                        LoadAllContracts();
                    }
                }
            }
            catch (Exception ex)
            {

                Console.WriteLine(ex.ToString());

            }
        }

    }

实现数据库的CRUD操作,我这里主要用到半自动化的ORM----Dapper。

老生常谈,首先需要从Nuget中安装Dapper,安装到DBDapper项目中。

或者在控制台中输入: Install -Package Dapper 完成Dapper下载安装。

创建一个DapperHelper帮助类

  public class DapperHelper
    {
        //三部曲
        //第一步:使用连接字符串创建一个IDBConnection对象;
        static IDbConnection conn = new SqlConnection(System.Configuration.ConfigurationManager.ConnectionStrings["SqlServerConnString"].ToString());
        //第二步:编写一个查询并将其存储在一个普通的字符串变量中;
        public static  void test()
        {
            string query = "SELECT * from contacts;";
            List<Contacts> contracts=(List<Contacts>)conn.Query<Contacts>(query);
            Console.WriteLine( contracts.Count);
        }

        public static  List<T> Query<T>(string typeofName)
        {
            string query = $"SELECT * from {typeofName};";
            List<T> contracts = (List<T>)conn.Query<T>(query);
            return contracts;
        }

        public static  int Insert<T>(string sql,IEnumerable<T> ls)
        {
            try
            {
              return   conn.Execute(sql, ls);
            }
            catch (Exception ex)
            {

                throw new Exception(ex.ToString());
            }

        }
        public static int Update(string sql, IEnumerable<KeyValuePair<string,object>> ls)
        {
            try
            {
                return conn.Execute(sql, ls);
            }
            catch (Exception ex)
            {

                throw new Exception(ex.ToString());
            }

        }

        public static int Delete(string sql, object obj)
        {
            try
            {
                return conn.Execute(sql, obj);
            }
            catch (Exception ex)
            {

                throw new Exception(ex.ToString());
            }
        }
        //public static int Update<T>(string sql, List<T> ls)
        //{

        //}
        //第三步:调用db.execute()并传递查询,完成。
    }

看上面的三步曲,和ADO.net大同小异,不过我们再也不用将数据库表的数据转换成实体了,采用Dapper便傻瓜式的转换了。

看我们的代码,我们会发现,Model跟View完全独立,不过这个时候,ViewModel会多出跟Model一样的一个实体出来,为了实现Model到ViewModel的一个完美映射,我这里采用了AutoMapper来实现映射。

老套路,我们从Nuget中下载AutoMapper,安装到ViewModels。

或者在控制台中输入: Install -Package AutoMapper完成AutoMapper下载安装。

在ViewModels中建立AutoMapper的一个包装类AutoMapperWrapper,AutoMapper需要初始化应映射规则,因此我们需要先创建一个映射

public class SourceProfile : MapperConfigurationExpression
    {
        public SourceProfile()
        {
            base.CreateMap<ContractsViewModels, Contacts>();
            base.CreateMap<Contacts, ContractsViewModels>();
            //base.CreateMap<ContractsViewModels,Models.Contracts>();
        }
    }

AutoMapperWrapper如下:

public class AutoMapperWrapper
    {
        //protected DTOObject Result { get; set; }

        //protected IEnumerable<DTOObject> Results { get; set; }
        static   Mapper mapper = null;
        public static void Start()
        {
            MapperConfiguration configuration = new MapperConfiguration(new SourceProfile());
            mapper =  new Mapper(configuration);
           // mapper.Map<,>
          //  Mapper
            //new SourceProfile();
        }
        public static T Map<S, T>(S soure)
        {
            T to = mapper.Map<S, T>(soure);

            return to;
        }

    }

以上我们便实现了一个ViewModel,DB的后台管理。接下来我们来看一个View怎么实现数据的绑定

先看一下代码



<Window x:Class="View.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:View"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800" Loaded="Window_Loaded">
    <Grid>
        <DataGrid x:Name="datagrid" ItemsSource="{Binding ContractsViewModels}" AutoGenerateColumns="False" HorizontalAlignment="Left" Height="273" Margin="72,70,0,0" VerticalAlignment="Top" Width="415">
            <DataGrid.Columns>
                <DataGridTextColumn Header="ID" Binding="{Binding ID}"></DataGridTextColumn>
                <DataGridTextColumn Header="FirstName" Binding="{Binding FirstName}"></DataGridTextColumn>
                <DataGridTextColumn Header="LastName" Binding="{Binding LastName}"></DataGridTextColumn>
                <DataGridTextColumn Header="Email" Binding="{Binding Email}"></DataGridTextColumn>
                <DataGridTextColumn Header="Company" Binding="{Binding Company}"></DataGridTextColumn>
                <DataGridTextColumn Header="Title" Binding="{Binding Title}"></DataGridTextColumn>

            </DataGrid.Columns>
        </DataGrid>
        <Button x:Name="btn_LoadAll" Content="加载所有Contract数据" HorizontalAlignment="Left" Margin="97,372,0,0" VerticalAlignment="Top" Click="btn_LoadAll_Click"/>
        <Button x:Name="btn_ChangeByTime" Content="定时改变" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Margin="284,372,0,0" Click="btn_ChangeByTime_Click"/>
        <Button x:Name="btn_Delete" Content="删除" HorizontalAlignment="Left" Margin="417,372,0,0" VerticalAlignment="Top" Width="75" Click="btn_Delete_Click"/>
        <TextBox x:Name="textBox" HorizontalAlignment="Left" Height="23" Margin="591,144,0,0" TextWrapping="Wrap" Text="{Binding ContractsViewModel.FirstName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="120"/>
        <Label Content="FirstName" HorizontalAlignment="Left" Margin="506,141,0,0" VerticalAlignment="Top"/>

    </Grid>
</Window>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace View
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            try
            {
                ViewModels.ContractsVMServices.Instances.OnLoadAllContracts += (s, es) => {
                    this.Dispatcher.InvokeAsync(() => datagrid.DataContext = s);
                };
                ViewModels.ContractsVMServices.Instances.OnLoadContracts += (s, es) => {
                    this.Dispatcher.InvokeAsync(() => textBox.DataContext = s);
                };
                ViewModels.ContractsVMServices.Instances.Init();
                ViewModels.ContractsVMServices.Instances.LoadOnContracts(0);
            }
            catch (Exception ex)
            {

            }
        }

        private void btn_LoadAll_Click(object sender, RoutedEventArgs e)
        {
            ViewModels.ContractsVMServices.Instances.Add();
        }

        private void btn_ChangeByTime_Click(object sender, RoutedEventArgs e)
        {
            ViewModels.ContractsVMServices.Instances.ChangeByTime();
        }

        private void btn_Delete_Click(object sender, RoutedEventArgs e)
        {
            ViewModels.ContractsVMServices.Instances.Delete();

        }
    }
}

以上我们看到DataGrid的ItemsSource必须要先绑定一个源,这个源的名称跟我们ViewModel中的数据集合是一致的。修改TextBox中的数据的时候我们发现DataGrid的数据也跟着改变。以上就是实现V与M的一个解耦。

原文地址:https://www.cnblogs.com/hglSV/p/11706420.html

时间: 2024-10-14 12:20:20

一篇文章,带你玩转MVVM,Dapper,AutoMapper的相关文章

两篇文章带你走入.NET Core 世界:Kestrel+Nginx+Supervisor 部署上云服务器(二)

背景: 上一篇:两篇文章带你走入.NET Core 世界:CentOS+Kestrel+Ngnix 虚拟机先走一遍(一) 已经交待了背景,这篇就省下背景了,这是第二篇文章了,看完就木有下篇了. 直接进入主题: 1.购买云服务器 之前在虚拟机跑了一下,感觉还不够真实,于是,准备买台服务器,认真的跑一下. 有阿里云,腾讯云,华为云,还有好多云,去哪买一个? 之前做为华为云的云享专家去参加了一下活动,本来也准备写篇文章,不过相同游记文太多, 这里就转一篇了:让华为云MVP告诉你——在华为的一天可以做什

三篇文章带你极速入门php(三)之php原生实现登陆注册

看下成果 ps:纯天然h5,绝不添加任何添加剂(css)以及化学成分(js)(<( ̄ ﹌  ̄)我就是喜欢纯天然,不接受任何反驳) 关于本文 用原生的php和html做了一个登陆注册,大概是可以窥见一般php开发的样子了.不过,low的地方区别提前说一下: 这个是多入口,一般程序都是单入口,单入口就是统一通过index.php进入,然后再引入其他文件,调用其代码,多入口就是每次通过不同文件进入(比如一会展示的login.php和register.php) 保留登陆信息用的是session,现在普遍

三篇文章带你极速入门php(一)之语法

本文适合阅读用户 有其他语言基础的童鞋 看完w3cschool语法教程来回顾一下的童鞋(传送门,想全面看一下php语法推荐这里) 毫无基础然而天资聪慧颇有慧根(不要左顾右看说的就是你,老夫这里有一本<php从入门到放弃>,观你根骨清奇10两银子卖给你如何) 看完本文后你会收获到什么 php的变量的定义,使用 函数的定义,使用,传递参数 数组的定义,调用,常用方法,使用场景 php中循环,判断,选择结构的语法 类的定义,成员变量和成员函数的定义和使用 相信我,认真看完本文,你就已经掌握了php常

还不知道事务消息吗?这篇文章带你全面扫盲!

在分布式系统中,为了保证数据一致性是必须使用分布式事务.分布式事务实现方式就很多种,今天主要介绍一下使用 RocketMQ 事务消息,实现分布事务. 文末有彩蛋,看完再走 为什么需要事务消息? 很多同学可能不知道事务消息是什么,没关系,举一个真实业务场景,先来带你了解一下普通的消息存在问题. 上面业务场景中,当用户支付成功,将会更新支付订单,然后发送 MQ 消息.手续费系统将会通过拉取消息,计算手续费然后保存到另外一个手续费数据库中. 由于计算手续费这个步骤可以离线计算,所以这里采用 MQ 解耦

一篇文章,带你明白什么是过拟合,欠拟合以及交叉验证

误差模型:过拟合,交叉验证,偏差-方差权衡 作者Natasha Latysheva;Charles Ravarani 发表于cambridgecoding 介绍 ??在本文中也许你会掌握机器学习中最核心的概念:偏差-方差权衡.其主要想法是,你想创建尽可能预测准确并且仍能适用于新数据的模型(这是泛化).危险的是,你可以轻松的在你制定的数据中创建过度拟合本地噪音的模型,这样的模型是无用的,并且导致弱泛化能力,因为噪声是随机的,故而在每个数据集中是不同的.从本质上讲,你希望创建仅捕获数据集中有用成份的

这篇文章带你彻底理解synchronized

本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q 1. synchronized简介在学习知识前,我们先来看一个现象: public class SynchronizedDemo implements Runnable { private static

带你玩转java多线程系列 “道篇” 多线程的优势及利用util.concurrent包测试单核多核下多线程的效率

java多线程 “道篇” - 多线程的优势及用concurrent包测试单核多核下多线程的效率 1 超哥对于多线程自己的理解 2 测试代码 3 CountDownLatch这个同步辅助类科普 4 如何把电脑设置成单核 5 测试结果 1 超哥对于多线程自己的理解 超哥的理解:对于多线程,无非是对于顺序执行下任务的一种抽取和封装,将原来顺序执行的任务单独拿出来放到线程类的run方法中,通过线程类的start方法进行执行,对于多线程访问共同资源时,我们需要加锁,也就是只有某个线程在拥有锁的时候,才能够

微信商城开发系列第四篇 不写代码玩转微信公众号

本系列文章属作者原创文章,请尊重作者的劳动成果,转载请注明出处:walkingmanc的专栏 , 谢谢! 同时欢迎大家加入微信商城开发QQ群:364072602,共同探讨进步.  为什么叫不写代码玩转微信公众号呢? 我们大家都知道,微信公众号有两种模式,一种是编辑模式,一种是开发模式.所谓的不写代码玩转微信公众号,其实就是在编辑模式下如何使用微信公众号的意思,呵呵,是不是有种恍然大悟的感觉. 其实,如果你关注的微信公众号比较多的话,你会发现有很多有名的公众号,它们没有菜单,每天都会发布4到5篇文

带你玩转Visual Studio——带你高效管理代码

上一篇文章带你玩转Visual Studio--带你高效开发通过对VAssistX优秀插件的讲解,让我们掌握了快速开发C++代码的技能.然而大部分的程序都不是一个人的开发的,是由一个团队的多个人一起开发的,大型的系统还可能由多个不同的团队分包进行开发.多人进行协作开发时,代码的管理就显得及为重要,需要借助代码管理工具的辅助,这种工具又称为版本控制系统. 目前主流的版本控制系统有: CVS:是一个用于代码版本控制的自由软件,它是一个比较早出现的工具,由于它有很多自身的缺陷,现在几乎被SVN所取代了