一、背景

由于现在做的项目都是采用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 = , int freSecond = )
{
Task.Run(async () =>
{
await Task.Delay(afterSecond * );
while (true)
{
try
{
foreach (var item in ContractsViewModels)
{
item.Title = "Change" + DateTime.Now.ToString();
string update = "Update contacts set Title=@Title where ID=@ID";
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 * );
}
} });
}
/// <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 > ? ContractsViewModels[ContractsViewModels.Count - ].ID + : ;
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 = "", LastName = "", Email = "", Company = "", Title = "" } });
if (res > )
{
LoadAllContracts();
}
}
catch (Exception ex)
{ Console.WriteLine(ex.ToString());
}
}
/// <summary>
/// 删除对象
/// 为了方便演示我只删除数据集合中的第一个对象
/// </summary>
public void Delete()
{
try
{
if (ContractsViewModels.Count > )
{
string delete = "delete from Contacts where ID= @ID";
int res = DapperHelper.Delete(delete, new { ID = ContractsViewModels[].ID });
if (res > )
{
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="" Width="" Loaded="Window_Loaded">
<Grid>
<DataGrid x:Name="datagrid" ItemsSource="{Binding ContractsViewModels}" AutoGenerateColumns="False" HorizontalAlignment="Left" Height="" Margin="72,70,0,0" VerticalAlignment="Top" Width="">
<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="" 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="" Click="btn_Delete_Click"/>
<TextBox x:Name="textBox" HorizontalAlignment="Left" Height="" Margin="591,144,0,0" TextWrapping="Wrap" Text="{Binding ContractsViewModel.FirstName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width=""/>
<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();
}
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的一个解耦。

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

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

    背景: 上一篇:两篇文章带你走入.NET Core 世界:CentOS+Kestrel+Ngnix 虚拟机先走一遍(一) 已经交待了背景,这篇就省下背景了,这是第二篇文章了,看完就木有下篇了. 直接进 ...

  2. 两篇文章带你走入.NET Core 世界:CentOS+Kestrel+Ngnix 虚拟机先走一遍(一)

    背景: 上一篇:ASP.Net Core on Linux (CentOS7)共享第三方依赖库部署 已经交待了背景,这篇就省下背景了. 折腾的过程分两步: 第一步是:本机跑虚拟机部署试一下: 第二步是 ...

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

    看下成果 ps:纯天然h5,绝不添加任何添加剂(css)以及化学成分(js)(<( ̄ ﹌  ̄)我就是喜欢纯天然,不接受任何反驳) 关于本文 用原生的php和html做了一个登陆注册,大概是可以窥 ...

  4. 难道你还不知道Spring之事务的回滚和提交的原理吗,这篇文章带你走进源码级别的解读。

    上一篇文章讲解了获取事务,并通过获取的connection设置只读,隔离级别等:这篇文章讲事务剩下的回滚和提交. 事务的回滚处理 之前已经完成了目标方法运行前的事务准备工作.而这些准备工作的最大目的无 ...

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

    本文适合阅读用户 有其他语言基础的童鞋 看完w3cschool语法教程来回顾一下的童鞋(传送门,想全面看一下php语法推荐这里) 毫无基础然而天资聪慧颇有慧根(不要左顾右看说的就是你,老夫这里有一本& ...

  6. 这篇文章带你彻底理解synchronized

    本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ...

  7. MySQL查询为什么没走索引?这篇文章带你全面解析

    工作中,经常遇到这样的问题,我明明在MySQL表上面加了索引,为什么执行SQL查询的时候却没有用到索引? 同一条SQL有时候查询用到了索引,有时候却没用到索引,这是咋回事? 原因可能是索引失效了,失效 ...

  8. 三篇文章带你极速入门php(二)之迅速搭建php环境

    前言 今天讲一下php在windows,mac,linux上的集成环境搭建,目标是简单快速,环境这个事得对号入座,windows用phpstudy,mac用mamp,linux用lnmp一键安装,直接 ...

  9. 带你玩转Visual Studio——带你了解VC++各种类型的工程

    原文地址:http://blog.csdn.net/luoweifu/article/details/48816605 上一篇文章带你玩转Visual Studio——带你新建一个工程一文中提到新建一 ...

随机推荐

  1. Python爬虫使用selenium爬取qq群的成员信息(全自动实现自动登陆)

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: python小爬虫 PS:如有需要Python学习资料的小伙伴可以 ...

  2. JQuery操作attr、prop、val()/text()/html()、class属性

    1.1 arr操作   设置单个属性 // 第一个参数:需要设置的属性名 // 第二个参数:对应的属性值 // $obj.attr(name, value); // 用法举例. $('img').at ...

  3. JQ中的Ajax的封装

    1.认识JQ中ajax的封装      jQ 对于ajax的封装有两层实现:$.ajax 为底层封装实现:基于 $.ajax ,分别实现了$.get 与$.post 的高层封装实现: 2.Ajax的底 ...

  4. selenium设置user-agent以及对于是否是浏览器内核进行反爬

    (Session info: chrome=75.0.3770.90),不同版本方法可能会有些不同 推荐查资料网站必应可以避开一堆广告 一.user-agent设置 from selenium imp ...

  5. webpack打包配置禁止html标签全部转为小写

    用webpack打包页面,发现html中特别写的用来给后端识别的大写标签全部被转为了小写标签,这时候需要将加一个配置 ,caseSensitive:true ,禁止大小写转换. webpack配置: ...

  6. Google在情报搜集中的基础技巧

    Google在情报搜集中的基础技巧 作者:王宇阳 时间:2019-06-06 作者笔记 ​ Google Hacking 是指使用特定的高级的google搜索语法,收集渗透测试目标的信息,查找目标的配 ...

  7. iOS中的NSOperation线程

    1.除NSThread之外的第二种多线程的编程方法   2.采用NSOperation(线程操作,通常用他的子类)和NSOperationQueue(线程队列)搭配来做多线程开发,采用NSOperat ...

  8. Hive SQL编译过程(转)

    转自:https://www.cnblogs.com/zhzhang/p/5691997.html Hive是基于Hadoop的一个数据仓库系统,在各大公司都有广泛的应用.美团数据仓库也是基于Hive ...

  9. 编译安装 proxychains-ng proxychains4

    下载 [root@localhost html]# git clone https://github.com/rofl0r/proxychains-ng.git 编译安装 [root@localhos ...

  10. Linux—主机扫描工具(Nmap)

    Nmap包含五项基本功能: 主机探测 (Host Discovery) 端口扫描 (Port Scanning) 版本检测 (Version Detection) 操作系统侦测 (Operating ...