【步步为营 Entity Framework+Reporting service开发】-(3) 创建commandline 应用程序 ,使用EF导入数据
接上一章,我们要创建一个commandline 应用程序,通过输入参数,可以让它去指定目录读取文件,并且导入数据库
【设计】
在编写这个程序之前,我们要想想这个程序都需要做什么?
1.读取commandline 参数。一个目录,taskid 和taskName 3个参数。
2.到指定目录读取csv/txt 文件,把csv/txt 转成 DataTable (这里有两种txt文件,trend 和bar)
3.去数据库中查询,如果当前taskid 没有记录则插入记录。否则更新记录。
根据上边需要,我们需要创建如下类/方法:
点击解决方案->Add new project->Console Application
命名为:ReportingSyncer。
proj 右键属性,修改命名空间和程序集名称,与前边ReportingDBManager的前缀保持一致,即:CnBlogsDemos.ReportingSyncer
创建commandline 类:
namespace CnBlogsDemos.ReportingSyncer { using System; using System.Collections.Generic; using System.Linq; using System.Text; public class CommandLine { protected Dictionary<string, string> _command; protected string _defaultConfig; public string InputDir { get { if (_command.ContainsKey("input")) { return _command["input"]; } throw new Exception("The expected parameter /input:[directory] is missing!"); } } public string ConfigFilePath { get { if (_command.Keys.Contains("config")) { return _command["config"]; } return _defaultConfig; } } public string TaskID { get { if (_command.ContainsKey("taskid")) { return _command["taskid"]; } throw new Exception("The expected parameter /taskid:[id] is missing!"); } } public string TaskName { get { if (_command.ContainsKey("taskname")) { return _command["taskname"]; } throw new Exception("The expected parameter /taskname:[name] is missing!"); } } public CommandLine(string[] args) { ParseCommandLine(args); CheckMandatoryParameter(); } public virtual string PrintInputParameters() { StringBuilder sb = new StringBuilder(); sb.Append("Input Parameter:"); foreach (KeyValuePair<string, string> param in _command) { sb.Append(string.Format("/{0}:{1}\t", param.Key, param.Value)); } return sb.ToString(); } private void ParseCommandLine(string[] args) { if (0 == args.Length || (1 == args.Length && (args[0].Contains("?") || args[0].ToLower().Contains("help")))) { Prompt(); throw new ArgumentException(""); } _command = new Dictionary<string, string>(); try { foreach (string arg in args) { string[] param = arg.ToLower().Split(new char[] { ':' }, 2); _command.Add(param[0].Replace("/", ""), param[1]); } } catch (Exception e) { throw new ArgumentException(e.Message); } } protected virtual void CheckMandatoryParameter() { throw new NotImplementedException(); } protected virtual void Prompt() { throw new NotImplementedException(); } } }
创建DataSyncer类(主要的操作都在这),先空着。
修改Programs类:
using System; using CnBlogsDemos.ReportingSyncer; namespace ReportingSyncer { class Program { static int Main(string[] args) { int iRet = -1; try { CommandLine cmd = new CommandLine(args); DataSyncer ds = new DataSyncer(cmd); } catch (Exception e) { //TODO } return iRet; } } }
现在我们开始写DataSyncer类了。
首先添加EF引用。由于刚才我们在上一个proj 引用过了。我们可以去c:\users\你的名字\documents\visual studio 2010\Projects\ReportingSyncer\packages\EntityFramework.4.3.1\lib\net40\EntityFramework.dll找。我的是64位机器。32位的话,应该忽略(X86)。
再添加项目引用-》ReportingDBManager,这样我们才可以使用刚才的DbStoreContext类。
根据上边类图所示:
DataSyncer类如下,具体的地方可以看代码注释:
namespace CnBlogsDemos.ReportingSyncer { using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Data; using CnBlogsDemos.ReportingDBManager; public class DataSyncer { private CommandLine _cmd; public DataSyncer(CommandLine cmd) { // TODO: Complete member initialization this._cmd = cmd; } internal int RunInCommandLineMode() { int iRet = -1; int taskId = 0; string inputDir = _cmd.InputDir; string taskName = string.Empty; string type = string.Empty; //Get taskid int.TryParse(_cmd.TaskID, out taskId); // if after parsing ,task id still equals 0 ,return if (taskId.Equals(0)) { Console.WriteLine("taskid is invalid"); return -1; } // if input directory is not exist parsing return if (!Directory.Exists(inputDir)) { Console.WriteLine("input directory is not exist"); return -1; } else { //read files from input dir var files = Directory.GetFiles(inputDir); if (!files.Any()) { Console.WriteLine("there is not files under input directory"); return -1; } else { foreach (var file in files) { if (file.Contains("trend.txt")) { type = "trend"; InsertAnalysisData(file, taskId, taskName, type); } else if (file.Contains("bar.txt")) { type = "bar"; InsertAnalysisData(file, taskId, taskName, type); } } } } return iRet; } public void InsertAnalysisData(string filePath, int taskId, string taskName, string type) { if (File.Exists(filePath)) { if (type.Equals("bar", StringComparison.OrdinalIgnoreCase)) { var dataRows = this.GetTableFromCSV(filePath).AsEnumerable(); // 创建一个新的 dbstore context 实例 using (DbStoreContext dsc = new DbStoreContext()) { // 如果 Bars表 存在任何相同的TaskID,删除相关记录 if (dsc.Bars.Any(n => n.TaskID.Equals(taskId))) { foreach (var item in dsc.Bars.Where(n => n.TaskID.Equals(taskId))) { dsc.Bars.Remove(item); } } foreach (var row in dataRows) { // 注意 ,这个 row.Field<string>(index);中的index 对应我们csv/txt 文件中的数据列,索引从0开始 Bar b = new Bar(); b.KeyWord = row.Field<string>(0); b.B1Better = Convert.ToInt32(row.Field<string>(1)); b.Equal = Convert.ToInt32(row.Field<string>(2)); b.B2Better = Convert.ToInt32(row.Field<string>(3)); b.Winner = row.Field<string>(4); b.IsActive = true; b.type = type; b.TaskID = taskId; b.TaskName = taskName; dsc.Bars.Add(b); } //保存修改。EF会去执行插入数据到DB,默认开启事务 dsc.SaveChanges(); } } else if (type.Equals("trend", StringComparison.OrdinalIgnoreCase)) { var tailWholePageTrendDataRows = this.GetTableFromCSV(filePath).AsEnumerable(); using (DbStoreContext dsc = new DbStoreContext()) { if (dsc.Trends.Any(n => n.TaskID.Equals(taskId))) { foreach (var item in dsc.Trends.Where(n => n.TaskID.Equals(taskId))) { dsc.Trends.Remove(item); } } foreach (var row in tailWholePageTrendDataRows) { // 注意 ,这个 row.Field<string>(index);中的index 对应我们csv/txt 文件中的数据列,索引从0开始 Trend t = new Trend(); t.id = Convert.ToInt32(row.Field<string>(0)); t.TaskID = taskId; t.Time = Convert.ToDateTime(row.Field<string>(3)); t.B1Better = Convert.ToInt32(row.Field<string>(4)); t.Equal = Convert.ToInt32(row.Field<string>(5)); t.B2Better = Convert.ToInt32(row.Field<string>(6)); t.UnCertain = Convert.ToInt32(row.Field<string>(7)); t.GrandTotal = Convert.ToInt32(row.Field<string>(8)); t.TaskName = taskName; t.IsActive = true; t.type = type; dsc.Trends.Add(t); } dsc.SaveChanges(); } } } } /// <summary> /// GetTableFromCSV file /// </summary> /// <param name="csvFilePath">file path</param> /// <returns>data table</returns> private DataTable GetTableFromCSV(string csvFilePath) { using (StreamReader sr = new StreamReader(csvFilePath)) { string line = sr.ReadLine(); if (!string.IsNullOrEmpty(line)) { string[] columns = line.Split('\t'); DataTable dt = new DataTable(); foreach (string columnName in columns) { dt.Columns.Add(columnName); } while ((line = sr.ReadLine()) != null) { DataRow dr = dt.NewRow(); string[] dataColumns = line.Split('\t'); dt.Rows.Add(dataColumns); } return dt; } throw new Exception(string.Format("File Content in {0} is empty", csvFilePath)); } } } }
倒数第二步:创建app.config。上篇文章说过。我们删除了ReportingDBManager类库中的app.config。
我们要在ReportingSyncer 类库中添加一个app.config指定我们的连接字符串
<?xml version="1.0" encoding="utf-8"?> <configuration> <connectionStrings> <add name="ReportingDataBase" connectionString="Initial Catalog=DemoReportDB;data source=.;Integrated Security=SSPI;Trusted_Connection=Yes" providerName="System.Data.SqlClient" /> </connectionStrings> </configuration>
最后一步,创建测试数据。
让我们创建一个文件夹E:\demos\reportingImprot。下边包含2个txt文件:trend.txt 和 bar.txt
trend.txt:
id taskid taskname time b1 equal b2 uncertain grandtotal 0 1 task1 2012-6-27 200 300 280 220 1000
注意中间是tab分割。
bar.txt:
KeyWord B1Better Equal B2Better Winner 联众 2 0 3 B1 疯狂倒计时 0 0 3 B2 张娜拉 0 1 2 B1 截图软件 1 0 1 B2
因为有汉字,请保存为unicode格式
为了方便模拟用户输入,我们可以预设commandline 参数:
右击ReportingSyncer 项目属性:debug-》Commandline arguments 输入:/input:E:\demos\reportingImprot/taskid:5 /taskname:TestTask。
在实际使用时候可以用cmd找到你的bin目录 执行release /debug 下的 exe文件。
例如 cmd-> c:
->cd e:\C:\Users\ricky\Documents\Visual Studio 2010\Projects\ReportingSyncer\ReportingSyncer\bin\Release reportingsyncer /input:E:\demos\reportingImprot/taskid:5 /taskname:TestTask
。
我们还是在debug 下试试吧。运行,也可以调试看看。
检查本地数据库。太神奇了吧?数据已经导进来了。
检查一下表结构
好,这章就讲到这里。
下一章会继续介绍如何使用vs 创建report