EntityFramework进阶——数据编辑与维护

实体数据对象状态

在EF环境下,应用程序更改数据对象会引发数据集状态的变更,可能的状态有以下几种:

数据对象状态列表
Added添加实体对象创建到实体集中,数据未添加进数据库
Modified实体对象已经存在于实体数据集中,数据库同时存在对应的数据,但某些实体对象属性值已经改变,未更新到数据库中
Deleted实体对象已经存在于实体数据集中,数据库同时存在对应的数据,但是实体对象本身被标识为删除
Unchanged实体对象存在于数据集中,数据库同时包含对应未曾更改的相同数据
Detached实体对象已经不在数据集中

SaveChanges方法被调用时:

1、Added状态会将新实体对象的属性数据更新到数据库。

2、Modified状态实体对象的属性值就会逐一更新到数据库中对应的数据字段。

3、Deleted则将其中对应实体对象的数据删除。

4、当实体对象为Added或者Modified状态时,调用SaveChanges方法更新数据成功后,会将状态调整为Unchanged

5、当实体对象为Deleted状态时,调用SaveChanges方法更新数据成功后,会将对象的状态调整为Detached

 

可以使用DbContext.Entry方法获取对象的状态。

下面有代码说明举例,假设存在如下实体类:

    public class Product{public int Id { get; set; }public string Name { get; set; }public string Category { get; set; }public int Price { get; set; }}

上下文类代码如下图所示:

    public class SaveChangesModel : DbContext{public SaveChangesModel(): base("name=SaveChangesModel"){}public virtual DbSet<Product> Product { get; set; }}

运行程序建立相应的数据库和表结构后,修改Main函数中的代码如下图所示:

static void Main(string[] args)
{using (SaveChangesModel db = new SaveChangesModel()){Product product = new Product{Name = "电脑",Category = "办公用品",Price = 5000,};Console.WriteLine("初始状态:{0}", db.Entry(product).State.ToString());db.Product.Add(product);Console.WriteLine("Add后的状态:{0}", db.Entry(product).State.ToString());Console.ReadKey();}
}

运行后效果如下图所示:

                       

可以看到,一开始product对象创建完成后是Detached状态,表示其与数据库无任何关联,调用Add方法后,转化成为Added状态,表示这一组数据对象当前是准备加入数据库的状态。

EF根据数据对象的状态决定是否进行底层数据库的变更,并且通过状态调整达到数据变更的目的。

可以将其中代码部分修改下,如下图所示:

//db.Product.Add(product);
db.Entry(product).State = System.Data.Entity.EntityState.Added;

其中直接将Product对象的state设置为Added,效果与Add方法相同,因此不需要明确调用Add方法,只需要通过状态标识即可完成添加操作。 


更新与删除 

 更新和删除操作必须先要找到数据集中相应的数据才能进行操作。

Find()方法可以利用数据表的主键进行快速查询出想要的数据。更新操作的示例代码如下图所示:

static void Main(string[] args)
{using (SaveChangesModel db = new SaveChangesModel()){var product = db.Product.Find(1);Console.WriteLine("初始状态:{0}", db.Entry(product).State.ToString());product.Price = 4500;Console.WriteLine("修改后状态:{0}", db.Entry(product).State.ToString());db.SaveChanges();Console.ReadKey();}
}

运行结果如下图所示:

                               

 

 删除效果的实例代码如下图所示:

static void Main(string[] args)
{using (SaveChangesModel db = new SaveChangesModel()){var product = db.Product.Find(1);Console.WriteLine("初始状态:{0}", db.Entry(product).State.ToString());db.Product.Remove(product);Console.WriteLine("删除后状态:{0}", db.Entry(product).State.ToString());db.SaveChanges();Console.ReadKey();}
}

运行结果下图所示: 

                            


 Attach

DbSet类定义了一个Attach方法,此方法定义接收一个实体数据对象参数,将其附加到数据集中,等同于将此数据直接从数据库取出并转换成对应的数据对象,而附加完成之后,entity的状态时Unchanged,通过修改其状态,即可通过SaveChanges方法变成变更更新操作。

修改Main函数中的方法如下图所示:

static void Main(string[] args)
{using (SaveChangesModel db = new SaveChangesModel()){Product product = new Product{Id = 1,Name = "电脑",Category = "办公用品",Price = 6000,};Console.WriteLine("初始状态:{0}", db.Entry(product).State.ToString());db.Product.Attach(product);Console.WriteLine("Attach后状态:{0}", db.Entry(product).State.ToString());db.Entry(product).State = System.Data.Entity.EntityState.Modified;Console.WriteLine("Modified后状态:{0}", db.Entry(product).State.ToString());Console.ReadKey();}
}

运行后结果如下图所示: 

                     

 


变更追踪——DbContext.ChangeTracker 

DbContext会对实体的更新操作进行追踪,如果想要存取变更状态的信息,可以通过DbContext.ChangeTracker属性的调用来获取支持追踪功能的DbChangeTracker对象,语句如下:

DbChangeTracker tracker = context.changeTracker

DbChangeTracker定义了Entries方法,执行这个方法返回的是一个当前DbContext追踪的实体对象,其相关的IEnumerable<DbEntityEntry>集合,进一步调用此方法即可逐一取出所有的DbEntityEntry对象,并提取所需的实体对象变更信息。 

示例代码如下图所示:

static void Main(string[] args)
{using (SaveChangesModel db = new SaveChangesModel()){var product = db.Product.Find(1);DbChangeTracker tracker = db.ChangeTracker;EntryInfo(tracker, "首次载入");product.Name = "惠普电脑";product.Price = 3000;EntryInfo(tracker,"修改一项商品数据的名称");db.Product.Add(new Product{Name = "鼠标",Price = 19,Category = "办公用品",});EntryInfo(tracker, "添加一项商品数据");var product2 = db.Product.Find(2);db.Product.Remove(product2);EntryInfo(tracker,"删除一项商品数据");Console.Read();}
}static void EntryInfo(DbChangeTracker tracker,string desc)
{Console.WriteLine("\n{0}: ",desc);Console.WriteLine("".PadRight(48, '.'));IEnumerable<DbEntityEntry> entries = tracker.Entries();foreach (DbEntityEntry entry in entries){Console.WriteLine("变更实体:{0}",entry.Entity.GetType().FullName);EntityState state = entry.State;Console.WriteLine("状态:{0}", state);if (state != EntityState.Deleted){if (state != EntityState.Added){PropertyList(entry.GetDatabaseValues(),"当前数据库中的副本");PropertyList(entry.OriginalValues,"属性值");}if (state != EntityState.Unchanged){PropertyList(entry.CurrentValues, "变更后的属性值");}Console.WriteLine("//");}}
}static void PropertyList(DbPropertyValues values,string desc)
{Console.WriteLine("{0}:", desc);foreach (string name in values.PropertyNames){Console.WriteLine("\t{0}:{1}", name, values[name]);}
}

修改实体部分的运行结果:

                                       

新增实体部分的运行结果:

                                       

删除实体部分的运行结果:

                                       

 


 更新验证异常——DbEntityValidationException

数据进入数据库之前,很有可能发生各种更新错误,因此必须进行各种验证以确保正确性。当我们执行SaveChanges方法进行底层数据更新操作时,EF会根据实体类的属性逐一进行验证,以决定是否执行数据的更新操作,避免错误数据进入数据库。一旦出现验证不符的数据内容,就会产生DbEntityVaildationException异常。

下面通过例子说明,新建控制台项目SaveChangesEX,并添加如下实体类:

    [Table("Product")]public class Product{[Key]public int Id { get; set; }[Required]public string Name { get; set; }[Range(100,5000)]public int Price { get; set; }[Range(100,5000)]public int SPrice { get; set; }}

上下文类代码如下图所示:

    public class SaveChangesEXModel : DbContext{public SaveChangesEXModel(): base("name=SaveChangesEXModel"){}public virtual DbSet<Product> Product { get; set; }}

首次运行项目在数据库中建立的表结构如下图所示:

                            

 

修改Main函数中的代码如下图所示:

static void Main(string[] args)
{using (SaveChangesEXModel db = new SaveChangesEXModel()){try{Product product = new Product{Name = null,Price = -40,SPrice = -120,};db.Product.Add(product);int count = db.SaveChanges();}catch(Exception ex){Console.WriteLine("\n错误信息:{0}\n",ex.Message);}Console.ReadKey();}
}

以上代码创建了一个新的Product对象,并将其name属性赋值为null, 这是无法通过验证的,同理,Price属性和SPrice属性。

运行效果如下图所示:

 

下面进一步扩展catch子句中的代码:

static void Main(string[] args)
{using (SaveChangesEXModel db = new SaveChangesEXModel()){try{Product product = new Product{Name = null,Price = -40,SPrice = -120,};db.Product.Add(product);int count = db.SaveChanges();}catch(Exception ex){Console.WriteLine("\n错误信息:{0}\n",ex.Message);if (ex is DbEntityValidationException){foreach (DbEntityValidationResult validationResult in ((DbEntityValidationException)ex).EntityValidationErrors){foreach (DbValidationError error in validationResult.ValidationErrors){Console.WriteLine(".....{0}",error.ErrorMessage);}}}}Console.ReadKey();}
}

DbEntityValidationException.EntityValidationErrors属性返回与此实体对象有关的验证错误,每一个验证错误封装为DbEntityValidationResult 对象,最后形成IEnumerable<DbEntityValidationResult>返回,因此我们可以通过foreach循环列举其中所有的DbEntityValidationResult 对象。

每一个实体相关的验证错误进一步封装在DbEntityValidationResult.ValidationErrors属性返回的ICollection<DbVaildationError>集合中,通过其中的DbValidationError.ValidationErrors可以取出真正的信息。

如果想要同时获取造成这个错误的关键属性。可以调用DbValidationError.PropertyName以获取属性名称。

运行效果如下图所示:

                 

上述输出结果中,除了原来的信息外,同时还列出了每一组属性专属的错误信息,这些信息是属性数据注解内置的默认消息正文。如果需要设置ErrorMessage指定输出信息,可以修改Product.cs如下所示:

    [Table("Product")]public class Product{[Key]public int Id { get; set; }[Required(ErrorMessage ="必须指定商品名称")]public string Name { get; set; }[Range(100,5000,ErrorMessage ="商品价格范围10-500")]public int Price { get; set; }[Range(100,5000,ErrorMessage ="商品特价范围10-500")]public int SPrice { get; set; }}

运行效果如下图所示:

                  

 


覆写DbContext.ValidateEntity方法

可以覆写DbContext.ValidateEntity方法自定义验证程序,以进一步调整输出验证结果,修改上下文类代码如下图所示:

public class SaveChangesEXModel : DbContext
{public SaveChangesEXModel(): base("name=SaveChangesEXModel"){}public virtual DbSet<Product> Product { get; set; }protected override DbEntityValidationResult ValidateEntity(DbEntityEntry entityEntry, IDictionary<object, object> items){var list = new List<DbValidationError>();if (entityEntry.CurrentValues.GetValue<string>("Name") == null || entityEntry.CurrentValues.GetValue<string>("Name").Length < 4){list.Add(new DbValidationError("Name", "商品名称必须多于四个字符"));}if (entityEntry.CurrentValues.GetValue<int>("Price") < 0){list.Add(new DbValidationError("Price","商品价格不得小于0"));}if (entityEntry.CurrentValues.GetValue<int>("SPrice") < 0){list.Add(new DbValidationError("SPrice", "商品特价不得小于0"));}if (list.Count() > 0){return new DbEntityValidationResult(entityEntry, list);}else{return base.ValidateEntity(entityEntry, items);}}
}

再次运行程序会得到如下结果:

             


覆写SaveChange方法 

在EF环境下,数据变更是最后调用SaveChanges方法将数据正式更新到数据库,如果在更新过程中有需要执行的程序代码,可以尝试直接在实体模型中覆写这个方法。

我们可以将处理验证异常的相关代码覆写到SaveChange方法中,如此一来,就不需要每次调用SaveChange时处理相关问题。

修改上下文代码如下图所示:

    public class SaveChangesEXModel : DbContext{public SaveChangesEXModel(): base("name=SaveChangesEXModel"){}public virtual DbSet<Product> Product { get; set; }public override int SaveChanges(){try{return base.SaveChanges();}catch (Exception ex){string message = ex.Message;if (ex is DbEntityValidationException){message = "验证异常\n";foreach (var validationResult in ((DbEntityValidationException)ex).EntityValidationErrors){foreach (var error in validationResult.ValidationErrors){message += ("......" + error.ErrorMessage + "\n");}}}throw new Exception(message);}           }}

修改Main方法中代码如下图所示:

static void Main(string[] args)
{using (SaveChangesEXModel db = new SaveChangesEXModel()){try{Product product = new Product{Name = null,Price = -40,SPrice = -120,};db.Product.Add(product);int count = db.SaveChanges();Console.WriteLine("添加了{0}项数据!", count);}catch(Exception ex){Console.WriteLine("\n错误信息:{0}\n", ex.Message);}Console.ReadKey();}
}

运行结果如下图所示:

                               

 

 


 SQL语句

EF支持SQL语句以便我们随时进行数据的增删改查操作。示例代码如下图所示:

using System.Data.Entity.Infrastructure;static void Main(string[] args)
{using (SaveChangesEXModel db = new SaveChangesEXModel()){string sql = "SELECT * FROM dbo.Product";DbSqlQuery<Product> query = db.Product.SqlQuery(sql);List<Product> products = query.ToList();Console.WriteLine("商品项数:{0}",products.Count());foreach (Product product in products){Console.WriteLine("{0}\t售价:{1},特价:{2}",product.Name,product.Price,product.SPrice);}Console.ReadKey();}
}

运行结果如下图所示:

                          

 

注意以下几点:

1、SQl语句语法必须正确, 不然报错。

2、SQl语句得到的属性值个数必须和指定的类型的属性个数相同。比如,select Name,Price From Product 这句Sql中只有Name和Price两个属性,无法顺利转化成Product对象,故会报错。

 

当查询结果属性个数不同时可以使用如下方法,修改Main函数中代码如下图所示:

static void Main(string[] args)
{using (SaveChangesEXModel db = new SaveChangesEXModel()){string sql = "SELECT Name FROM dbo.Product";DbRawSqlQuery<string> query = db.Database.SqlQuery<string>(sql);//此方法List<string> productsName = query.ToList();Console.WriteLine("商品项数:{0}", productsName.Count());foreach (string productName in productsName)Console.WriteLine("{0}", productName);}Console.ReadKey();
}

如果需要返回多个字段,可以预先创建对应的类进行转换,如下图所示:

    public class SProduct{public int Id { get; set; }public string Name { get; set; }public int Price { get; set; }}

修改Main函数中的代码:

static void Main(string[] args)
{using (SaveChangesEXModel db = new SaveChangesEXModel()){string sql = "SELECT Name,Price FROM dbo.Product";DbRawSqlQuery<SProduct> query = db.Database.SqlQuery<SProduct>(sql);List<SProduct> products = query.ToList();Console.WriteLine("商品项数:{0}", products.Count());foreach (SProduct product in products){Console.WriteLine("{0} ,价格:{1}", product.Name,product.Price);}Console.ReadKey();}
}

 


在SQL语句中使用参数

不当的SQL语句很容易遭受黑客的注入攻击,我们可以使用参数动态建立所需要的SQL语句。代码如下:

static void Main(string[] args)
{using (SaveChangesEXModel db = new SaveChangesEXModel()){SqlParameter P0 = new SqlParameter("P0",8);SqlParameter P1 = new SqlParameter("P1", "%移动%");object[] parameters = { P0,P1 };string sql = "select Id,Name,Price From Product Where Id >@P0 AND Name LIKE @P1 ";DbRawSqlQuery<SProduct> query = db.Database.SqlQuery<SProduct>(sql, parameters);List<SProduct> Products = query.ToList();}
}

上述SQL语句查找Id大于8,并且名字包含“移动”的数据。


执行非查询变更指令——ExecuteSqlCommand 

Update、InsertInto、delete等更新操作必须通过ExecuteSqlCommand方法,而且不会返回任何数据集。语句如下:

int count = context.Database.ExecuteSqlCommand(sql)

上述的程序代码返回变更的数据项,count 记录了被更新的项数。

using (SaveChangesEXModel db = new SaveChangesEXModel())
{string sql = "UPDATE Product SET SPrice = -1 Where Price > 100";int count = db.Database.ExecuteSqlCommand(sql);Console.WriteLine("更新数据项数:{0}",count);Console.ReadKey();
}

同样可以在SQL语句中使用参数:

using (SaveChangesEXModel db = new SaveChangesEXModel())
{SqlParameter P0 = new SqlParameter("P0", -1);SqlParameter P1 = new SqlParameter("P1", 100);object[] parameters = { P0, P1 };string sql = "UPDATE Product SET SPrice = @P0 Where Price > @P1";int count = db.Database.ExecuteSqlCommand(sql,parameters);Console.WriteLine("更新数据项数:{0}",count);Console.ReadKey();
}

 


使用Local查询追踪本地数据集

可以使用DbSet.Local属性返回当前系统本地的数据内容,也就是DbContext对象追踪的数据集内容。

参考如下代码:

context.Product.Count();

context.Product.Local.Count();

第一条会送出SQL语句查询,返回数据库中对应的数据表中所储存的记录条数。第二行则是针对本地的DbSet对象读取其中的数据项数(或者记录条数),由于没有执行查询,DbContext并没有获取任何数据进行处理,因此,第二条的值为0.

static void Main(string[] args)
{using (SaveChangesEXModel db = new SaveChangesEXModel()){var count = db.Product.Count();Console.WriteLine("数据库中数据的条数:{0}",count);try{Product product = new Product{Name = "茶杯",Price = 10,SPrice = 5,};db.Product.Add(product);Console.WriteLine("本地的数据条数:{0}", db.Product.Local.Count);}catch (Exception ex){Console.WriteLine("\n错误信息:{0}\n", ex.Message);}Console.ReadKey();}
}

运行结果如下图所示:

                 

 


local查询 

EF只有真正取出数据内容时才会进行SQL语句的传送,而这个过程是EF自动执行的,因此不当的设计容易造成重复查询,引发严重的性能问题,若要避免这种情况,可以尝试通过Local获取载入的数据,往后对Local数据进行操作,以减少SQL查询操作。

通过以下程序代码来说明:

using(var context = new KTStoreModel)
{var products = context.product.Where(p => p.price > 100);Product firstProduct = products.First();Console.WriteLine("\n第一项商品数据名称:{0}\n",firstProduct.Name);Console.Writeline("\n所有商品数据列表:\n");foreach(var product in products){Console.WriteLine(Product.Name);}Console.Read()
}

上述代码会导致SQL语句执行两次,第一次是查询第一项商品数据,第二次是查询所有商品数据。

改写代码,将其中Where方法的调用调整如下:

context.Product.Where(p => p.price > 100).Load();
var products = context.product.local;

第一次直接调用Load方法预先将数据载入DbContext,并且通过Local属性的引用取回载入的数据内容并存储于Products变量中。由于直接查询Local属性获取本地数据,因此不需要重复执行SQL语句,这对于性能的提升会有一定帮助。


 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/548498.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第六章扩展——VMA

由 user process角度来说明的话&#xff0c;VMA 是 user process 里一段 virtual address space 区域&#xff1b;virtual address space 是连续的内存空间&#xff0c;当然VMA 也会是连续的空间。VMA 对 Linux 的主要好处是&#xff0c;可以内存的使用更有效率&#xff0c;並且…

EntityFramework进阶——数据变更冲突

TimeStamp 更新操作可能伴随数据冲突&#xff0c;我们可以通过并发处理妥善解决这一方面的问题。避免数据冲突比较方便的做法是自动加入字节数组&#xff08;byte[]&#xff09;类型的TimeStamp属性&#xff0c;对应到数据表中的rowvewsion类型字段&#xff0c;自动监控数据的…

MyBatis 数据持久层

引用&#xff1a;http://baike.baidu.com/view/4372646.htm MyBatis 的前身就是 iBatis 。是一个数据持久层(ORM)框架。 MyBatis框架 [1]iBATIS一词来源于“internet”和“abatis”的组合&#xff0c;是一个基于Java的持久层框架。iBATIS提供的持久层框架包括SQL Maps和Data Ac…

TP类库解析和使用系列[Input类]

2019独角兽企业重金招聘Python工程师标准>>> [ 概述 ] Input类是新版新增的一个输入数据管理类&#xff0c;提供了一些输入数据的管理和过滤。 [ 方法 ] getInstance 实例化Input类 filter 设置数据过滤方法 以下方法都是动态方法&#xff1a; get 获取get数据 post…

详解CSMA/CD

学网络的人&#xff0c;恐怕都得知道在总线网络中必须要用到CSMA/CD来传输数据。 CSMA/CD——带冲突检测的载波侦听多路访问机制&#xff0c;用来决定某一时刻介质访问权限问题&#xff0c;原理如下&#xff1a;所有站点共享一条传输线路&#xff08;总线&#xff09;&…

php读取checkbox数组值

2019独角兽企业重金招聘Python工程师标准>>> 用在批量删除或者更新等操作方面 <form method"post" action"CheckAction.php"> <input type"checkbox" value"h" name"flags[]">头条[h] <input ty…

EntityFramework进阶——Entity Splitting和Table Splitting

Entity Splitting——把单个实体拆分成多个表。Table Splitting——把单个表拆分成多个实体 Entity Splitting 下面通过例子来说明&#xff1a; 假设存在如下实体类&#xff1a; public class Employee{public int EmployeeId { get; set; }public string Name { get; set; …

EntityFramework进阶——CodeFirst数据库迁移

与视觉操作界面相比&#xff0c;CodeFirst机制提供了一套命令行模式的迁移工具&#xff0c;协助开发人员有效的进行数据库与数据模型的同步维护操作。 新建一个控制台项目CFMigrationsDemo&#xff0c;选用来自数据库的EF设计器模式&#xff0c; 并向其中添加一个Product.cs类文…

前端控件JQuery Datatables使用——常用功能初始化

本文用于自己记录&#xff0c;忘记时可以用来回顾。点击这里进入JQuerydatatable官网API地址 JQuery Datatables初始化 个人比较喜欢JQueryDatatables的Bootstrap4风格&#xff0c;所以文章以Bootstrap4风格为例。 JQueryDatatable的初始化很简单&#xff0c;按照官网的举例说…

一款轻量级的消息提示插件 —— toastr

toastr是一款轻量级的消息提示插件&#xff0c;基于JQuery&#xff0c;使用简单方便&#xff0c;外观大气漂亮。 点击这里进入toastr在线调试使用 点击这里进入toastr官方网站 使用效果如下图所示&#xff1a; 插件使用需要引用的JS和CSS文件如下图所示&#xff1a; 各版…

《Spring Recipes》第二章笔记:Creating Beans by Invokin...

2019独角兽企业重金招聘Python工程师标准>>> 《Spring Recipes》第二章笔记&#xff1a;Creating Beans by Invoking an Instance Factory Method 问题 用户希望使用工厂类来实例化bean。 解决方案 Spring的bean元素中提供了factory-bean属性来配置工厂类&#xff0…

JQuery Datatables editor进行增删改查操作(一)

背景 editor作为Datatables的插件之一&#xff0c;功能十分强大&#xff0c;有全行编辑模式、泡泡编辑模式、行内编辑模式。个人觉得&#xff0c;除了泡泡编辑模式外&#xff0c;其他两种模式功能在实际项目中会经常使用到&#xff0c;泡泡模式提供了模态框可以对表格内的数据…

将C#中DateTime类型转化为JavaScript中的Date类型

将C#中的DateTime类型数据返回到前端页面时&#xff0c;显示的样子如下图所示&#xff1a; 可以用JS前端操作转化成JS的Date格式&#xff0c;直接上代码 &#xff1a; // 对Date的扩展&#xff0c;将 Date 转化为指定格式的String // 月(M)、日(d)、小时(h)、分(m)、秒(s)、季…

FusionCharts V3图表导出图片和PDF属性说明

百闻不如一见&#xff0c;狠狠点击&#xff0c;快快下载&#xff1a;&#xff08;演示文档有错误&#xff0c;不提供下载了。待新的演示文档出来。&#xff09; 许多朋友说上面的DEMO用不了。fusioncharts官方的演示非常不错&#xff0c;就是来不及整理&#xff0c;各位大侠们…

JQuery Datatables editor进行增删改查操作(二)

背景 上一篇对editor进行了一个基本功能的初始化操作演示&#xff0c;本文进一步了解下editor在上一篇文章功能的基础上添加了部分常用基本功能。 整体界面如下图所示&#xff1a; 新增界面&#xff1a; 新增界面增加了单选按钮&#xff0c;下拉框&#xff0c;和时间选择器&am…

《统一沟通-微软-实战》-6-部署-2-中介服务器-5-语音路由-语音策略

创建语音策略和配置 PSTN 用法记录 如果要创建新的语音策略&#xff0c;请执行以下步骤。如果要编辑语音策略&#xff0c;请参阅修改语音策略和配置 PSTN 用法记录中的相关步骤。 注意&#xff1a; 每个语音策略必须至少具有一条关联的 PSTN 用法记录。要查看企业语音部署中提…

JQuery Datatables editor 行内编辑功能

背景 ERP软件中&#xff0c;能进行行内编辑的表单是常用功能&#xff0c;行内编辑功能能让用户在表格中自己填写、修改、删除数据或者选择数据&#xff0c;可以说表格控件是ERP软件的核心控件&#xff0c;而行内编辑功能是核心中的关键。 本文会继续接着上一篇文章的介绍继续…

Shell图形化监控网络流量

shell图形化监控网络流量 网络流量的监控工具有很多&#xff0c;如&#xff1a;Mrtg、Cacti、Zabbix等等&#xff0c;他们都有着各自的特点&#xff0c;不同的侧重&#xff0c;只为适合不同的应用场景的各种特殊需求。除了网络流量监控工具以外&#xff0c;还有Nagios这样的监控…

JQuery Datatables editor 在编辑前刷新数据

背景 在同一时间&#xff0c;可能很多人在编辑修改同一数据&#xff0c;这会导致在一个人还在在修改的过程中另一人在完成了修改并保存了数据。editor在button插件的扩展中可以有效的缓解这个问题&#xff1a;自定义一个编辑按钮&#xff0c;此编辑按钮实现 点击按钮后&#x…

JQuery Datatables 显示行的附加信息

点击这里查看datatables官网介绍 点击这里查看datatables中文网介绍 效果如下图所示&#xff1a; 点击首列调用ajax返回数据&#xff0c;并展开明细行如下图所示&#xff1a; CSS代码&#xff1a; td.details-control {background: url(../../scripts/datatables-1.10.19/ima…