加载中…

加载中...

EntityFrameworkCoreSQLite与WPF结合的使用

转载 2019-02-23 20:52:08

EntityFrameworkCore SQLiteWPF结合的使用

1、安装环境

Nuget中输入Microsoft.EntityFrameworkCore.Sqlite进行安装,测试项目使用的是 Framework框架,结果引用中添加了二十几个DLL引用,我猜想如果使用的是.Net Core框架,可能会少很多DLL引用,当然操作系统的DLL引用估计还是少不了。

个人猜想要是使用Code First,应该能删除不少Dll,不清楚这些结构,不敢删除。

2、数据表的创建

这里定义两个数据表:

public class ReplaceFolder

 {

       public int ID { getset; }

       [Required, Column("FolderName")]

       public string FolderName { getset; }//文件夹名称

       [Required, Column("ParentID"), DefaultValue(-1)]

       public int ParentID { getset; } //所属的父文件夹ID,默认值为-1,则表示无父文件夹

       public string Description { getset; }  //描述

       publicList ReplaceRules { getset; }

}

上面是一个带树形结构的数据表

下面是其中的子表(当然其中还含有子表,这里就不继续了)

   public class ReplaceRule

   {

       public int ID { getset; }

       [Required]

       public string RulesName { getset; }//规则名称

       public string Description { getset; }  //描述

       [Required]

       public int ReplaceFolderID { getset; } //所属的父文件夹ID,如果是ID,必须是表名加上ID组成

       publicList ReplaceGroups { getset; }

}

 

定义数据库对象:

public class DataContext : DbContext

 {

       publicDataContext()

            base()

       {

       }

       protected override voidOnConfiguring(DbContextOptionsBuilder optionsBuilder)

       {

            optionsBuilder.UseSqlite("Data Source=Sqlite.db");  //无数据库则创建

       }

       //部件数据表

       publicDbSet Parts { getset; }

       //替换规则数据表

       publicDbSet ReplaceFolders { getset; }//文件夹

       publicDbSet ReplaceRules { getset; }//规则

       protected override voidOnModelCreating(ModelBuilder modelBuilder)

       {

           modelBuilder.Entity().HasIndex(t => new { t.RulesName,t.ReplaceFolderID }).IsUnique();

           modelBuilder.Entity().HasIndex(t => new { t.GroupName,t.ReplaceRuleID }).IsUnique();

         

       }

  }

问题点:如果一个子表,其名称和所属的父键共同组成唯一项标识,譬如在一个文件夹下,子项文件名称不能重复,但在不同的文件夹下,子项文件名称是可以重复的,如何声明呢?

   很简单,子项是不能使用文件名称作为主键的(因为主键本身就是唯一标识),这时如上述的ReplaceRule表定义,仍旧使用ID作为主键,然后在OnModelCreating中如上述代码添加限制代码即可(网上查了很久没找到,原来如此简单)

 modelBuilder.Entity().HasIndex(t => new { t.RulesName,t.ReplaceFolderID }).IsUnique();

3、绑定到WPFDataGrid

使用如下的代码既可以进行简单的绑定:

                ReplaceFolder parentFolder =parentTree.Key asReplaceFolder;

                var nodes = TreeRulesSource.Where(s => s.ReplaceFolderID== parentFolder.ID).ToList();

问题点:绑定到DataGrid后,我想进行各种添加/删除/修改操作,甚至还有撤销/还原操作,最后再点击一个Save按钮来保存到数据库,如何实现呢?(注意:如果采用及时更新方式则不需要下述方法

可能很多人会想到ObservableCollectio集合,不错,是用它,但如何把ObservableCollectio集合对应的修改数据又反过来更新到数据库呢?我这里使用了ObservableCollectio集合的扩展:

   public class ObservableCollectionEx<<span se-mark="1">T> :ObservableCollection

   {

       publicList AddedItems;

       publicList DeletedItems;

       #region 构造函数

       publicObservableCollectionEx() : base()

       {

            AddedItems = new List();

            DeletedItems = new List();

       }

       publicObservableCollectionEx(List list) : base(list)

       {

            AddedItems = new List();

            DeletedItems = new List();

       }

       publicObservableCollectionEx(IEnumerable collection) : base(collection)

       {

            AddedItems = new List();

            DeletedItems = new List();

       }

       #endregion

       //当有AddRemove命令时会触发

       protected override voidOnCollectionChanged(NotifyCollectionChangedEventArgs e)

       {

            base.OnCollectionChanged(e);

            if (e.NewItems != null)

            {

                foreach (var n in e.NewItems)

                {

                    AddedItems.Add((T)n);

                }

            }

            if (e.OldItems != null)

            {

                foreach (var m in e.OldItems)

                {

                    if (AddedItems.Contains((T)m))

                    {

                       AddedItems.Remove((T)m);

 

                    }

                    else

                        DeletedItems.Add((T)m);

 

                }

            }

 

       }

       /// 

       /// 清除添加/删除的存储记录

       /// 

       public voidClearAddedOrDeleted()

       {

            AddedItems.Clear();DeletedItems.Clear();

       }

       protected override void ClearItems()

       {

            base.ClearItems();

            ClearAddedOrDeleted();

       }

}//

查询数据类似下面(并将PartsDataSource绑定到DataGrid)

var ret = from in _Context.Parts where i.Manufacturer ==selItem.Key.ToString() select i;

PartsDataSource = newObservableCollectionEx(ret.Cast());

然后当点击保存按钮时,执行如下类似的操作:

public void SaveParts()

       {

            foreach (var stu in PartsDataSource)

            {

                //修改数据,对以修改的数据添加修改时间,一般不需要

                if (_Context.Entry(stu).State ==EntityState.Modified)

                {

                    stu.ModifiedDate =System.DateTime.Now;

                }

            }

            //添加到数据库

           foreach (var add in PartsDataSource.AddedItems)

            {

                add.CreateDate =System.DateTime.Now;

                add.ModifiedDate =System.DateTime.Now;

                _Context.Parts.Add(add);

            }

//从数据库中删除

            foreach (var del in PartsDataSource.DeletedItems)

            {

                _Context.Parts.Remove(del);

            }

           PartsDataSource.ClearAddedOrDeleted();//清除集合的操作记录

            _Context.SaveChanges(); //保存到数据库

   }

使用上述方法基本可以应付各种修改和保存操作了,但又出现了两个新问题:

问题1>使用上面的普通数据表声明,UI可以通知到查询数据,但后台修改了查询的数据后,却不能及时通知到UI,这时需要实现通知属性,如何实现呢,网上搜索了很久无结果,最后还把问题归类到与DataGrid绑定到DataTable一样的情况。

譬如有一个DataGrid显示了很多数据,点击某一行的一个按钮打开一个对话框选择,然后后台将该行数据进行了更改,但DataGrid并不会及时更新。怎么处理???

灵光一现,为何不把数据表中的字段实现类型WPF的通知机制呢?

譬如下面这个表,本来Entity是用它的结构映射数据库中的相应的表的,我直接修改了两个字段的定义,如下,同时类还继承了通知类:

public class ReplaceItem : ViewModelBase

   {

       public int ID { getset; }

       [Required, Column("ProductType")]

       public string ProductType //产品类型

       {

            get return GetValue<<span se-mark="1">string>(); }

            set { SetValue(value); }

       }

       [Required]

       public string Manufacturer  //生产商

       {

            get return GetValue<<span se-mark="1">string>(); }

            set { SetValue(value); }

       }

       [Required]

       public int ReplaceGroupID { getset; }//组名称

}//

注意,上面的ViewModelBase使用的是DevExpress的开源库MVVM

https://www.nuget.org/packages/DevExpressMvvm/

我想也可以使用类似下面的代码(不过这个没测试)

string _Manufacturer  ;

public stringManufacturer  

{   

    get { return _Manufacturer  ; }   ​

set   {     

      if(_Manufacturer !=value)     {        _Manufacturer   =value;        

       OnPropertyChanged(()=>Manufacturer  );    }  

        }

}​​

按这种方式修改后,运行代码不报错,后台的修改还能及时的更新到UI,然后我又赶紧验证了一下数据库的创建:将原运行的数据库删除,然后重新运行程序,生成的新数据库与原数据库一摸一样。

​问题2> 保存命令按钮使用的是MVVM模式,那么保存按钮的Enabled状态如何控制呢?

大家马上想到了类似下面的代码

 if (ReplaceItemsSource == null) return false;

  if (ReplaceItemsSource.DeletedItems.Count > 0) return true;          

 if (ReplaceItemsSource.AddedItems.Count > 0) return true;                          foreach (var stu in ReplaceItemsSource)            

{       if (_Context.Entry(stu).State == EntityState.Modified)                                    return true;                       

 }           

 return false;

​这段代码逻辑上无任何问题,少量数据的情况下也OK,但当数据量超过1000行后,UI界面会卡得动不了,主要问题就是上述的遍历搜索修改项。

      有没有好的解决方案呢,也想到了,就是使用​表格控件的RowEditEnded结束事件,当该事件触发时,提取当前编辑行,通过_Context.Entry(stu).State方式判别是否修改,然后设置Save按钮的状态,那使用了事件不就破坏了MVVM方式吗?这里使用DevExpressMVVM中的事件转换为命令方式:

                                                                                                                                 

 

​上面介绍过,这个框架是开源的,针对不同的表格控件,只需要执行相应的GridViewRowEditEndedEventArgsConverter即可,我这里使用的是Telerik的GridView控件,则为:

   public class GridViewRowEditEndedEventArgsConverter : EventArgsConverterBase    {        protected override object Convert(object sender, GridViewRowEditEndedEventArgs args)        {            return args.EditedItem;

        }    }

其实就一行代码。​

     至此才算是完善了WPF的通知机制,第一次看到在Entity的映射表中使用通知类,第一次试用,没想到竟然成功了。

EntityFrameworkCore再配合几行Linq操作,原来如此复杂的数据操作现在轻易搞定。

     这里再补充一点其他额外的应用,譬如通过EntityFrameworkCore的多表联合查询的数据结果,如果享受​EntityFrameworkCore的状态追踪功能的,即当查询出来的数据后,只需要调用SaveChanged()函数就能保存修改,当然添加和删除使用上述的集合扩展很好,一种方式就是在数据库的映射表中添加一些非映射表字段,这些表就是用于多表查询存储的其他字段,如下是一个例子

from q in listOrders       

    group q by new { q.ProductType, q.Manufacturer } into g      

    select new OrderItem       {        

             ProductType = g.Key.ProductType,        

             Manufacturer = g.Key.Manufacturer,       

             Amount = g.Sum(x => x.Amount),         

             ProductClass = g.First().ProductClass,      

            ProductName = g.First().ProductName,       

            Description = g.First().Description,        

            ProductUnit = g.First().ProductUnit,         

            UnitPrice = g.First().UnitPrice,        });

​本方法在应用添加和删除功能时,OK,但无法追踪修改状态​​,调试时发现状态是隔离的,最后通过类似上述的代码监测UI修改,然后再通过代码设置对象为修改状态,最后调用保存函数时,才能保存修改的值,不明白这个机理,不知是EntityFramework Core目前的问题,还是一直就是如此。

阅读(0) 评论(0) 收藏(0) 转载(0) 举报/Report

评论

重要提示:警惕虚假中奖信息
0条评论展开
相关阅读
加载中,请稍后
ParkerWu
  • 博客等级:
  • 博客积分:0
  • 博客访问:28,179
  • 关注人气:0
  • 荣誉徽章:

相关博文

推荐博文

新浪BLOG意见反馈留言板 电话:4000520066 提示音后按1键(按当地市话标准计费) 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

新浪公司 版权所有