Quartz.net打造信息抽取器
由于最近的一个项目需要定时抽取特定XML信息,然后保存到数据库,最后通过WebApi把手机端要使用的方法给暴露出来,所以去研究了一下Quartz.net。由于项目很小,我没用到Autofac,Repository模式,UOW这些东西,这个小项目中所涉及的知识点有:
1.Quartz.net配置
2.序列化XML信息到类对象中
3.AutoMapper做DomainModel和DTO之间的映射
4.WebApi允许多个Get方法的存在
下面我们一步一步的来进行说明。
让我们先来看看我们待处理的数据:http://price.agridoor.com.cn/nxt_price/uploads/2014/05/09/20140509_37.xml

从上图可以看到,这个xml数据有个根节点NXT_PRICE,并且根节点下面有很多个price子节点。那么如果我们想把这些xml数据反序列化到我们定义的类对象中,该怎么做呢?
其实很简单。
由于一个根节点NXT_PRICE下面有N个price子节点集合,所以我们创建如下的两个实体类来描述这种关系:
[XmlRoot("NXT_PRICE")]
public class NxtPriceModel
{
public NxtPriceModel()
{
priceItems = new List<PriceItemModel>();
}
[XmlElement("price")]
public List<PriceItemModel> priceItems { get; set; }
}
public class PriceItemModel
{
[XmlElement("seq")]
public int Seq { get; set; } [XmlElement("name")]
public string Name { get; set; } [XmlElement("type")]
public string Type { get; set; } [XmlElement("price")]
public decimal Price { get; set; } [XmlElement("unit")]
public string Unit { get; set; } [XmlElement("time")]
public string Time { get; set; } [XmlElement("first")]
public string First { get; set; } [XmlElement("second")]
public string Second { get; set; } [XmlElement("area")]
public string Area { get; set; }
}
这样两个实体类很简单,相信大家也看出了其中的包含关系。
然后如何反序列化到我们给定的实体类中呢? 这里使用XmlSerializer则是再合适不过的事情了。通过XmlSerializer对象,我们可以直接将对应的xml节点解释成实体类属性,并自动将数据保存到类中的集合对象中。
WriteLog("=============================开始反序列化文件========================");
XmlSerializer ser = new XmlSerializer(typeof(NxtPriceModel));
ms.Position = 0;
var result = (NxtPriceModel)ser.Deserialize(ms);
通过上面的代码,我们就能成功的将路径中的xml信息下载并保存到类集合中,非常方便。这样,我们就完成了xml序列化成类对象的功能。
数据都已经保存到类对象中了,下一步就让我们来Consume它。
我们创建一个Asp.net MVC4项目,并利用Install-package quartz命令将其添加到项目中。然后在项目中添加一个MyJob类,继承自IJob对象:
public class MyJob : IJob
{
private object obj = new object(); public void Execute(IJobExecutionContext context)
{
if (ShouldRun())
Run();
} private bool ShouldRun()
{
CommandPengEntities cpEntities = new CommandPengEntities();
try
{
string strStart = DateTime.Now.ToString("yyyy-MM-dd") + " 00:00:00";
string strEnd = DateTime.Now.ToString("yyyy-MM-dd") + " 23:59:59"; DateTime? dtStart = DateTime.Parse(strStart);
DateTime? dtEnd = DateTime.Parse(strEnd); var result = (from p in cpEntities.NxtPrice where p.Time >= dtStart && p.Time <= dtEnd select p).FirstOrDefault();
if (result == null)
{
WriteLog("当天数据不存在,准许插入新数据");
return true; //当天数据未被插入
}
WriteLog("当天数据存在,禁止插入新数据");
return false; //当天数据已经插入
}
catch (Exception ex)
{
WriteLog(ex.InnerException.Message);
return false;
}
finally
{
DisposeContext(cpEntities);
}
} private void Run()
{
string uri = "http://price.agridoor.com.cn/nxt_price/uploads/2014/05/09/20140509_37.xml";
WriteLog("=============================开始进行文件获取========================");
var request = HttpWebRequest.Create(uri);
IAsyncResult iasync = request.BeginGetResponse((iar) =>
{
var requestCallBack = (HttpWebRequest)iar.AsyncState;
var response = requestCallBack.EndGetResponse(iar);
WriteLog("=============================获取文件内容结束========================"); var stream = response.GetResponseStream(); byte[] buffer = null; //将stream保存到MemoryStream中
WriteLog("=============================保存内容到内存流========================");
using (MemoryStream ms = new MemoryStream())
{
int count = 0;
do
{
byte[] buf = new byte[1024];
count = stream.Read(buf, 0, 1024);
ms.Write(buf, 0, count);
}
while (stream.CanRead && count > 0);
buffer = ms.ToArray(); //string txt = Encoding.UTF8.GetString(buffer); //开始反序列化
WriteLog("=============================开始反序列化文件========================");
XmlSerializer ser = new XmlSerializer(typeof(NxtPriceModel));
ms.Position = 0;
var result = (NxtPriceModel)ser.Deserialize(ms); //检测是否为空
WriteLog("=============================检测文件是否为空========================");
if (result == null) return;
if (result.priceItems == null) return;
if (result.priceItems.Count == 0) return; var allItems = result.priceItems;
var totalRecords = result.priceItems.Count;
int batch = 200; //每200条批量提交一次
int totalExecuteCount = (totalRecords % batch == 0) ? (totalRecords / batch) : (totalRecords / batch + 1); //写入数据库
lock (obj)
{
PerformLogicInsert(totalRecords, totalExecuteCount, batch, allItems);
}
}
}, request);
} private void AddEntity(PriceItemModel pModel,CommandPengEntities cpEntities)
{
var itemShouldInsert = AutoMapper.Mapper.Map<NxtPrice>(pModel);
cpEntities.NxtPrice.AddObject(itemShouldInsert);
} private bool CommitEntity(CommandPengEntities cpEntities)
{
int rowAffected = cpEntities.SaveChanges();
if (rowAffected > 0)
return true;
return false;
} private void DisposeContext(CommandPengEntities cpEntities)
{
if (cpEntities != null)
cpEntities.Dispose();
} private void PerformLogicInsert(int totalRecords
, int totalExecuteCount
, int batch
, List<PriceItemModel> allItems)
{
WriteLog("=============================启用事务控制========================"); CommandPengEntities cpEntities = null;
try
{
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TimeSpan(0, 3, 0)))
{
cpEntities = new CommandPengEntities(); for (int i = 0; i < totalExecuteCount; i++)
{
int remainRecords = totalRecords - i * batch;
int isLastBatch = remainRecords / batch;
int loopCounter = 0;
if (isLastBatch > 0) //不是最后一批数据
loopCounter = batch;
else //最后一批数据
loopCounter = remainRecords; for (int j = 0; j < loopCounter; j++)
{
var item = allItems[i * batch + j];
AddEntity(item, cpEntities);
} try
{
bool flag = CommitEntity(cpEntities);
if (flag) WriteLog(string.Format("第{0}批数据插入完毕,共计{1}条", i, loopCounter));
else WriteLog(string.Format("第{0}批数据插入失败", i));
}
catch (Exception ex)
{
WriteLog(ex.Message);
WriteLog(ex.InnerException.Message);
}
}
scope.Complete();
WriteLog("=============================事务提交成功========================");
}
}
finally
{
DisposeContext(cpEntities);
}
} private void WriteLog(string content)
{
string logFile = AppDomain.CurrentDomain.BaseDirectory + "\\log" + DateTime.Now.ToString("yyyyMMdd") + ".txt";
using (FileStream stream = new FileStream(logFile, FileMode.Append))
{
byte[] buffer = Encoding.UTF8.GetBytes(DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss") + " " + content + Environment.NewLine);
stream.Write(buffer, 0, buffer.Length);
stream.Flush();
}
}
}
上面的代码主要是抽取数据,然后将数据写入到数据库中的操作行为.其中需要说到一点的就是Automapper的使用.
由于数据访问层,我直接使用的NxtPrice.edmx,所以会自动生成一个NxtPrice模型出来,这样当我们提交数据的时候,需要将对象进行转换:
private void AddEntity(PriceItemModel pModel,CommandPengEntities cpEntities)
{
var itemShouldInsert = AutoMapper.Mapper.Map<NxtPrice>(pModel);
cpEntities.NxtPrice.AddObject(itemShouldInsert);
}
由于数据库插入对象时NxtPrice,而我们的数据集合对象是NxtPriceModel,所以这里需要将NxtPriceModel中的字段逐一赋值给NxtPrice对象,由于automapper能够提供类似的行为,所以我们采用automapper自动来进行.automapper会对比两个model的异同,只要是字段相同的话,都会自动进行映射.这样就省去了很多的操作步骤.
AutoMapper进行映射前,我们需要在Global.asax中配置一下,方可使用:
private void ModelMapper()
{
//Model and DTO conversion
AutoMapper.Mapper.CreateMap<PriceItemModel, NxtPrice>();
AutoMapper.Mapper.CreateMap<NxtPrice,PriceItemModel>(); }
这样就达到我们的自动映射的要求了.
最后需要说明的是,我们的Job写完之后,需要定时运行,这个该如何做呢?
由于Quartz.net中,工作分为创建计划任务->创建工作内容->创建触发条件->启动 四个步骤,所以这里我们就按照这四个步骤来进行:
private void StartSchedular()
{
IScheduler scheduler = null;
// start up scheduler
// construct a factory
ISchedulerFactory factory = new StdSchedulerFactory();
// get a scheduler
scheduler = factory.GetScheduler();
// start the scheduler
scheduler.Start(); // create job
IJobDetail job = JobBuilder.Create<MyJob>()
.WithIdentity("MyJob", "MyJobs")
.Build(); // create trigger
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("MyJobTrigger", "MyJobs")
// start at 7:30 every day
.StartAt(DateBuilder.DateOf(7,30,0))
.WithSimpleSchedule(x => x.WithInterval(TimeSpan.FromMilliseconds(1)).WithRepeatCount(0))
.Build(); // Schedule the job using the job and trigger
scheduler.ScheduleJob(job, trigger);
}
注释说的很明白了, 其中scheduler对象就是创建的计划任务.
job对象则是我们刚刚新建的MyJob类及其要执行的内容.
trigger对象则是设置触发条件,这里我们设置为每天7:30开始,运行一次即可.
最后一句则是将工作内容和触发器连接起来,以便进行控制.
这样运行之后,我们运行程序,就能够看到系统正常运行了.

数据库的数据都填充以后,我们开始编写我们的webapi代码了.这里由于不是讲解的重点,我就不多说了,但是需要注意一点的是,webapi中可以通过设置路由来允许多个Get方法并存:
config.Routes.MapHttpRoute(
name: "ApiByName",
routeTemplate: "api/{controller}/{action}/{name}",
defaults: null,
constraints: new { name = @"^[a-z]+$" }
);
这样当我们访问多个get方法的时候,就可以通过如下的方式访问了:
http://192.168.0.119/api/PriceItems/GetProductType/?cityName=新乡市
http://192.168.0.119/api/PriceItems/GetProvince/
最后需要说明的是,由于WebApi默认返回JSON格式,所以如果你想接受XML格式的内容,服务端是不需要做任何设置的。你只需要在客户端加上Accept:application/xml即可请求到xml的数据,非常方便。
Quartz.net打造信息抽取器的更多相关文章
- trie树信息抽取之中文数字抽取
这一章讲一下利用trie树对中文数字抽取的算法.trie树是一个非常有用的数据结构,可以应用于大部分文本信息抽取/转换之中,后续会开一个系列,对我在实践中摸索出来的各种抽取算法讲开来.比如中文时间抽取 ...
- 【开源】C#信息抽取系统【招募C#队友】
FDDC2018金融算法挑战赛02-A股上市公司公告信息抽取 更新时间 2018年7月11日 By 带着兔子去旅行 信息抽取是NLP里的一个实用内容.该工具的目标是打造一个泛用的自动信息抽取工具.使得 ...
- 新闻网页通用抽取器GNEv0.04版更新,支持提取正文图片与源代码
GeneralNewsExtractor以下简称GNE是一个新闻网页通用抽取器,能够在不指定任何抽取规则的情况下,把新闻网站的正文提取出来. 我们来看一下它的基本使用方法. 安装 GNE 使用 pip ...
- Atitit.Atiposter 发帖机 信息发布器 v7 q516
Atitit.Atiposter 发帖机 信息发布器 v7 q516 V7 jetty 版本 基本访问改为web版. 这样发布调试 V1 初步实现sina csdn cnblogs V2 实现qz ...
- Scala正则和抽取器:解析方法参数
在<正则表达式基础知识>中概括了正则表达式的基础知识, 本文讲解如何使用正则表达式解析方法参数,从而可以根据 DAO 自动生成 Service. 在做 Java 项目时,常常要根据 DAO ...
- Java中正则表达式、模式匹配与信息抽取
引言 记得几年前在做网页爬虫后的信息抽取时,针对网页源码中隐藏的要提取的信息,比如评论.用户信息等属性信息,直接利用HtmlParser得到.如此做倒是简单,不过利用的是网页的规范的tag标记.其实j ...
- Mp3tag(MP3文件信息修改器) V2.79a 多语绿色版
软件名称: Mp3tag(MP3文件信息修改器) 软件语言: 多国语言 授权方式: 免费软件 运行环境: Win 32位/64位 软件大小: 3.0MB 图片预览: 软件简介: Mp3Tag 是一款m ...
- 窗口信息获取器 Spy4Win v0.20b 中文绿色版
软件名称:窗口信息获取器 Spy4Win v0.20b 中文绿色版软件类别:国产软件运行环境:Windows XP软件语言:简体中文授权方式:免费版软件大小:1.67 MB软件等级:整理时间:2012 ...
- ACL2016信息抽取与知识图谱相关论文掠影
实体关系推理与知识图谱补全 Unsupervised Person Slot Filling based on Graph Mining 作者:Dian Yu, Heng Ji 机构:Computer ...
随机推荐
- IOS之Foundation--plist简说
将集合元素通过代码写入plist文件中 主要用来一览代码写入plist文件,在以后的工作中,可能会有字典一样的集合元素,需要你转为plist文件,那么你是选择手动输入plist文件中呢,还是通过以下代 ...
- swift网络编程入门应用:天气预报
学习来自<小波说雨燕 第二季 网络编程(入门篇)> 工具:xcode6.4 首先在Main.storyborad中添加并设置好三个label做简单的界面显示: import UIKit / ...
- Visual Studio发布Web项目报错:Unable to add 'xxx' to the Web site. Unable to add file 'xxx'. The specified file could not be encrypted.
背景 Visual Studio下的Web项目 现象 发布时遇到Unable to add 'xxx' to the Web site. Unable to add file 'xxx'. The ...
- SQL Server 2008 R2——开发资料搜集
ADO手册 http://download.csdn.net/detail/wlsgzl/8501115 =============================================== ...
- lucene入门
一.lucene简介 Lucene是apache下的一个靠性能的.功能全面的用纯java开发的一个全文搜索引擎库.它几乎适合任何需要全文搜索应用程序,尤其是跨平台.lucene是开源的免费的工程.lu ...
- CentOS 升级内核
因为要安装go,尝试升级内核到 2.6.32.61,出现了一些问题,参考如下文档,多谢各位 http://liaozy.blog.51cto.com/921527/553921 http://www. ...
- 记一次ftp服务器搭建走过的坑
记一次ftp服务器搭建走过的坑 1.安装 ①下载 wget https://security.appspot.com/downloads/vsftpd-3.0.3.tar.gz #要FQ ②解压 ta ...
- log4j日志优先级导致的不输出日志
在sae部署微信代码的时候,发现它的默认日志很不友好,看起来很费劲,详细看了一下说明发现它可以根据log4j的输出级别而分类输出,就拖了一个log4j的xml文件扔进项目代码,然后部署运行,发现没有日 ...
- Mysql在windows下和linux下对表名大小写默认要求的一个细节
今天在虚拟机里搭建项目环境,偷了下懒,直接把本机数据库中的表用sqlyog复制给虚拟机中的数据库,然后开始部署项目,项目一启动提示: Table 'sdmqrt.QRTZ_LOCKS' doesn't ...
- java Annotation Demo
Java 1.5引入了annotation,这个功能非常好用,是用c#等语言借鉴过来的一个特性. 首先编译器本身支持一些像overrides,supresswarning之类的注解. Spring,j ...