C# 中实现ftp 图片上传功能(多快好省)
前言
此篇讲到的是图片上传功能,每个网站必定会有这样类似的功能,上传文件、上传图片等等。那么接下来,看看我们ef+uploadfile+ftp如何玩转上传图片吧
效果预览
具体实现
一个简单数据库 只有一个主键id,一个身份证正面路径和一个身份证背面路径三个字段。
首先呢,我们把实体类新建好如下:
public class imagemodel:baseentity { /// <summary> /// 用户id /// </summary> public int id { get; set; } /// <summary> ///身份证正面相对路径 /// </summary> public string idprooffront { get; set; } /// <summary> ///身份证背面相对路径 /// </summary> public string idproofback { get; set; } }
其中 我们将身份信息实体继承自baseentity,我们看看baseentity里面是什么东东,代码如下:
public abstract partial class baseentity { public override bool equals(object obj) { return equals(obj as baseentity); } private type getunproxiedtype() { return gettype(); } public virtual bool equals(baseentity other) { if (other == null) return false; if (referenceequals(this, other)) return true; return false; } public override int gethashcode() { return base.gethashcode(); } public static bool operator ==(baseentity x, baseentity y) { return equals(x, y); } public static bool operator !=(baseentity x, baseentity y) { return !(x == y); } }
这里,我们将baseentity定义成一个抽象类,里面包含一些静态方法和重载方法
======================回到html=======
我们先回过头来讲页面,上面演示的是一个很简单的单页面,html代码如下:
<form enctype="multipart/form-data" id="form" action="/home/uploadimage" method="post"> <div class="full_w" style="margin-top: 100px; margin-left: 30%; width: 800px;"> <div class="h_title"> <b>用户上传的文件</b></div> <div class="entry"> 步骤: <span class="red" style="color: red">(上传资料必须是bmp,gif,jpg,jpeg,png类型,不能大于2m)</span> <ol> <li>先按『选择』键选择上传文件;</li> <li>按『上传』键上传文件;</li> <li>按『保存』键保存文件;</li> </ol> </div> <div class="entry"> <div class="sep"></div> </div> <div class="entry"> <div id="wrapper" style="text-align: center; position: relative;"> <div class="form-group"> <input id="uploadfile" type="file" value="浏览..." class="file" name="filename" data-upload-url="#" style="position: absolute; top: 0; right: 0; min-width: 100%; min-height: 100%; text-align: right; opacity: 0; background: none repeat scroll 0 0 transparent; cursor: inherit; display: block;" /> </div> </div> </div> <table> <tbody> <tr> <td class="entry">身份证正面</td> <td> @if (model == null || model.id == null || string.isnullorempty(model.idprooffront)) { <a href="javascript:void(0);" rel="external nofollow" rel="external nofollow" target="_blank" class="winview"> <img style="border: none; width: 150px; height: 100px" src="/img/noupload.png" /> </a> } else { <a href="@(viewbag.pathsrc + model.idprooffront)" rel="external nofollow" target="_blank"class="winview" > <img style="border: none; width: 150px; height: 100px" src="@(viewbag.pathsrc + model.idprooffront)" /> </a> } @html.hiddenfor(m => m.idprooffront) @html.hiddenfor(m => m.id) </td> <td> <a href="javascript:void(0)" rel="external nofollow" rel="external nofollow" class="easyui-linkbutton btnfinleup" data-op="1" data-type="image">上传</a> </td> </tr> <tr> <td class="entry">身份证背面</td> <span id="lblinfosi" style="color: green"></span> <td> @if (model == null || model.id == null || string.isnullorempty(model.idproofback)) { <a href="javascript:void(0);" rel="external nofollow" rel="external nofollow" target="_blank" class="winview"> <img style="border: none; width: 150px; height: 100px" src="/img/noupload.png" /> </a> } else { <a href="@(viewbag.pathsrc + model.idproofback)" rel="external nofollow" target="_blank" class="winview" > <img style="border: none; width: 150px; height: 100px" src="@(viewbag.pathsrc + model.idproofback)" /> </a> } @html.hiddenfor(m => m.idproofback) </td> <td> <a href="javascript:void(0)" rel="external nofollow" rel="external nofollow" class="easyui-linkbutton btnfinleup" data-op="2" data-type="image">上传</a> </td> </tr> </tbody> </table> <div class="entry"> <button class="button" name="btnsaveall" value="保存" id="btnsaveall" style="height: 30px; width: 80px; text-align: center;">保存</button> <a href="/home/index" rel="external nofollow" style="height: 30px; text-align: center; width: 80px; background: #ffffff; border: 1px solid #dcdcdc; border-radius: 2px; color: #444444; cursor: pointer; display: inline-block; font: 700 11px tahoma, arial, sans-serif; margin-right: 10px; padding: 7px 12px 7px 12px; position: relative; text-decoration: none; text-shadow: 0px 1px 0px #ffffff;">返回</a> </div> </div> </form>
现在我们看页面将会毫无样式,所以我们先引用下样式,这里引用了bootstrap 和一个 style2.css ,引入样式后 界面如下:
其中,关于选择框是用了一个js单独封装起来了,是代码中的zh.js,代码如下:
/*! * fileinput chinese translations * * this file must be loaded after 'fileinput.js'. patterns in braces '{}', or * any html markup tags in the messages must not be converted or translated. * * @see http://github.com/kartik-v/bootstrap-fileinput * @author kangqf <kangqingfei@gmail.com> * * note: this file must be saved in utf-8 encoding. */ (function ($) { "use strict"; $.fn.fileinputlocales['zh'] = { filesingle: '文件', fileplural: '个文件', browselabel: '选择 …', removelabel: '移除', removetitle: '清除选中文件', cancellabel: '取消', canceltitle: '取消进行中的上传', uploadlabel: '上传', uploadtitle: '上传选中文件', msgno: '没有', msgnofilesselected: '', msgcancelled: '取消', msgzoommodalheading: '详细预览', msgsizetoosmall: 'file "{name}" (<b>{size} kb</b>) is too small and must be larger than <b>{minsize} kb</b>.', msgsizetoolarge: '文件 "{name}" (<b>{size} kb</b>) 超过了允许大小 <b>{maxsize} kb</b>.', msgfilestooless: '你必须选择最少 <b>{n}</b> {files} 来上传. ', msgfilestoomany: '选择的上传文件个数 <b>({n})</b> 超出最大文件的限制个数 <b>{m}</b>.', msgfilenotfound: '文件 "{name}" 未找到!', msgfilesecured: '安全限制,为了防止读取文件 "{name}".', msgfilenotreadable: '文件 "{name}" 不可读.', msgfilepreviewaborted: '取消 "{name}" 的预览.', msgfilepreviewerror: '读取 "{name}" 时出现了一个错误.', msginvalidfilename: 'invalid or unsupported characters in file name "{name}".', msginvalidfiletype: '不正确的类型 "{name}". 只支持 "{types}" 类型的文件.', msginvalidfileextension: '不正确的文件扩展名 "{name}". 只支持 "{extensions}" 的文件扩展名.', msgfiletypes: { 'image': 'image', 'html': 'html', 'text': 'text', 'video': 'video', 'audio': 'audio', 'flash': 'flash', 'pdf': 'pdf', 'object': 'object' }, msguploadaborted: '该文件上传被中止', msguploadthreshold: 'processing...', msguploadempty: 'no valid data available for upload.', msgvalidationerror: '验证错误', msgloading: '加载第 {index} 文件 共 {files} …', msgprogress: '加载第 {index} 文件 共 {files} - {name} - {percent}% 完成.', msgselected: '{n} {files} 选中', msgfoldersnotallowed: '只支持拖拽文件! 跳过 {n} 拖拽的文件夹.', msgimagewidthsmall: '宽度的图像文件的"{name}"的必须是至少{size}像素.', msgimageheightsmall: '图像文件的"{name}"的高度必须至少为{size}像素.', msgimagewidthlarge: '宽度的图像文件"{name}"不能超过{size}像素.', msgimageheightlarge: '图像文件"{name}"的高度不能超过{size}像素.', msgimageresizeerror: '无法获取的图像尺寸调整。', msgimageresizeexception: '错误而调整图像大小。<pre>{errors}</pre>', msgajaxerror: 'something went wrong with the {operation} operation. please try again later!', msgajaxprogresserror: '{operation} failed', ajaxoperations: { deletethumb: 'file delete', uploadthumb: 'single file upload', uploadbatch: 'batch file upload', uploadextra: 'form data upload' }, dropzonetitle: '拖拽文件到这里 …<br>支持多文件同时上传', dropzoneclicktitle: '<br>(或点击{files}按钮选择文件)', fileactionsettings: { removetitle: '删除文件', uploadtitle: '上传文件', zoomtitle: '查看详情', dragtitle: '移动 / 重置', indicatornewtitle: '没有上传', indicatorsuccesstitle: '上传', indicatorerrortitle: '上传错误', indicatorloadingtitle: '上传 ...' }, previewzoombuttontitles: { prev: '预览上一个文件', next: '预览下一个文件', toggleheader: '缩放', fullscreen: '全屏', borderless: '无边界模式', close: '关闭当前预览' } }; })(window.jquery);
好了,界面大概就整成这样子,现在需要我们实现功能了。首先用js将上传控件初始化一下:
//上传控件初始化 function initfileupload() { $("#uploadfile").fileinput({ //uploadextradata: { kvid: '10' }, language: 'zh', //设置语言 showupload: false, //是否显示上传按钮 uploadasync: true, //默认异步上传 showremove: false, autoreplace: true, maxfilecount: 1, maxfilesize: 10240, dropzonetitle: '拖拽文件到这里 …<br>仅限.pdf, .jpg, .jpeg, .gif', enctype: 'multipart/form-data', fileactionsettings: { uploadclass: "hidden", zoomclass: "hidden", removeclass: "hidden" }, allowedfileextensions: ['jpg', 'png', 'gif', 'pdf'],//接收的文件后缀 msgfilestoomany: "选择上传的文件数量({n}) 超过允许的最大数值{m}!", uploadurl: "/fileupload/fileupload", //上传的地址 }).on("filebatchselected", function (event, files) { $(".file-preview-success").remove(); }) $("#uploadfile").on("fileuploaded", function (event, data, previewid, index) { console.log("accountdata 初始化后 fileopt" + accountdata); console.log("accountdata.fileopt " + accountdata.fileopt); var obj = data.response; if (obj.status != 500 && obj.data != undefined) { var src = accountdata.pathsrc + obj.data.filename; showname = obj.data.filename; //alert(showname); var pid = accountdata.pid; var fileobj = undefined; var field = ""; var ispdf = false; debugger; if (accountdata.fileopt == 1) { fileobj = $("#idprooffront"); //$("#personinfo_oldidfile").val(obj.data.filename); field = "idprooffront"; fileobj.val(obj.data.filename); } else if (accountdata.fileopt == 2) { fileobj = $("#idproofback"); field = "idproofback"; $("#idproofback").val(showname); fileobj.val(obj.data.filename); } //fileobj = $("#idprooffront"); //$("#idprooffront").val(obj.data.filename); //field = "idprooffront"; //fileobj.val(obj.data.filename); fileobj.prev().attr("href", src); src = ispdf == true ? "/content/images/pdf.png" : src; fileobj.prev().find("img").attr("src", src); } else { console.error(obj.data); } }); $('#uploadfile').on('filesuccessremove', function (event, id) { }); $('#uploadfile').on('fileerror', function (event, data, msg) { }); //上传 $(".btnfinleup").click(function () { var filename = $("#uploadfile").val(); var obj = document.getelementbyid("uploadfile"); var type = $(this).attr("data-op"); //alert("当前点击的type是:" + type); var filetype = $(this).attr("data-type"); var files = $("#uploadfile").fileinput("getfilestack"); if (files.length == 0) { layer.msg('请选择要上传的文件', function () { }); return; } var array = filetype.split(","); var selecttype = files[0].type.tolowercase(); var falg = false; for (var i = 0; i < array.length; i++) { if (selecttype.indexof(array[i]) == -1) { falg = false; } else falg = true; if (falg) break; } if (!falg) { layer.msg('只能选择' + filetype + ' 类型的文件', function () { }); return; } accountdata.fileopt = type; $("#uploadfile").fileinput("upload"); }); }
然后再 加载页面的时候调用这个初始化即可
$(function () { initfileupload(); })
ftp上传操作
注意,再initfileupload方法中 上传了图片,会自动访问uploadurl 这个url地址,存放图片,我们先看看这个action如何通过ftp上传指定服务器的。
fileupload方法如下
/// <summary> /// 通过ftp上传指定服务器 /// </summary> /// <returns></returns> public actionresult fileupload() { bool flag = false; string msg = string.empty; int size = convert.toint16(_filesize) * 1024 * 1024; try { dictionary<string, string> filedict = new dictionary<string, string>(); for (int i = 0; i < request.files.count; i++) { httppostedfilebase file = request.files[i]; string extension = path.getextension(file.filename); string[] fileextensions = _fileextension.split(';'); if (fileextensions.any(o => o.equals(extension, stringcomparison.ordinalignorecase))) { if (file.contentlength <= size) { string filename = string.format("{0}_{1}", datetime.now.tostring("yyyymmddhhmmssfff"), path.getfilename(file.filename)); if (file.contentlength <= 10 * 1024 * 1024) { byte[] buffer = new byte[file.contentlength]; file.inputstream.read(buffer, 0, file.contentlength); flag = fileupload(buffer, file.filename, out filename, out msg); } else//图片压缩有问题>> { var stream = imagehelper.getpicthumbnail(file.inputstream, 40); byte[] buffer = new byte[stream.length]; stream.read(buffer, 0, (int)stream.length); flag = fileupload(buffer, file.filename, out filename, out msg); } filedict.add(request.files.allkeys[i], filename); } else { msg = string.format("上传文件不能大于{0}m", _filesize); } } else { msg = string.format("上传的文件类型不正确"); } } return json(new { result = "0", msg = "" + msg, data = filedict }); } catch (exception ex) { return json(new { result = "0", msg = "网络异常,请稍后再试" }); } }
其中 _fileextension _filepath _filesize 是分别从配置文件中读取出来的如下:
private static string _fileextension = configurationmanager.appsettings["filetype"]; private readonly string _filepath = configurationmanager.appsettings["uploadpath"]; private readonly string _filesize = configurationmanager.appsettings["filesizem"];
方法中有一个 fileupload 上传文件的方法 如下:
/// <summary> /// 上传文件 /// </summary> /// <param name="filebytes"></param> /// <param name="originalname"></param> /// <param name="msg"></param> /// <returns></returns> protected bool fileupload(byte[] filebytes, string originalname, out string newfilename, out string msg) { msg = ""; newfilename = ""; try { ftpupfile ftp = new ftpupfile(); newfilename = ftp.upfile(filebytes, originalname); if (string.isnullorempty(newfilename)) { msg = "上传文件时出错!"; return false; } return true; } catch (exception ex) { msg = ex.message; return false; } }
其中
ftpupfile 是一个ftp上传文件帮助类,大家可以直接照搬 ,代码如下:
/// <summary> /// ftp上传文件 /// </summary> public class ftpupfile { string filetype = configurationmanager.appsettings["filetype"]; string ipaddress = configurationmanager.appsettings["ipaddress"]; string username = configurationmanager.appsettings["username"]; string password = configurationmanager.appsettings["password"]; /// <summary> /// ftp上传文件 /// </summary> /// <param name="filename">上传文件路径</param> /// <param name="ftpserverip">ftp服务器的ip和端口</param> /// <param name="ftppath">ftp服务器下的哪个目录</param> /// <param name="ftpuserid">ftp用户名</param> /// <param name="ftppassword">ftp密码</param> public bool upload(string filename, string ftpserverip, string ftppath, string ftpuserid, string ftppassword) { fileinfo fileinf = new fileinfo(filename); string uri = "ftp://" + ftpserverip + "/" + ftppath + "/" + fileinf.name; try { ftpwebrequest reqftp = (ftpwebrequest)ftpwebrequest.create(new uri(uri)); // ftp用户名和密码 reqftp.credentials = new networkcredential(ftpuserid, ftppassword); reqftp.keepalive = false; // 指定执行什么命令 reqftp.method = webrequestmethods.ftp.uploadfile; // 指定数据传输类型 reqftp.usebinary = true; // 上传文件时通知服务器文件的大小 reqftp.contentlength = fileinf.length; //this.invoke(inituprogress, fileinf.length); // 缓冲大小设置为2kb int bufflength = 4096; byte[] buff = new byte[bufflength]; int contentlen; // 打开一个文件流 (system.io.filestream) 去读上传的文件 filestream fs = fileinf.openread(); // 把上传的文件写入流 stream strm = reqftp.getrequeststream(); contentlen = fs.read(buff, 0, bufflength); while (contentlen != 0) { strm.write(buff, 0, contentlen); contentlen = fs.read(buff, 0, bufflength); } // 关闭两个流 strm.close(); strm.dispose(); fs.close(); fs.dispose(); return true; } catch (exception ex) { return false; } } /// <summary> /// 新建目录 /// </summary> /// <param name="ftppath"></param> /// <param name="dirname"></param> public void makedir(string ftppath, string dirname, string username, string password) { try { //实例化ftp连接 ftpwebrequest request = (ftpwebrequest)ftpwebrequest.create(new uri(ftppath + dirname)); // ftp用户名和密码 request.credentials = new networkcredential(username, password); // 默认为true,连接不会被关闭 request.keepalive = false; //指定ftp操作类型为创建目录 request.method = webrequestmethods.ftp.makedirectory; //获取ftp服务器的响应 ftpwebresponse response = (ftpwebresponse)request.getresponse(); response.close(); } catch (exception ex) { //respons } } /// <summary> /// 删除指定文件 /// </summary> /// <param name="ftppath"></param> /// <param name="dirname"></param> /// <param name="username"></param> /// <param name="password"></param> public void deletefile(string ftppath, string username, string password) { try { // string uri = "ftp://" + ftpserverip + "/" + ftppath + "/" + fileinf.name; //ftppath = "ftp://192.168.1.111:2005/2012-12-05/20121206o5catice.docx"; //password = "111"; //username = "yuanluluoli"; //实例化ftp连接 ftpwebrequest request = (ftpwebrequest)ftpwebrequest.create(new uri(ftppath)); request.method = webrequestmethods.ftp.deletefile; // ftp用户名和密码 request.credentials = new networkcredential(username, password); // 默认为true,连接不会被关闭 request.keepalive = false; //获取ftp服务器的响应 ftpwebresponse response = (ftpwebresponse)request.getresponse(); response.close(); } catch (exception ex) { //respons } } /// <summary> /// 检查目录是否存在 /// </summary> /// <param name="ftppath">要检查的目录的路径</param> /// <param name="dirname">要检查的目录名</param> /// <returns>存在返回true,否则false</returns> public bool checkdirectoryexist(string ftppath, string dirname, string username, string password) { bool result = false; try { //实例化ftp连接 ftpwebrequest request = (ftpwebrequest)ftpwebrequest.create(new uri(ftppath)); // ftp用户名和密码 request.credentials = new networkcredential(username, password); request.keepalive = false; //指定ftp操作类型为创建目录 request.method = webrequestmethods.ftp.listdirectorydetails; //获取ftp服务器的响应 ftpwebresponse response = (ftpwebresponse)request.getresponse(); streamreader sr = new streamreader(response.getresponsestream(), encoding.default); stringbuilder str = new stringbuilder(); string line = sr.readline(); while (line != null) { str.append(line); str.append("|"); line = sr.readline(); } string[] datas = str.tostring().split('|'); for (int i = 0; i < datas.length; i++) { if (datas[i].contains("<dir>")) { int index = datas[i].indexof("<dir>"); string name = datas[i].substring(index + 5).trim(); if (name == dirname) { result = true; break; } } } sr.close(); sr.dispose(); response.close(); } catch (exception) { return false; } return result; } /// <summary> /// 上传文件 /// </summary> /// <param name="buffer">文件的byte数组</param> /// <param name="originalname">文件原始名字(带后缀名)</param> /// <param name="perstr">新文件名的前缀</param> /// <returns></returns> public string upfile(byte[] buffer, string originalname, string perstr = "") { if (buffer == null || buffer.length <= 0 || string.isnullorempty(originalname)) throw new argumentexception("参数错误!"); string filepathstr = string.empty; string filepathsql = null; try { string pathstr = perstr + datetime.now.tostring().replace("/", "").replace("-", "").replace(":", "").replace(" ", ""); string rodumlist = generalhelper.getmixpwd(10);//10位随机数 filepathstr = "~/file/" + pathstr + rodumlist + path.getextension(originalname); //stream sr = upfile.postedfile.inputstream; //byte[] file = new byte[sr.length]; //sr.read(file, 0, file.length); streamwriter sw = new streamwriter(httpcontext.current.server.mappath(filepathstr)); sw.basestream.write(buffer, 0, buffer.length); sw.flush(); sw.close(); // file.saveas(httpcontext.current.server.mappath(filepathstr));//把文件上传到服务器的绝对路径上 bool check; string ftppath = datetime.now.tostring("yyyy-mm-dd"); string uri = @"ftp://" + ipaddress + "/"; //检查是否存在此目录文件夹 if (checkdirectoryexist(uri, ftppath, username, password)) { //存在此文件夹就直接上传 check = upload(httpcontext.current.server.mappath(filepathstr), ipaddress, ftppath, username, password); } else { makedir(uri, ftppath, username, password);//创建 check = upload(httpcontext.current.server.mappath(filepathstr), ipaddress, ftppath, username, password); } //成功就更新 if (check) { filepathsql = ftppath + "/" + pathstr + rodumlist + path.getextension(originalname); } //检查是否存在此文件 if (file.exists(httpcontext.current.server.mappath(filepathstr))) { file.delete(httpcontext.current.server.mappath(filepathstr)); } return filepathsql; } catch (exception ex) { file.delete(httpcontext.current.server.mappath(filepathstr)); throw ex; } } /// <summary> /// 上传文件 /// 不修改名字及后缀名 /// </summary> /// <param name="originalfilepath">上传文件的绝对路径</param> /// <returns></returns> public string upfile(string originalfilepath) { if (string.isnullorempty(originalfilepath)) throw new argumentexception("参数错误!"); string filepathsql = null; try { //检查是否存在此文件 if (!file.exists(originalfilepath)) throw new exception("文件不存在!"); //stream sr = upfile.postedfile.inputstream; //byte[] file = new byte[sr.length]; //sr.read(file, 0, file.length); // file.saveas(httpcontext.current.server.mappath(filepathstr));//把文件上传到服务器的绝对路径上 bool check; string ftppath = datetime.now.tostring("yyyy-mm-dd"); string uri = @"ftp://" + ipaddress + "/"; //检查是否存在此目录文件夹 if (checkdirectoryexist(uri, ftppath, username, password)) { //存在此文件夹就直接上传 check = upload(originalfilepath, ipaddress, ftppath, username, password); } else { makedir(uri, ftppath, username, password);//创建 check = upload(originalfilepath, ipaddress, ftppath, username, password); } //成功就更新 if (check) { filepathsql = ftppath + "/" + path.getfilename(originalfilepath); } //检查是否存在此文件 if (file.exists(originalfilepath)) { file.delete(originalfilepath); } return filepathsql; } catch (exception ex) { //file.delete(originalfilepath); throw ex; } } public string ftp_up(htmlinputfile upfile) { //encrypt en = new encrypt(); string filepathstr = string.empty; string filepathsql = null; try { string pathstr = datetime.now.tostring().replace("/", "").replace("-", "").replace(":", "").replace(" ", ""); string rodumlist = generalhelper.getmixpwd(10);//10位随机数 filepathstr = "~/file/" + pathstr + rodumlist + path.getextension(upfile.postedfile.filename); stream sr = upfile.postedfile.inputstream; byte[] file = new byte[sr.length]; sr.read(file, 0, file.length); streamwriter sw = new streamwriter(httpcontext.current.server.mappath(filepathstr)); sw.basestream.write(file, 0, file.length); sw.flush(); sw.close(); sr.flush(); sr.close(); // file.saveas(httpcontext.current.server.mappath(filepathstr));//把文件上传到服务器的绝对路径上 bool check; string ftppath = datetime.now.tostring("yyyy-mm-dd"); string uri = @"ftp://" + ipaddress + "/"; //检查是否存在此目录文件夹 if (checkdirectoryexist(uri, ftppath, username, password)) { //存在此文件夹就直接上传 check = upload(httpcontext.current.server.mappath(filepathstr), ipaddress, ftppath, username, password); } else { makedir(uri, ftppath, username, password);//创建 check = upload(httpcontext.current.server.mappath(filepathstr), ipaddress, ftppath, username, password); } //成功就更新 if (check) { filepathsql = ftppath + "/" + pathstr + rodumlist + path.getextension(upfile.postedfile.filename); } //检查是否存在此文件 if (file.exists(httpcontext.current.server.mappath(filepathstr))) { file.delete(httpcontext.current.server.mappath(filepathstr)); } return filepathsql; } catch (exception) { file.delete(httpcontext.current.server.mappath(filepathstr)); return filepathsql; // response.write("<script>alert(" + ex.message + ");</script>"); } } /// <summary> /// 上传 /// </summary> /// <param name="file"></param> /// <returns></returns> public string ftp_up(httppostedfilebase postedfile) { string filepathstr = string.empty; string filepathsql = null; try { string pathstr = datetime.now.tostring("yyyymmddhhmmss"); string rodumlist = generalhelper.getmixpwd(10);//10位随机数 string filename = system.io.path.getfilename(postedfile.filename); string eextension = path.getextension(filename); string strlocation = httpcontext.current.server.mappath("~/file/"); filepathstr = strlocation + pathstr + rodumlist + eextension; postedfile.saveas(filepathstr); bool check; string ftppath = datetime.now.tostring("yyyy-mm-dd"); string uri = @"ftp://" + ipaddress + "/"; //检查是否存在此目录文件夹 if (checkdirectoryexist(uri, ftppath, username, password)) { //存在此文件夹就直接上传 check = upload(filepathstr, ipaddress, ftppath, username, password); } else { makedir(uri, ftppath, username, password);//创建 check = upload(filepathstr, ipaddress, ftppath, username, password); } //成功就更新 if (check) { filepathsql = ftppath + "/" + pathstr + rodumlist + eextension; } //检查是否存在此文件 if (file.exists(filepathstr)) { file.delete(filepathstr); } return filepathsql; } catch (exception ex) { //检查是否存在此文件 if (file.exists(filepathstr)) { file.delete(filepathstr); } return ""; // response.write("<script>alert(" + ex.message + ");</script>"); } } /// <summary> /// ftp下载文件在服务器目录 /// </summary> /// <param name="pathname">本地保存目录路径和文件名称</param> /// <param name="filename">ftp目录路径和文件名称</param> /// <returns></returns> public bool filedown(string pathname, string filename) { string uri = "ftp://" + ipaddress + "/" + filename; string filename = pathname;//本地保存目录 //创建一个文件流 filestream fs = null; stream responsestream = null; try { //创建一个与ftp服务器联系的ftpwebrequest对象 ftpwebrequest request = (ftpwebrequest)webrequest.create(new uri(uri)); //连接登录ftp服务器 request.credentials = new networkcredential(username, password); request.keepalive = false; //设置请求的方法是ftp文件下载 request.method = webrequestmethods.ftp.downloadfile; //获取一个请求响应对象 ftpwebresponse response = (ftpwebresponse)request.getresponse(); //获取请求的响应流 responsestream = response.getresponsestream(); //判断本地文件是否存在,如果存在,则打开和重写本地文件 if (file.exists(filename)) fs = file.open(filename, filemode.open, fileaccess.readwrite); //判断本地文件是否存在,如果不存在,则创建本地文件 else { fs = file.create(filename); } if (fs != null) { int buffer_count = 65536; byte[] buffer = new byte[buffer_count]; int size = 0; while ((size = responsestream.read(buffer, 0, buffer_count)) > 0) { fs.write(buffer, 0, size); } fs.flush(); fs.close(); responsestream.close(); } return true; } catch (exception ex) { return false; } finally { if (fs != null) fs.close(); if (responsestream != null) responsestream.close(); } } /// <summary> /// 保存和上传图片 /// </summary> /// <param name="imgtwo">需要上传图片</param> /// <param name="date"></param> /// <returns>文件路径</returns> public string saveuploadimg(bitmap imgtwo) { string filepathstr = string.empty; string filepathsql = null; try { string pathstr = datetime.now.tostring().replace("/", "").replace("-", "").replace(":", "").replace(" ", ""); string rodumlist = generalhelper.getmixpwd(10);//10位随机数 filepathstr = "~/file/" + pathstr + rodumlist + ".jpg"; imgtwo.save(httpcontext.current.server.mappath(filepathstr));//把文件上传到服务器的绝对路径上 bool check; string ftppath = datetime.now.tostring("yyyy-mm-dd"); string uri = @"ftp://" + ipaddress + "/"; //检查是否存在此目录文件夹 if (checkdirectoryexist(uri, ftppath, username, password)) { //存在此文件夹就直接上传 check = upload(httpcontext.current.server.mappath(filepathstr), ipaddress, ftppath, username, password); } else { makedir(uri, ftppath, username, password);//创建 check = upload(httpcontext.current.server.mappath(filepathstr), ipaddress, ftppath, username, password); } //成功就更新 if (check) { filepathsql = ftppath + "/" + pathstr + rodumlist + ".jpg"; } //检查是否存在此文件 if (file.exists(httpcontext.current.server.mappath(filepathstr))) { file.delete(httpcontext.current.server.mappath(filepathstr)); } imgtwo.dispose(); return filepathsql; } catch (exception ex) { file.delete(httpcontext.current.server.mappath(filepathstr)); return filepathsql; } } #region /// <summary> /// 文件大小 /// </summary> public bool _file_length(int contentlength) { bool length = false; int filelen = contentlength; if (filelen > 2048 * 1024 == false)//不能超过2m { length = true; } return length; } #endregion //用来获取文件类型 public bool file_pastfilename(string filename) { //bmp, doc, docx, gif, jpg, jpeg, pdf, png, tif, tiff bool isnot = true; string ext = path.getextension(filename); string[] type = filetype.split(';'); for (int i = 0; i < type.length; i++) { if (type[i].tolower() == ext.tolower()) { isnot = false; break; } } return isnot; } }
值得注意的是:帮助类中也从配置文件中读取了存放地址、文件类型、用户名、密码等必要信息。这个大家可以自己再配置文件中配置,配置好了如下所示:
<add key="filesizem" value="10"></add> <add key="filetype" value=".bmp;.gif;.jpg;.jpeg;.png;.pdf" /> <!---本地测试--> <add key="ipaddress" value="路径" /> <!--ftp上传文件的帐号--> <add key="username" value="账号" /> <!--ftp上传文件的密码--> <add key="password" value="密码" /> <!--后台显示图片地址--> <add key="pathsrc" value="路径" />
还有一个类是图片帮助类,代码如下:
public class imagehelper { /// <summary> /// 图片压缩 /// </summary> /// <param name="sfile">原图路径</param> /// <param name="dfile">保存路径</param> /// <param name="flag">压缩质量(数字越小压缩率越高) 1-100</param> /// <param name="dwidth">宽度</param> /// <param name="dheight">高度</param> /// <returns></returns> public static bool savepicthumbnail(string sfile, string dfile, int flag, int dwidth = 0, int dheight = 0) { system.drawing.image isource = system.drawing.image.fromfile(sfile); return savepicthumbnail(dfile, flag, isource, dwidth, dheight); } /// <summary> /// 图片压缩 /// </summary> /// <param name="sfile">原图流</param> /// <param name="dfile">保存路径</param> /// <param name="flag">压缩质量(数字越小压缩率越高) 1-100</param> /// <param name="dwidth">宽度</param> /// <param name="dheight">高度</param> /// <returns></returns> public static bool savepicthumbnail(stream stream, string dfile, int flag, int dwidth = 0, int dheight = 0) { system.drawing.image isource = system.drawing.image.fromstream(stream); return savepicthumbnail(dfile, flag, isource, dwidth, dheight); } #region getpicthumbnail public static stream getpicthumbnail(stream stream ,int flag, int dwidth = 0, int dheight = 0) { system.drawing.image isource = system.drawing.image.fromstream(stream); imageformat tformat = isource.rawformat; int sw = 0, sh = 0; if (dheight == 0 && dwidth == 0) { sw = isource.width; sh = isource.height; } else if (dwidth != 0) { sw = dwidth; sh = isource.height * dwidth / isource.width; } else if (dheight != 0) { sh = dheight; sw = isource.width * dheight / isource.height; } bitmap ob = new bitmap(sw, sh); graphics g = graphics.fromimage(ob); g.clear(color.whitesmoke); g.compositingquality = compositingquality.highquality; g.smoothingmode = smoothingmode.highquality; g.interpolationmode = interpolationmode.highqualitybicubic; g.drawimage(isource, new rectangle(0, 0, sw, sh), 0, 0, isource.width, isource.height, graphicsunit.pixel); g.dispose(); //以下代码为保存图片时,设置压缩质量 encoderparameters ep = new encoderparameters(); long[] qy = new long[1]; qy[0] = flag;//设置压缩的比例1-100 encoderparameter eparam = new encoderparameter(system.drawing.imaging.encoder.quality, qy); ep.param[0] = eparam; memorystream ms = new memorystream(); try { imagecodecinfo[] arrayici = imagecodecinfo.getimageencoders(); imagecodecinfo jpegiciinfo = null; for (int x = 0; x < arrayici.length; x++) { if (arrayici[x].formatdescription.equals("jpeg")) { jpegiciinfo = arrayici[x]; break; } } if (jpegiciinfo != null) { ob.save(ms, jpegiciinfo, ep);//dfile是压缩后的新路径 } else { ob.save(ms, tformat); } return stream; } catch { return null; } finally { isource.dispose(); ob.dispose(); } } public static bool savepicthumbnail(string dfile, int flag, system.drawing.image isource, int dwidth = 0, int dheight = 0) { imageformat tformat = isource.rawformat; int sw = 0, sh = 0; if (dheight == 0 && dwidth == 0) { sw = isource.width; sh = isource.height; } else if (dwidth != 0) { sw = dwidth; sh = isource.height * dwidth / isource.width; } else if (dheight != 0) { sh = dheight; sw = isource.width * dheight / isource.height; } bitmap ob = new bitmap(sw, sh); graphics g = graphics.fromimage(ob); g.clear(color.whitesmoke); g.compositingquality = compositingquality.highquality; g.smoothingmode = smoothingmode.highquality; g.interpolationmode = interpolationmode.highqualitybicubic; g.drawimage(isource, new rectangle(0, 0, sw, sh), 0, 0, isource.width, isource.height, graphicsunit.pixel); g.dispose(); //以下代码为保存图片时,设置压缩质量 encoderparameters ep = new encoderparameters(); long[] qy = new long[1]; qy[0] = flag;//设置压缩的比例1-100 encoderparameter eparam = new encoderparameter(system.drawing.imaging.encoder.quality, qy); ep.param[0] = eparam; try { imagecodecinfo[] arrayici = imagecodecinfo.getimageencoders(); imagecodecinfo jpegiciinfo = null; for (int x = 0; x < arrayici.length; x++) { if (arrayici[x].formatdescription.equals("jpeg")) { jpegiciinfo = arrayici[x]; break; } } if (jpegiciinfo != null) { ob.save(dfile, jpegiciinfo, ep);//dfile是压缩后的新路径 } else { ob.save(dfile, tformat); } return true; } catch { return false; } finally { isource.dispose(); ob.dispose(); } } #endregion }
然后我们通过在fileuploaded的回调函数中绑定url即可显示上传后的图片,效果如下图:
不过这个只是上传到ftp服务器上了,并没有保存到数据库,现在我们要做的就是通过ef的方式将上传的图片保存到数据库。
保存图片
首先我们要建一个操作的接口基类,并且还要约束成baseentity,代码如下
public interface irepository<t> where t : baseentity { /// <summary> /// 根据过滤条件,获取记录 /// </summary> /// <param name="exp"></param> /// <param name="isnotracking">(默认不跟踪实体状态)使用notracking的查询会在性能方面得到改善</param> /// <returns></returns> iqueryable<t> find(expression<func<t, bool>> exp = null, bool isnotracking = true); /// <summary> /// 根据过滤条件,获取记录 /// </summary> /// <param name="wherelambda"></param> /// <param name="isnotracking">(默认不跟踪实体状态)使用notracking的查询会在性能方面得到改善</param> /// <param name="values"></param> /// <returns></returns> iqueryable<t> find(string wherelambda = null, bool isnotracking = true, params object[] values); iqueryable<tentity> othertable<tentity>() where tentity : baseentity; idbset<tentity> set<tentity>() where tentity : baseentity; /// <summary> /// 判断记录是否存在 /// </summary> /// <param name="exp"></param> /// <returns></returns> bool isexist(expression<func<t, bool>> exp); /// <summary> /// 查找单个(如果没找到则返回为null) /// </summary> /// <param name="exp"></param> /// <param name="isnotracking">(默认不跟踪实体状态)使用notracking的查询会在性能方面得到改善</param> /// <returns></returns> t findsingle(expression<func<t, bool>> exp, bool isnotracking = true); /// <summary> /// 得到分页记录 /// </summary> /// <param name="pageindex">the pageindex.</param> /// <param name="pagesize">the pagesize.</param> /// <param name="total">总条数</param> /// <param name="exp">条件谓词</param> /// <param name="orderby">排序,格式如:"id"/"id descending"</param> iqueryable<t> find(int pageindex, int pagesize, out int total, expression<func<t, bool>> exp = null, string orderby = ""); /// <summary> /// 得到分页记录 /// </summary> /// <param name="pageindex">the pageindex.</param> /// <param name="pagesize">the pagesize.</param> /// <param name="total">总条数</param> /// <param name="wherelambda">条件谓词</param> /// <param name="orderby">排序,格式如:"id"/"id descending"</param> iqueryable<t> find(int pageindex, int pagesize, out int total, string wherelambda = "", string orderby = "", params object[] values); /// <summary> /// 根据过滤条件获取记录数 /// </summary> int getcount(expression<func<t, bool>> exp = null); /// <summary> /// 添加实体 /// </summary> /// <param name="entity">the entities.</param> /// <param name="iscomit">是否提交(true)</param> /// <returns></returns> int add(t entity, bool iscomit = true); /// <summary> /// 批量添加 /// </summary> /// <param name="entity">the entities.</param> /// <param name="iscomit">是否提交(true)</param> int adds(list<t> entitis, bool iscomit = true); /// <summary> /// 更新实体(会更新实体的所有属性) /// </summary> /// <param name="entity">the entities.</param> /// <param name="iscomit">是否提交(true)</param> /// <returns></returns> int update(t entity, bool iscomit = true); /// <summary> /// 删除实体 /// </summary> /// <param name="entity">the entities.</param> /// <param name="iscomit">是否提交(true)</param> /// <returns></returns> int delete(t entity, bool iscomit = true); /// <summary> /// 实现按需要只更新部分更新 /// <para>如:update(u =>u.id==1,u =>new user{name="ok"});</para> /// </summary> /// <param name="where">the where.</param> /// <param name="entity">the entity.</param> int update(expression<func<t, bool>> where, expression<func<t, t>> entity); /// <summary> /// 批量按条件删除 /// </summary> /// <param name="exp"></param> int delete(expression<func<t, bool>> exp); /// <summary> /// 对数据库执行给定的 ddl/dml 命令。 /// </summary> /// <param name="sql">sql</param> /// <param name="parameters">参数</param> /// <returns></returns> int executesqlcommand(string sql, params sqlparameter[] parameters); /// <summary> /// 执行sql查询语句 /// </summary> /// <param name="sql"></param> /// <returns></returns> dbrawsqlquery<int> executesqlquery(string sql); /// <summary> /// 执行原始的sql查询 /// </summary> /// <typeparam name="telement">返回的泛型类型</typeparam> /// <param name="sql">sql</param> /// <param name="parameters">参数</param> /// <returns></returns> ilist<telement> sqlquery<telement>(string sql, params sqlparameter[] parameters); /// <summary> /// 开启一个事务 /// </summary> /// <param name="fun"></param> bool begintransaction(func<bool> fun); /// <summary> /// 执行sql语句或存储过程 /// 返回datatable数据集 /// </summary> /// <param name="sql">sql语句或存储过程 例如:exec usp_procedure</param> /// <param name="parameters">参数列表</param> /// <returns></returns> datatable sqlqueryfordatatable(string sql, dbparameter[] parameters); /// <summary> /// 返回datatable数据集 /// </summary> /// <param name="proname">存储过程名</param> /// <param name="parameters">参数列表</param> /// <returns></returns> [obsolete("此方法已过时,请改用sqlqueryfordatatable")] datatable executefordatatable(string proname, idataparameter[] parameters); }
然后需要实现这个基类接口,代码如下:
public class baserepository<t> : irepository<t> where t : baseentity { private dbcontext context { get { dbcontext db = (dbcontext)callcontext.getdata("dbcontext"); if (db == null) { db = new dbcontext(); // db.database.log = o => logginghelper.instance.logging(loglevel.debug, o); callcontext.setdata("dbcontext", db); } return db; } } /// <summary> /// 根据过滤条件,获取记录 /// </summary> /// <param name="exp"></param> /// <param name="isnotracking">(默认不跟踪实体状态)使用notracking的查询会在性能方面得到改善</param> /// <returns></returns> public iqueryable<t> find(expression<func<t, bool>> exp = null, bool isnotracking = true) { return filter(exp, isnotracking); } /// <summary> /// 根据过滤条件,获取记录 /// </summary> /// <param name="wherelambda"></param> /// <param name="isnotracking">(默认不跟踪实体状态)使用notracking的查询会在性能方面得到改善</param> /// <param name="values"></param> /// <returns></returns> public iqueryable<t> find(string wherelambda = null, bool isnotracking = true, params object[] values) { return filter(wherelambda, isnotracking, values); } /// <summary> /// 判断记录是否存在 /// </summary> /// <param name="exp"></param> /// <returns></returns> public bool isexist(expression<func<t, bool>> exp) { return context.set<t>().any(exp); } /// <summary> /// 查找单个(如果没找到则返回为null) /// </summary> /// <param name="exp"></param> /// <param name="isnotracking">(默认不跟踪实体状态)使用notracking的查询会在性能方面得到改善</param> /// <returns></returns> public t findsingle(expression<func<t, bool>> exp, bool isnotracking = true) { return filter(exp, isnotracking).firstordefault(); } /// <summary> /// 得到分页记录 /// </summary> /// <param name="pageindex">the pageindex.</param> /// <param name="pagesize">the pagesize.</param> /// <param name="total">总条数</param> /// <param name="exp">条件谓词</param> /// <param name="orderby">排序,格式如:"id"/"id descending"</param> public iqueryable<t> find(int pageindex, int pagesize, out int total, expression<func<t, bool>> exp = null, string orderby = "") { if (pageindex < 1) pageindex = 1; var query = filter(exp); if (!string.isnullorempty(orderby)) query = query.orderby(orderby); total = query.count(); ///return query.skip(pagesize * (pageindex - 1)).take(pagesize); return null; } /// <summary> /// 得到分页记录 /// </summary> /// <param name="pageindex">the pageindex.</param> /// <param name="pagesize">the pagesize.</param> /// <param name="total">总条数</param> /// <param name="wherelambda">条件谓词</param> /// <param name="orderby">排序,格式如:"id"/"id descending"</param> public iqueryable<t> find(int pageindex, int pagesize, out int total, string wherelambda = "", string orderby = "", params object[] values) { if (pageindex < 1) pageindex = 1; var query = filter(wherelambda); if (string.isnullorempty(orderby)) query = query.orderby(orderby); total = query.count(); // return query.skip(pagesize * (pageindex - 1)).take(pagesize); return null; } /// <summary> /// 根据过滤条件获取记录数 /// </summary> public int getcount(expression<func<t, bool>> exp = null) { return filter(exp).count(); } /// <summary> /// 添加书体 /// </summary> /// <param name="entity">the entities.</param> /// <param name="iscomit">是否提交(true)</param> /// <returns></returns> public int add(t entity, bool iscomit = true) { context.entry<t>(entity).state = system.data.entity.entitystate.added; return iscomit ? context.savechanges() : 0; } /// <summary> /// 批量添加 /// </summary> /// <param name="entity">the entities.</param> /// <param name="iscomit">是否提交(true)</param> public int adds(list<t> entitis, bool iscomit = true) { foreach (t item in entitis) { context.entry<t>(item).state = system.data.entity.entitystate.added; } return iscomit ? context.savechanges() : 0; } /// <summary> /// 更新实体(会更新实体的所有属性) /// </summary> /// <param name="entity">the entities.</param> /// <param name="iscomit">是否提交(true)</param> /// <returns></returns> public int update(t entity, bool iscomit = true) { context.entry(entity).state = system.data.entity.entitystate.modified; return iscomit ? context.savechanges() : 0; } /// <summary> /// 删除实体 /// </summary> /// <param name="entity">the entities.</param> /// <param name="iscomit">是否提交(true)</param> /// <returns></returns> public int delete(t entity, bool iscomit = true) { context.set<t>().remove(entity); return iscomit ? context.savechanges() : 0; } /// <summary> /// 实现按需要只更新部分更新 /// <para>如:update(u =>u.id==1,u =>new user{name="ok"});</para> /// </summary> /// <param name="where">the where.</param> /// <param name="entity">the entity.</param> public int update(expression<func<t, bool>> where, expression<func<t, t>> entity) { return context.set<t>().where(where).update(entity); } /// <summary> /// 批量按条件删除 /// </summary> /// <param name="exp"></param> public int delete(expression<func<t, bool>> exp) { return context.set<t>().where(exp).delete(); } /// <summary> /// 对数据库执行给定的 ddl/dml 命令。 /// </summary> /// <param name="sql">sql</param> /// <param name="parameters">参数</param> /// <returns></returns> public int executesqlcommand(string sql, params sqlparameter[] parameters) { return context.database.executesqlcommand(sql, parameters); } /// <summary> /// 执行原始的sql查询 /// </summary> /// <typeparam name="telement">返回的泛型类型</typeparam> /// <param name="sql">sql</param> /// <param name="parameters">参数</param> /// <returns></returns> public ilist<telement> sqlquery<telement>(string sql, params sqlparameter[] parameters) { return context.database.sqlquery<telement>(sql, parameters).tolist(); } public bool begintransaction(func<bool> fun) { using (var trans = context.database.begintransaction()) { try { var result = fun(); trans.commit(); return result; } catch (exception) { trans.rollback(); return false; } } } private iqueryable<t> filter(expression<func<t, bool>> exp = null, bool isnotracking = true) { var dbset = context.set<t>().asqueryable(); if (exp != null) dbset = dbset.where(exp); if (isnotracking) dbset = dbset.asnotracking(); return dbset; } private iqueryable<t> filter(string wherelambda = null, bool isnotracking = true, params object[] values) { var dbset = context.set<t>().asqueryable(); if (wherelambda != null) dbset = dbset.where(wherelambda, values); if (isnotracking) dbset = dbset.asnotracking(); return dbset; } public iqueryable<t> table { get { return find(wherelambda: null); } } public iqueryable<tentity> othertable<tentity>() where tentity : baseentity { return context.set<tentity>().asnotracking().asqueryable(); } public idbset<tentity> set<tentity>() where tentity : baseentity { return context.set<tentity>(); } /// <summary> /// 执行sql查询语句 /// </summary> /// <param name="sql"></param> /// <returns></returns> public dbrawsqlquery<int> executesqlquery(string sql) { try { return context.database.sqlquery<int>(sql); } catch (exception ex) { throw ex; } } /// <summary> /// 执行sql语句或存储过程 /// 返回datatable数据集 /// </summary> /// <param name="sql">sql语句或存储过程 例如:exec usp_procedure</param> /// <param name="parameters">参数列表</param> /// <returns></returns> public system.data.datatable sqlqueryfordatatable(string sql, params system.data.common.dbparameter[] parameters) { sqlconnection conn = new system.data.sqlclient.sqlconnection(); try { conn = (sqlconnection)context.database.connection; if (conn.state != connectionstate.open) { conn.open(); } sqlcommand cmd = new sqlcommand(); stringbuilder sb = new stringbuilder(sql); if (parameters != null && parameters.length > 0) { if (sql.startswith("exec ", stringcomparison.ordinalignorecase)) sb.appendformat(" {0}", string.join(",", parameters.select(o => o.parametername).toarray())); foreach (var item in parameters) { cmd.parameters.add(item); } } cmd.connection = conn; cmd.commandtext = sb.tostring(); sqldataadapter adapter = new sqldataadapter(cmd); datatable table = new datatable(); adapter.fill(table); conn.close();//连接需要关闭 return table; } catch (exception ex) { throw ex; } finally { if (conn.state == connectionstate.open) conn.close(); } } /// <summary> /// 返回datatable数据集 /// </summary> /// <param name="proname">存储过程名</param> /// <param name="parameters">参数列表</param> /// <returns></returns> [obsolete("此方法已过时,请改用sqlqueryfordatatable")] public system.data.datatable executefordatatable(string proname, idataparameter[] parameters) { try { sqlconnection conn = new system.data.sqlclient.sqlconnection(); conn.connectionstring = context.database.connection.connectionstring; if (conn.state != connectionstate.open) { conn.open(); } sqlcommand cmd = new sqlcommand(proname, conn); cmd.commandtype = commandtype.storedprocedure; if (parameters != null && parameters.length > 0) { foreach (var item in parameters) { cmd.parameters.add(item); } } sqldataadapter adapter = new sqldataadapter(cmd); datatable table = new datatable(); adapter.fill(table); return table; } catch (exception ex) { throw ex; } } }
注意,我们在
这个实现类中定义了一个私有方法,指明了数据库访问的上下文dbcontext
dbcontext类如下:
public class dbcontext : system.data.entity.dbcontext { static dbcontext() { //database.setinitializer(new createdatabaseifnotexists<personaldbcontext>()); } public dbcontext() : base("name=dbcontext") { } protected override void onmodelcreating(dbmodelbuilder modelbuilder) { var typestoregister = assembly.getexecutingassembly().gettypes() .where(type => !string.isnullorempty(type.namespace)) .where(type => type.basetype != null && type.basetype.isgenerictype && type.basetype.getgenerictypedefinition() == typeof(entitytypeconfiguration<>)); foreach (var type in typestoregister) { dynamic configurationinstance = activator.createinstance(type); modelbuilder.configurations.add(configurationinstance); } } }
我们将name=dbcontext 意思是去寻找webconfig中具有相同名称的值 ,所以,我们在配置文件中配置该项如下:
再configuration节点下面新建
<connectionstrings> <add name="dbcontext" providername="system.data.sqlclient" connectionstring="server=.;database=test;uid=sa;pwd=sa;"/> </connectionstrings>
忘了说,这里对实现类中的一些扩展方法做了延伸,新建一个dynamicqueryable类,代码如下:
public static class dynamicqueryable { public static iqueryable<t> where<t>(this iqueryable<t> source, string predicate, params object[] values) { return (iqueryable<t>)where((iqueryable)source, predicate, values); } public static iqueryable where(this iqueryable source, string predicate, params object[] values) { if (source == null) throw new argumentnullexception("source"); if (predicate == null) throw new argumentnullexception("predicate"); lambdaexpression lambda = dynamicexpression.parselambda(source.elementtype, typeof(bool), predicate, values); return source.provider.createquery( expression.call( typeof(queryable), "where", new type[] { source.elementtype }, source.expression, expression.quote(lambda))); } public static iqueryable select(this iqueryable source, string selector, params object[] values) { if (source == null) throw new argumentnullexception("source"); if (selector == null) throw new argumentnullexception("selector"); lambdaexpression lambda = dynamicexpression.parselambda(source.elementtype, null, selector, values); return source.provider.createquery( expression.call( typeof(queryable), "select", new type[] { source.elementtype, lambda.body.type }, source.expression, expression.quote(lambda))); } public static iqueryable<dynamic> select<t>(this iqueryable<t> source, string selector, params object[] values) { return (iqueryable<dynamic>)select((iqueryable)source, selector, values); } public static iqueryable<t> orderby<t>(this iqueryable<t> source, string ordering, params object[] values) { return (iqueryable<t>)orderby((iqueryable)source, ordering, values); } public static iqueryable<t> thenby<t>(this iqueryable<t> source, string ordering, params object[] values) { return (iqueryable<t>)thenby((iqueryable)source, ordering, values); } public static iqueryable thenby(this iqueryable source, string ordering, params object[] values) { if (source == null) throw new argumentnullexception("source"); if (ordering == null) throw new argumentnullexception("ordering"); parameterexpression[] parameters = new parameterexpression[] { expression.parameter(source.elementtype, "") }; expressionparser parser = new expressionparser(parameters, ordering, values); ienumerable<dynamicordering> orderings = parser.parseordering(); expression queryexpr = source.expression; string methodasc = "thenby"; string methoddesc = "thenbydescending"; foreach (dynamicordering o in orderings) { queryexpr = expression.call( typeof(queryable), o.ascending ? methodasc : methoddesc, new type[] { source.elementtype, o.selector.type }, queryexpr, expression.quote(expression.lambda(o.selector, parameters))); } return source.provider.createquery(queryexpr); } public static iqueryable orderby(this iqueryable source, string ordering, params object[] values) { if (source == null) throw new argumentnullexception("source"); if (ordering == null) throw new argumentnullexception("ordering"); parameterexpression[] parameters = new parameterexpression[] { expression.parameter(source.elementtype, "") }; expressionparser parser = new expressionparser(parameters, ordering, values); ienumerable<dynamicordering> orderings = parser.parseordering(); expression queryexpr = source.expression; string methodasc = "orderby"; string methoddesc = "orderbydescending"; foreach (dynamicordering o in orderings) { queryexpr = expression.call( typeof(queryable), o.ascending ? methodasc : methoddesc, new type[] { source.elementtype, o.selector.type }, queryexpr, expression.quote(expression.lambda(o.selector, parameters))); methodasc = "thenby"; methoddesc = "thenbydescending"; } return source.provider.createquery(queryexpr); } public static iqueryable<t> orderby<t>(this iqueryable<t> source, string propertyname, bool ascending) where t : class { type type = typeof(t); propertyinfo property = type.getproperty(propertyname); if (property == null) throw new argumentexception("propertyname", "not exist"); parameterexpression param = expression.parameter(type, "p"); expression propertyaccessexpression = expression.makememberaccess(param, property); lambdaexpression orderbyexpression = expression.lambda(propertyaccessexpression, param); string methodname = ascending ? "orderby" : "orderbydescending"; methodcallexpression resultexp = expression.call(typeof(queryable), methodname, new type[] { type, property.propertytype }, source.expression, expression.quote(orderbyexpression)); return source.provider.createquery<t>(resultexp); } public static iqueryable take(this iqueryable source, int count) { if (source == null) throw new argumentnullexception("source"); return source.provider.createquery( expression.call( typeof(queryable), "take", new type[] { source.elementtype }, source.expression, expression.constant(count))); } public static iqueryable skip(this iqueryable source, int count) { if (source == null) throw new argumentnullexception("source"); return source.provider.createquery( expression.call( typeof(queryable), "skip", new type[] { source.elementtype }, source.expression, expression.constant(count))); } public static iqueryable groupby(this iqueryable source, string keyselector, string elementselector, params object[] values) { if (source == null) throw new argumentnullexception("source"); if (keyselector == null) throw new argumentnullexception("keyselector"); if (elementselector == null) throw new argumentnullexception("elementselector"); lambdaexpression keylambda = dynamicexpression.parselambda(source.elementtype, null, keyselector, values); lambdaexpression elementlambda = dynamicexpression.parselambda(source.elementtype, null, elementselector, values); return source.provider.createquery( expression.call( typeof(queryable), "groupby", new type[] { source.elementtype, keylambda.body.type, elementlambda.body.type }, source.expression, expression.quote(keylambda), expression.quote(elementlambda))); } public static bool any(this iqueryable source) { if (source == null) throw new argumentnullexception("source"); return (bool)source.provider.execute( expression.call( typeof(queryable), "any", new type[] { source.elementtype }, source.expression)); } public static int count(this iqueryable source) { if (source == null) throw new argumentnullexception("source"); return (int)source.provider.execute( expression.call( typeof(queryable), "count", new type[] { source.elementtype }, source.expression)); } } public abstract class dynamicclass { public override string tostring() { propertyinfo[] props = this.gettype().getproperties(bindingflags.instance | bindingflags.public); stringbuilder sb = new stringbuilder(); sb.append("{"); for (int i = 0; i < props.length; i++) { if (i > 0) sb.append(", "); sb.append(props[i].name); sb.append("="); sb.append(props[i].getvalue(this, null)); } sb.append("}"); return sb.tostring(); } } public class dynamicproperty { string name; type type; public dynamicproperty(string name, type type) { if (name == null) throw new argumentnullexception("name"); if (type == null) throw new argumentnullexception("type"); this.name = name; this.type = type; } public string name { get { return name; } } public type type { get { return type; } } } public static class dynamicexpression { public static expression parse(type resulttype, string expression, params object[] values) { expressionparser parser = new expressionparser(null, expression, values); return parser.parse(resulttype); } public static lambdaexpression parselambda(type ittype, type resulttype, string expression, params object[] values) { return parselambda(new parameterexpression[] { expression.parameter(ittype, "") }, resulttype, expression, values); } public static lambdaexpression parselambda(parameterexpression[] parameters, type resulttype, string expression, params object[] values) { expressionparser parser = new expressionparser(parameters, expression, values); return expression.lambda(parser.parse(resulttype), parameters); } public static expression<func<t, s>> parselambda<t, s>(string expression, params object[] values) { return (expression<func<t, s>>)parselambda(typeof(t), typeof(s), expression, values); } public static type createclass(params dynamicproperty[] properties) { return classfactory.instance.getdynamicclass(properties); } public static type createclass(ienumerable<dynamicproperty> properties) { return classfactory.instance.getdynamicclass(properties); } } internal class dynamicordering { public expression selector; public bool ascending; } internal class signature : iequatable<signature> { public dynamicproperty[] properties; public int hashcode; public signature(ienumerable<dynamicproperty> properties) { this.properties = properties.toarray(); hashcode = 0; foreach (dynamicproperty p in properties) { hashcode ^= p.name.gethashcode() ^ p.type.gethashcode(); } } public override int gethashcode() { return hashcode; } public override bool equals(object obj) { return obj is signature ? equals((signature)obj) : false; } public bool equals(signature other) { if (properties.length != other.properties.length) return false; for (int i = 0; i < properties.length; i++) { if (properties[i].name != other.properties[i].name || properties[i].type != other.properties[i].type) return false; } return true; } } internal class classfactory { public static readonly classfactory instance = new classfactory(); static classfactory() { } // trigger lazy initialization of static fields modulebuilder module; dictionary<signature, type> classes; int classcount; readerwriterlock rwlock; private classfactory() { assemblyname name = new assemblyname("dynamicclasses"); assemblybuilder assembly = appdomain.currentdomain.definedynamicassembly(name, assemblybuilderaccess.run); #if enable_linq_partial_trust new reflectionpermission(permissionstate.unrestricted).assert(); #endif try { module = assembly.definedynamicmodule("module"); } finally { #if enable_linq_partial_trust permissionset.revertassert(); #endif } classes = new dictionary<signature, type>(); rwlock = new readerwriterlock(); } public type getdynamicclass(ienumerable<dynamicproperty> properties) { rwlock.acquirereaderlock(timeout.infinite); try { signature signature = new signature(properties); type type; if (!classes.trygetvalue(signature, out type)) { type = createdynamicclass(signature.properties); classes.add(signature, type); } return type; } finally { rwlock.releasereaderlock(); } } type createdynamicclass(dynamicproperty[] properties) { lockcookie cookie = rwlock.upgradetowriterlock(timeout.infinite); try { string typename = "dynamicclass" + (classcount + 1); #if enable_linq_partial_trust new reflectionpermission(permissionstate.unrestricted).assert(); #endif try { typebuilder tb = this.module.definetype(typename, typeattributes.class | typeattributes.public, typeof(dynamicclass)); fieldinfo[] fields = generateproperties(tb, properties); generateequals(tb, fields); generategethashcode(tb, fields); type result = tb.createtype(); classcount++; return result; } finally { #if enable_linq_partial_trust permissionset.revertassert(); #endif } } finally { rwlock.downgradefromwriterlock(ref cookie); } } fieldinfo[] generateproperties(typebuilder tb, dynamicproperty[] properties) { fieldinfo[] fields = new fieldbuilder[properties.length]; for (int i = 0; i < properties.length; i++) { dynamicproperty dp = properties[i]; fieldbuilder fb = tb.definefield("_" + dp.name, dp.type, fieldattributes.private); propertybuilder pb = tb.defineproperty(dp.name, propertyattributes.hasdefault, dp.type, null); methodbuilder mbget = tb.definemethod("get_" + dp.name, methodattributes.public | methodattributes.specialname | methodattributes.hidebysig, dp.type, type.emptytypes); ilgenerator genget = mbget.getilgenerator(); genget.emit(opcodes.ldarg_0); genget.emit(opcodes.ldfld, fb); genget.emit(opcodes.ret); methodbuilder mbset = tb.definemethod("set_" + dp.name, methodattributes.public | methodattributes.specialname | methodattributes.hidebysig, null, new type[] { dp.type }); ilgenerator genset = mbset.getilgenerator(); genset.emit(opcodes.ldarg_0); genset.emit(opcodes.ldarg_1); genset.emit(opcodes.stfld, fb); genset.emit(opcodes.ret); pb.setgetmethod(mbget); pb.setsetmethod(mbset); fields[i] = fb; } return fields; } void generateequals(typebuilder tb, fieldinfo[] fields) { methodbuilder mb = tb.definemethod("equals", methodattributes.public | methodattributes.reuseslot | methodattributes.virtual | methodattributes.hidebysig, typeof(bool), new type[] { typeof(object) }); ilgenerator gen = mb.getilgenerator(); localbuilder other = gen.declarelocal(tb); label next = gen.definelabel(); gen.emit(opcodes.ldarg_1); gen.emit(opcodes.isinst, tb); gen.emit(opcodes.stloc, other); gen.emit(opcodes.ldloc, other); gen.emit(opcodes.brtrue_s, next); gen.emit(opcodes.ldc_i4_0); gen.emit(opcodes.ret); gen.marklabel(next); foreach (fieldinfo field in fields) { type ft = field.fieldtype; type ct = typeof(equalitycomparer<>).makegenerictype(ft); next = gen.definelabel(); gen.emitcall(opcodes.call, ct.getmethod("get_default"), null); gen.emit(opcodes.ldarg_0); gen.emit(opcodes.ldfld, field); gen.emit(opcodes.ldloc, other); gen.emit(opcodes.ldfld, field); gen.emitcall(opcodes.callvirt, ct.getmethod("equals", new type[] { ft, ft }), null); gen.emit(opcodes.brtrue_s, next); gen.emit(opcodes.ldc_i4_0); gen.emit(opcodes.ret); gen.marklabel(next); } gen.emit(opcodes.ldc_i4_1); gen.emit(opcodes.ret); } void generategethashcode(typebuilder tb, fieldinfo[] fields) { methodbuilder mb = tb.definemethod("gethashcode", methodattributes.public | methodattributes.reuseslot | methodattributes.virtual | methodattributes.hidebysig, typeof(int), type.emptytypes); ilgenerator gen = mb.getilgenerator(); gen.emit(opcodes.ldc_i4_0); foreach (fieldinfo field in fields) { type ft = field.fieldtype; type ct = typeof(equalitycomparer<>).makegenerictype(ft); gen.emitcall(opcodes.call, ct.getmethod("get_default"), null); gen.emit(opcodes.ldarg_0); gen.emit(opcodes.ldfld, field); gen.emitcall(opcodes.callvirt, ct.getmethod("gethashcode", new type[] { ft }), null); gen.emit(opcodes.xor); } gen.emit(opcodes.ret); } } public sealed class parseexception : exception { int position; public parseexception(string message, int position) : base(message) { this.position = position; } public int position { get { return position; } } public override string tostring() { return string.format(res.parseexceptionformat, message, position); } } internal class expressionparser { struct token { public tokenid id; public string text; public int pos; } enum tokenid { unknown, end, identifier, stringliteral, integerliteral, realliteral, exclamation, percent, amphersand, openparen, closeparen, asterisk, plus, comma, minus, dot, slash, colon, lessthan, equal, greaterthan, question, openbracket, closebracket, bar, exclamationequal, doubleamphersand, lessthanequal, lessgreater, doubleequal, greaterthanequal, doublebar } interface ilogicalsignatures { void f(bool x, bool y); void f(bool? x, bool? y); } interface iarithmeticsignatures { void f(int x, int y); void f(uint x, uint y); void f(long x, long y); void f(ulong x, ulong y); void f(float x, float y); void f(double x, double y); void f(decimal x, decimal y); void f(int? x, int? y); void f(uint? x, uint? y); void f(long? x, long? y); void f(ulong? x, ulong? y); void f(float? x, float? y); void f(double? x, double? y); void f(decimal? x, decimal? y); } interface irelationalsignatures : iarithmeticsignatures { void f(string x, string y); void f(char x, char y); void f(datetime x, datetime y); void f(timespan x, timespan y); void f(char? x, char? y); void f(datetime? x, datetime? y); void f(timespan? x, timespan? y); } interface iequalitysignatures : irelationalsignatures { void f(bool x, bool y); void f(bool? x, bool? y); } interface iaddsignatures : iarithmeticsignatures { void f(datetime x, timespan y); void f(timespan x, timespan y); void f(datetime? x, timespan? y); void f(timespan? x, timespan? y); } interface isubtractsignatures : iaddsignatures { void f(datetime x, datetime y); void f(datetime? x, datetime? y); } interface inegationsignatures { void f(int x); void f(long x); void f(float x); void f(double x); void f(decimal x); void f(int? x); void f(long? x); void f(float? x); void f(double? x); void f(decimal? x); } interface inotsignatures { void f(bool x); void f(bool? x); } interface ienumerablesignatures { void where(bool predicate); void any(); void any(bool predicate); void all(bool predicate); void count(); void count(bool predicate); void min(object selector); void max(object selector); void sum(int selector); void sum(int? selector); void sum(long selector); void sum(long? selector); void sum(float selector); void sum(float? selector); void sum(double selector); void sum(double? selector); void sum(decimal selector); void sum(decimal? selector); void average(int selector); void average(int? selector); void average(long selector); void average(long? selector); void average(float selector); void average(float? selector); void average(double selector); void average(double? selector); void average(decimal selector); void average(decimal? selector); } static readonly type[] predefinedtypes = { typeof(object), typeof(boolean), typeof(char), typeof(string), typeof(sbyte), typeof(byte), typeof(int16), typeof(uint16), typeof(int32), typeof(uint32), typeof(int64), typeof(uint64), typeof(single), typeof(double), typeof(decimal), typeof(datetime), typeof(timespan), typeof(guid), typeof(math), typeof(convert) }; static readonly expression trueliteral = expression.constant(true); static readonly expression falseliteral = expression.constant(false); static readonly expression nullliteral = expression.constant(null); static readonly string keywordit = "it"; static readonly string keywordiif = "iif"; static readonly string keywordnew = "new"; static dictionary<string, object> keywords; dictionary<string, object> symbols; idictionary<string, object> externals; dictionary<expression, string> literals; parameterexpression it; string text; int textpos; int textlen; char ch; token token; public expressionparser(parameterexpression[] parameters, string expression, object[] values) { if (expression == null) throw new argumentnullexception("expression"); if (keywords == null) keywords = createkeywords(); symbols = new dictionary<string, object>(stringcomparer.ordinalignorecase); literals = new dictionary<expression, string>(); if (parameters != null) processparameters(parameters); if (values != null) processvalues(values); text = expression; textlen = text.length; settextpos(0); nexttoken(); } void processparameters(parameterexpression[] parameters) { foreach (parameterexpression pe in parameters) if (!string.isnullorempty(pe.name)) addsymbol(pe.name, pe); if (parameters.length == 1 && string.isnullorempty(parameters[0].name)) it = parameters[0]; } void processvalues(object[] values) { for (int i = 0; i < values.length; i++) { object value = values[i]; if (i == values.length - 1 && value is idictionary<string, object>) { externals = (idictionary<string, object>)value; } else { addsymbol("@" + i.tostring(system.globalization.cultureinfo.invariantculture), value); } } } void addsymbol(string name, object value) { if (symbols.containskey(name)) throw parseerror(res.duplicateidentifier, name); symbols.add(name, value); } public expression parse(type resulttype) { int exprpos = token.pos; expression expr = parseexpression(); if (resulttype != null) if ((expr = promoteexpression(expr, resulttype, true)) == null) throw parseerror(exprpos, res.expressiontypemismatch, gettypename(resulttype)); validatetoken(tokenid.end, res.syntaxerror); return expr; } #pragma warning disable 0219 public ienumerable<dynamicordering> parseordering() { list<dynamicordering> orderings = new list<dynamicordering>(); while (true) { expression expr = parseexpression(); bool ascending = true; if (tokenidentifieris("asc") || tokenidentifieris("ascending")) { nexttoken(); } else if (tokenidentifieris("desc") || tokenidentifieris("descending")) { nexttoken(); ascending = false; } orderings.add(new dynamicordering { selector = expr, ascending = ascending }); if (token.id != tokenid.comma) break; nexttoken(); } validatetoken(tokenid.end, res.syntaxerror); return orderings; } #pragma warning restore 0219 // ?: operator expression parseexpression() { int errorpos = token.pos; expression expr = parselogicalor(); if (token.id == tokenid.question) { nexttoken(); expression expr1 = parseexpression(); validatetoken(tokenid.colon, res.colonexpected); nexttoken(); expression expr2 = parseexpression(); expr = generateconditional(expr, expr1, expr2, errorpos); } return expr; } // ||, or operator expression parselogicalor() { expression left = parselogicaland(); while (token.id == tokenid.doublebar || tokenidentifieris("or")) { token op = token; nexttoken(); expression right = parselogicaland(); checkandpromoteoperands(typeof(ilogicalsignatures), op.text, ref left, ref right, op.pos); left = expression.orelse(left, right); } return left; } // &&, and operator expression parselogicaland() { expression left = parsecomparison(); while (token.id == tokenid.doubleamphersand || tokenidentifieris("and")) { token op = token; nexttoken(); expression right = parsecomparison(); checkandpromoteoperands(typeof(ilogicalsignatures), op.text, ref left, ref right, op.pos); left = expression.andalso(left, right); } return left; } // =, ==, !=, <>, >, >=, <, <= operators expression parsecomparison() { expression left = parseadditive(); while (token.id == tokenid.equal || token.id == tokenid.doubleequal || token.id == tokenid.exclamationequal || token.id == tokenid.lessgreater || token.id == tokenid.greaterthan || token.id == tokenid.greaterthanequal || token.id == tokenid.lessthan || token.id == tokenid.lessthanequal) { token op = token; nexttoken(); expression right = parseadditive(); bool isequality = op.id == tokenid.equal || op.id == tokenid.doubleequal || op.id == tokenid.exclamationequal || op.id == tokenid.lessgreater; if (isequality && !left.type.isvaluetype && !right.type.isvaluetype) { if (left.type != right.type) { if (left.type.isassignablefrom(right.type)) { right = expression.convert(right, left.type); } else if (right.type.isassignablefrom(left.type)) { left = expression.convert(left, right.type); } else { throw incompatibleoperandserror(op.text, left, right, op.pos); } } } else if (isenumtype(left.type) || isenumtype(right.type)) { if (left.type != right.type) { expression e; if ((e = promoteexpression(right, left.type, true)) != null) { right = e; } else if ((e = promoteexpression(left, right.type, true)) != null) { left = e; } else { throw incompatibleoperandserror(op.text, left, right, op.pos); } } } else { checkandpromoteoperands(isequality ? typeof(iequalitysignatures) : typeof(irelationalsignatures), op.text, ref left, ref right, op.pos); } switch (op.id) { case tokenid.equal: case tokenid.doubleequal: left = generateequal(left, right); break; case tokenid.exclamationequal: case tokenid.lessgreater: left = generatenotequal(left, right); break; case tokenid.greaterthan: left = generategreaterthan(left, right); break; case tokenid.greaterthanequal: left = generategreaterthanequal(left, right); break; case tokenid.lessthan: left = generatelessthan(left, right); break; case tokenid.lessthanequal: left = generatelessthanequal(left, right); break; } } return left; } // +, -, & operators expression parseadditive() { expression left = parsemultiplicative(); while (token.id == tokenid.plus || token.id == tokenid.minus || token.id == tokenid.amphersand) { token op = token; nexttoken(); expression right = parsemultiplicative(); switch (op.id) { case tokenid.plus: if (left.type == typeof(string) || right.type == typeof(string)) goto case tokenid.amphersand; checkandpromoteoperands(typeof(iaddsignatures), op.text, ref left, ref right, op.pos); left = generateadd(left, right); break; case tokenid.minus: checkandpromoteoperands(typeof(isubtractsignatures), op.text, ref left, ref right, op.pos); left = generatesubtract(left, right); break; case tokenid.amphersand: left = generatestringconcat(left, right); break; } } return left; } // *, /, %, mod operators expression parsemultiplicative() { expression left = parseunary(); while (token.id == tokenid.asterisk || token.id == tokenid.slash || token.id == tokenid.percent || tokenidentifieris("mod")) { token op = token; nexttoken(); expression right = parseunary(); checkandpromoteoperands(typeof(iarithmeticsignatures), op.text, ref left, ref right, op.pos); switch (op.id) { case tokenid.asterisk: left = expression.multiply(left, right); break; case tokenid.slash: left = expression.divide(left, right); break; case tokenid.percent: case tokenid.identifier: left = expression.modulo(left, right); break; } } return left; } // -, !, not unary operators expression parseunary() { if (token.id == tokenid.minus || token.id == tokenid.exclamation || tokenidentifieris("not")) { token op = token; nexttoken(); if (op.id == tokenid.minus && (token.id == tokenid.integerliteral || token.id == tokenid.realliteral)) { token.text = "-" + token.text; token.pos = op.pos; return parseprimary(); } expression expr = parseunary(); if (op.id == tokenid.minus) { checkandpromoteoperand(typeof(inegationsignatures), op.text, ref expr, op.pos); expr = expression.negate(expr); } else { checkandpromoteoperand(typeof(inotsignatures), op.text, ref expr, op.pos); expr = expression.not(expr); } return expr; } return parseprimary(); } expression parseprimary() { expression expr = parseprimarystart(); while (true) { if (token.id == tokenid.dot) { nexttoken(); expr = parsememberaccess(null, expr); } else if (token.id == tokenid.openbracket) { expr = parseelementaccess(expr); } else { break; } } return expr; } expression parseprimarystart() { switch (token.id) { case tokenid.identifier: return parseidentifier(); case tokenid.stringliteral: return parsestringliteral(); case tokenid.integerliteral: return parseintegerliteral(); case tokenid.realliteral: return parserealliteral(); case tokenid.openparen: return parseparenexpression(); default: throw parseerror(res.expressionexpected); } } expression parsestringliteral() { validatetoken(tokenid.stringliteral); char quote = token.text[0]; string s = token.text.substring(1, token.text.length - 2); int start = 0; while (true) { int i = s.indexof(quote, start); if (i < 0) break; s = s.remove(i, 1); start = i + 1; } //if (quote == '\'') { // if (s.length != 1) // throw parseerror(res.invalidcharacterliteral); // nexttoken(); // return createliteral(s[0], s); //} nexttoken(); return createliteral(s, s); } expression parseintegerliteral() { validatetoken(tokenid.integerliteral); string text = token.text; if (text[0] != '-') { ulong value; if (!uint64.tryparse(text, out value)) throw parseerror(res.invalidintegerliteral, text); nexttoken(); if (value <= (ulong)int32.maxvalue) return createliteral((int)value, text); if (value <= (ulong)uint32.maxvalue) return createliteral((uint)value, text); if (value <= (ulong)int64.maxvalue) return createliteral((long)value, text); return createliteral(value, text); } else { long value; if (!int64.tryparse(text, out value)) throw parseerror(res.invalidintegerliteral, text); nexttoken(); if (value >= int32.minvalue && value <= int32.maxvalue) return createliteral((int)value, text); return createliteral(value, text); } } expression parserealliteral() { validatetoken(tokenid.realliteral); string text = token.text; object value = null; char last = text[text.length - 1]; if (last == 'f' || last == 'f') { float f; if (single.tryparse(text.substring(0, text.length - 1), out f)) value = f; } else { double d; if (double.tryparse(text, out d)) value = d; } if (value == null) throw parseerror(res.invalidrealliteral, text); nexttoken(); return createliteral(value, text); } expression createliteral(object value, string text) { constantexpression expr = expression.constant(value); literals.add(expr, text); return expr; } expression parseparenexpression() { validatetoken(tokenid.openparen, res.openparenexpected); nexttoken(); expression e = parseexpression(); validatetoken(tokenid.closeparen, res.closeparenoroperatorexpected); nexttoken(); return e; } expression parseidentifier() { validatetoken(tokenid.identifier); object value; if (keywords.trygetvalue(token.text, out value)) { if (value is type) return parsetypeaccess((type)value); if (value == (object)keywordit) return parseit(); if (value == (object)keywordiif) return parseiif(); if (value == (object)keywordnew) return parsenew(); nexttoken(); return (expression)value; } if (symbols.trygetvalue(token.text, out value) || externals != null && externals.trygetvalue(token.text, out value)) { expression expr = value as expression; if (expr == null) { expr = expression.constant(value); } else { lambdaexpression lambda = expr as lambdaexpression; if (lambda != null) return parselambdainvocation(lambda); } nexttoken(); return expr; } if (it != null) return parsememberaccess(null, it); throw parseerror(res.unknownidentifier, token.text); } expression parseit() { if (it == null) throw parseerror(res.noitinscope); nexttoken(); return it; } expression parseiif() { int errorpos = token.pos; nexttoken(); expression[] args = parseargumentlist(); if (args.length != 3) throw parseerror(errorpos, res.iifrequiresthreeargs); return generateconditional(args[0], args[1], args[2], errorpos); } expression generateconditional(expression test, expression expr1, expression expr2, int errorpos) { if (test.type != typeof(bool)) throw parseerror(errorpos, res.firstexprmustbebool); if (expr1.type != expr2.type) { expression expr1as2 = expr2 != nullliteral ? promoteexpression(expr1, expr2.type, true) : null; expression expr2as1 = expr1 != nullliteral ? promoteexpression(expr2, expr1.type, true) : null; if (expr1as2 != null && expr2as1 == null) { expr1 = expr1as2; } else if (expr2as1 != null && expr1as2 == null) { expr2 = expr2as1; } else { string type1 = expr1 != nullliteral ? expr1.type.name : "null"; string type2 = expr2 != nullliteral ? expr2.type.name : "null"; if (expr1as2 != null && expr2as1 != null) throw parseerror(errorpos, res.bothtypesconverttoother, type1, type2); throw parseerror(errorpos, res.neithertypeconvertstoother, type1, type2); } } return expression.condition(test, expr1, expr2); } expression parsenew() { nexttoken(); validatetoken(tokenid.openparen, res.openparenexpected); nexttoken(); list<dynamicproperty> properties = new list<dynamicproperty>(); list<expression> expressions = new list<expression>(); while (true) { int exprpos = token.pos; expression expr = parseexpression(); string propname; if (tokenidentifieris("as")) { nexttoken(); propname = getidentifier(); nexttoken(); } else { memberexpression me = expr as memberexpression; if (me == null) throw parseerror(exprpos, res.missingasclause); propname = me.member.name; } expressions.add(expr); properties.add(new dynamicproperty(propname, expr.type)); if (token.id != tokenid.comma) break; nexttoken(); } validatetoken(tokenid.closeparen, res.closeparenorcommaexpected); nexttoken(); type type = dynamicexpression.createclass(properties); memberbinding[] bindings = new memberbinding[properties.count]; for (int i = 0; i < bindings.length; i++) bindings[i] = expression.bind(type.getproperty(properties[i].name), expressions[i]); return expression.memberinit(expression.new(type), bindings); } expression parselambdainvocation(lambdaexpression lambda) { int errorpos = token.pos; nexttoken(); expression[] args = parseargumentlist(); methodbase method; if (findmethod(lambda.type, "invoke", false, args, out method) != 1) throw parseerror(errorpos, res.argsincompatiblewithlambda); return expression.invoke(lambda, args); } expression parsetypeaccess(type type) { int errorpos = token.pos; nexttoken(); if (token.id == tokenid.question) { if (!type.isvaluetype || isnullabletype(type)) throw parseerror(errorpos, res.typehasnonullableform, gettypename(type)); type = typeof(nullable<>).makegenerictype(type); nexttoken(); } if (token.id == tokenid.openparen) { expression[] args = parseargumentlist(); methodbase method; switch (findbestmethod(type.getconstructors(), args, out method)) { case 0: if (args.length == 1) return generateconversion(args[0], type, errorpos); throw parseerror(errorpos, res.nomatchingconstructor, gettypename(type)); case 1: return expression.new((constructorinfo)method, args); default: throw parseerror(errorpos, res.ambiguousconstructorinvocation, gettypename(type)); } } validatetoken(tokenid.dot, res.dotoropenparenexpected); nexttoken(); return parsememberaccess(type, null); } expression generateconversion(expression expr, type type, int errorpos) { type exprtype = expr.type; if (exprtype == type) return expr; if (exprtype.isvaluetype && type.isvaluetype) { if ((isnullabletype(exprtype) || isnullabletype(type)) && getnonnullabletype(exprtype) == getnonnullabletype(type)) return expression.convert(expr, type); if ((isnumerictype(exprtype) || isenumtype(exprtype)) && (isnumerictype(type)) || isenumtype(type)) return expression.convertchecked(expr, type); } if (exprtype.isassignablefrom(type) || type.isassignablefrom(exprtype) || exprtype.isinterface || type.isinterface) return expression.convert(expr, type); throw parseerror(errorpos, res.cannotconvertvalue, gettypename(exprtype), gettypename(type)); } expression parsememberaccess(type type, expression instance) { if (instance != null) type = instance.type; int errorpos = token.pos; string id = getidentifier(); nexttoken(); if (token.id == tokenid.openparen) { if (instance != null && type != typeof(string)) { type enumerabletype = findgenerictype(typeof(ienumerable<>), type); if (enumerabletype != null) { type elementtype = enumerabletype.getgenericarguments()[0]; return parseaggregate(instance, elementtype, id, errorpos); } } expression[] args = parseargumentlist(); methodbase mb; switch (findmethod(type, id, instance == null, args, out mb)) { case 0: throw parseerror(errorpos, res.noapplicablemethod, id, gettypename(type)); case 1: methodinfo method = (methodinfo)mb; if (!ispredefinedtype(method.declaringtype)) throw parseerror(errorpos, res.methodsareinaccessible, gettypename(method.declaringtype)); if (method.returntype == typeof(void)) throw parseerror(errorpos, res.methodisvoid, id, gettypename(method.declaringtype)); return expression.call(instance, (methodinfo)method, args); default: throw parseerror(errorpos, res.ambiguousmethodinvocation, id, gettypename(type)); } } else { memberinfo member = findpropertyorfield(type, id, instance == null); if (member == null) throw parseerror(errorpos, res.unknownpropertyorfield, id, gettypename(type)); return member is propertyinfo ? expression.property(instance, (propertyinfo)member) : expression.field(instance, (fieldinfo)member); } } static type findgenerictype(type generic, type type) { while (type != null && type != typeof(object)) { if (type.isgenerictype && type.getgenerictypedefinition() == generic) return type; if (generic.isinterface) { foreach (type intftype in type.getinterfaces()) { type found = findgenerictype(generic, intftype); if (found != null) return found; } } type = type.basetype; } return null; } expression parseaggregate(expression instance, type elementtype, string methodname, int errorpos) { parameterexpression outerit = it; parameterexpression innerit = expression.parameter(elementtype, ""); it = innerit; expression[] args = parseargumentlist(); it = outerit; methodbase signature; if (findmethod(typeof(ienumerablesignatures), methodname, false, args, out signature) != 1) throw parseerror(errorpos, res.noapplicableaggregate, methodname); type[] typeargs; if (signature.name == "min" || signature.name == "max") { typeargs = new type[] { elementtype, args[0].type }; } else { typeargs = new type[] { elementtype }; } if (args.length == 0) { args = new expression[] { instance }; } else { args = new expression[] { instance, expression.lambda(args[0], innerit) }; } return expression.call(typeof(enumerable), signature.name, typeargs, args); } expression[] parseargumentlist() { validatetoken(tokenid.openparen, res.openparenexpected); nexttoken(); expression[] args = token.id != tokenid.closeparen ? parsearguments() : new expression[0]; validatetoken(tokenid.closeparen, res.closeparenorcommaexpected); nexttoken(); return args; } expression[] parsearguments() { list<expression> arglist = new list<expression>(); while (true) { arglist.add(parseexpression()); if (token.id != tokenid.comma) break; nexttoken(); } return arglist.toarray(); } expression parseelementaccess(expression expr) { int errorpos = token.pos; validatetoken(tokenid.openbracket, res.openparenexpected); nexttoken(); expression[] args = parsearguments(); validatetoken(tokenid.closebracket, res.closebracketorcommaexpected); nexttoken(); if (expr.type.isarray) { if (expr.type.getarrayrank() != 1 || args.length != 1) throw parseerror(errorpos, res.cannotindexmultidimarray); expression index = promoteexpression(args[0], typeof(int), true); if (index == null) throw parseerror(errorpos, res.invalidindex); return expression.arrayindex(expr, index); } else { methodbase mb; switch (findindexer(expr.type, args, out mb)) { case 0: throw parseerror(errorpos, res.noapplicableindexer, gettypename(expr.type)); case 1: return expression.call(expr, (methodinfo)mb, args); default: throw parseerror(errorpos, res.ambiguousindexerinvocation, gettypename(expr.type)); } } } static bool ispredefinedtype(type type) { foreach (type t in predefinedtypes) if (t == type) return true; return false; } static bool isnullabletype(type type) { return type.isgenerictype && type.getgenerictypedefinition() == typeof(nullable<>); } static type getnonnullabletype(type type) { return isnullabletype(type) ? type.getgenericarguments()[0] : type; } static string gettypename(type type) { type basetype = getnonnullabletype(type); string s = basetype.name; if (type != basetype) s += '?'; return s; } static bool isnumerictype(type type) { return getnumerictypekind(type) != 0; } static bool issignedintegraltype(type type) { return getnumerictypekind(type) == 2; } static bool isunsignedintegraltype(type type) { return getnumerictypekind(type) == 3; } static int getnumerictypekind(type type) { type = getnonnullabletype(type); if (type.isenum) return 0; switch (type.gettypecode(type)) { case typecode.char: case typecode.single: case typecode.double: case typecode.decimal: return 1; case typecode.sbyte: case typecode.int16: case typecode.int32: case typecode.int64: return 2; case typecode.byte: case typecode.uint16: case typecode.uint32: case typecode.uint64: return 3; default: return 0; } } static bool isenumtype(type type) { return getnonnullabletype(type).isenum; } void checkandpromoteoperand(type signatures, string opname, ref expression expr, int errorpos) { expression[] args = new expression[] { expr }; methodbase method; if (findmethod(signatures, "f", false, args, out method) != 1) throw parseerror(errorpos, res.incompatibleoperand, opname, gettypename(args[0].type)); expr = args[0]; } void checkandpromoteoperands(type signatures, string opname, ref expression left, ref expression right, int errorpos) { expression[] args = new expression[] { left, right }; methodbase method; if (findmethod(signatures, "f", false, args, out method) != 1) throw incompatibleoperandserror(opname, left, right, errorpos); left = args[0]; right = args[1]; } exception incompatibleoperandserror(string opname, expression left, expression right, int pos) { return parseerror(pos, res.incompatibleoperands, opname, gettypename(left.type), gettypename(right.type)); } memberinfo findpropertyorfield(type type, string membername, bool staticaccess) { bindingflags flags = bindingflags.public | bindingflags.declaredonly | (staticaccess ? bindingflags.static : bindingflags.instance); foreach (type t in selfandbasetypes(type)) { memberinfo[] members = t.findmembers(membertypes.property | membertypes.field, flags, type.filternameignorecase, membername); if (members.length != 0) return members[0]; } return null; } int findmethod(type type, string methodname, bool staticaccess, expression[] args, out methodbase method) { bindingflags flags = bindingflags.public | bindingflags.declaredonly | (staticaccess ? bindingflags.static : bindingflags.instance); foreach (type t in selfandbasetypes(type)) { memberinfo[] members = t.findmembers(membertypes.method, flags, type.filternameignorecase, methodname); int count = findbestmethod(members.cast<methodbase>(), args, out method); if (count != 0) return count; } method = null; return 0; } int findindexer(type type, expression[] args, out methodbase method) { foreach (type t in selfandbasetypes(type)) { memberinfo[] members = t.getdefaultmembers(); if (members.length != 0) { ienumerable<methodbase> methods = members. oftype<propertyinfo>(). select(p => (methodbase)p.getgetmethod()). where(m => m != null); int count = findbestmethod(methods, args, out method); if (count != 0) return count; } } method = null; return 0; } static ienumerable<type> selfandbasetypes(type type) { if (type.isinterface) { list<type> types = new list<type>(); addinterface(types, type); return types; } return selfandbaseclasses(type); } static ienumerable<type> selfandbaseclasses(type type) { while (type != null) { yield return type; type = type.basetype; } } static void addinterface(list<type> types, type type) { if (!types.contains(type)) { types.add(type); foreach (type t in type.getinterfaces()) addinterface(types, t); } } class methoddata { public methodbase methodbase; public parameterinfo[] parameters; public expression[] args; } int findbestmethod(ienumerable<methodbase> methods, expression[] args, out methodbase method) { methoddata[] applicable = methods. select(m => new methoddata { methodbase = m, parameters = m.getparameters() }). where(m => isapplicable(m, args)). toarray(); if (applicable.length > 1) { applicable = applicable. where(m => applicable.all(n => m == n || isbetterthan(args, m, n))). toarray(); } if (applicable.length == 1) { methoddata md = applicable[0]; for (int i = 0; i < args.length; i++) args[i] = md.args[i]; method = md.methodbase; } else { method = null; } return applicable.length; } bool isapplicable(methoddata method, expression[] args) { if (method.parameters.length != args.length) return false; expression[] promotedargs = new expression[args.length]; for (int i = 0; i < args.length; i++) { parameterinfo pi = method.parameters[i]; if (pi.isout) return false; expression promoted = promoteexpression(args[i], pi.parametertype, false); if (promoted == null) return false; promotedargs[i] = promoted; } method.args = promotedargs; return true; } expression promoteexpression(expression expr, type type, bool exact) { if (expr.type == type) return expr; if (expr is constantexpression) { constantexpression ce = (constantexpression)expr; if (ce == nullliteral) { if (!type.isvaluetype || isnullabletype(type)) return expression.constant(null, type); } else { string text; if (literals.trygetvalue(ce, out text)) { type target = getnonnullabletype(type); object value = null; switch (type.gettypecode(ce.type)) { case typecode.int32: case typecode.uint32: case typecode.int64: case typecode.uint64: value = parsenumber(text, target); break; case typecode.double: if (target == typeof(decimal)) value = parsenumber(text, target); break; case typecode.string: value = parseenum(text, target); break; } if (value != null) return expression.constant(value, type); } } } if (iscompatiblewith(expr.type, type)) { if (type.isvaluetype || exact) return expression.convert(expr, type); return expr; } return null; } static object parsenumber(string text, type type) { switch (type.gettypecode(getnonnullabletype(type))) { case typecode.sbyte: sbyte sb; if (sbyte.tryparse(text, out sb)) return sb; break; case typecode.byte: byte b; if (byte.tryparse(text, out b)) return b; break; case typecode.int16: short s; if (short.tryparse(text, out s)) return s; break; case typecode.uint16: ushort us; if (ushort.tryparse(text, out us)) return us; break; case typecode.int32: int i; if (int.tryparse(text, out i)) return i; break; case typecode.uint32: uint ui; if (uint.tryparse(text, out ui)) return ui; break; case typecode.int64: long l; if (long.tryparse(text, out l)) return l; break; case typecode.uint64: ulong ul; if (ulong.tryparse(text, out ul)) return ul; break; case typecode.single: float f; if (float.tryparse(text, out f)) return f; break; case typecode.double: double d; if (double.tryparse(text, out d)) return d; break; case typecode.decimal: decimal e; if (decimal.tryparse(text, out e)) return e; break; } return null; } static object parseenum(string name, type type) { if (type.isenum) { memberinfo[] memberinfos = type.findmembers(membertypes.field, bindingflags.public | bindingflags.declaredonly | bindingflags.static, type.filternameignorecase, name); if (memberinfos.length != 0) return ((fieldinfo)memberinfos[0]).getvalue(null); } return null; } static bool iscompatiblewith(type source, type target) { if (source == target) return true; if (!target.isvaluetype) return target.isassignablefrom(source); type st = getnonnullabletype(source); type tt = getnonnullabletype(target); if (st != source && tt == target) return false; typecode sc = st.isenum ? typecode.object : type.gettypecode(st); typecode tc = tt.isenum ? typecode.object : type.gettypecode(tt); switch (sc) { case typecode.sbyte: switch (tc) { case typecode.sbyte: case typecode.int16: case typecode.int32: case typecode.int64: case typecode.single: case typecode.double: case typecode.decimal: return true; } break; case typecode.byte: switch (tc) { case typecode.byte: case typecode.int16: case typecode.uint16: case typecode.int32: case typecode.uint32: case typecode.int64: case typecode.uint64: case typecode.single: case typecode.double: case typecode.decimal: return true; } break; case typecode.int16: switch (tc) { case typecode.int16: case typecode.int32: case typecode.int64: case typecode.single: case typecode.double: case typecode.decimal: return true; } break; case typecode.uint16: switch (tc) { case typecode.uint16: case typecode.int32: case typecode.uint32: case typecode.int64: case typecode.uint64: case typecode.single: case typecode.double: case typecode.decimal: return true; } break; case typecode.int32: switch (tc) { case typecode.int32: case typecode.int64: case typecode.single: case typecode.double: case typecode.decimal: return true; } break; case typecode.uint32: switch (tc) { case typecode.uint32: case typecode.int64: case typecode.uint64: case typecode.single: case typecode.double: case typecode.decimal: return true; } break; case typecode.int64: switch (tc) { case typecode.int64: case typecode.single: case typecode.double: case typecode.decimal: return true; } break; case typecode.uint64: switch (tc) { case typecode.uint64: case typecode.single: case typecode.double: case typecode.decimal: return true; } break; case typecode.single: switch (tc) { case typecode.single: case typecode.double: return true; } break; default: if (st == tt) return true; break; } return false; } static bool isbetterthan(expression[] args, methoddata m1, methoddata m2) { bool better = false; for (int i = 0; i < args.length; i++) { int c = compareconversions(args[i].type, m1.parameters[i].parametertype, m2.parameters[i].parametertype); if (c < 0) return false; if (c > 0) better = true; } return better; } // return 1 if s -> t1 is a better conversion than s -> t2 // return -1 if s -> t2 is a better conversion than s -> t1 // return 0 if neither conversion is better static int compareconversions(type s, type t1, type t2) { if (t1 == t2) return 0; if (s == t1) return 1; if (s == t2) return -1; bool t1t2 = iscompatiblewith(t1, t2); bool t2t1 = iscompatiblewith(t2, t1); if (t1t2 && !t2t1) return 1; if (t2t1 && !t1t2) return -1; if (issignedintegraltype(t1) && isunsignedintegraltype(t2)) return 1; if (issignedintegraltype(t2) && isunsignedintegraltype(t1)) return -1; return 0; } expression generateequal(expression left, expression right) { return expression.equal(left, right); } expression generatenotequal(expression left, expression right) { return expression.notequal(left, right); } expression generategreaterthan(expression left, expression right) { if (left.type == typeof(string)) { return expression.greaterthan( generatestaticmethodcall("compare", left, right), expression.constant(0) ); } return expression.greaterthan(left, right); } expression generategreaterthanequal(expression left, expression right) { if (left.type == typeof(string)) { return expression.greaterthanorequal( generatestaticmethodcall("compare", left, right), expression.constant(0) ); } return expression.greaterthanorequal(left, right); } expression generatelessthan(expression left, expression right) { if (left.type == typeof(string)) { return expression.lessthan( generatestaticmethodcall("compare", left, right), expression.constant(0) ); } return expression.lessthan(left, right); } expression generatelessthanequal(expression left, expression right) { if (left.type == typeof(string)) { return expression.lessthanorequal( generatestaticmethodcall("compare", left, right), expression.constant(0) ); } return expression.lessthanorequal(left, right); } expression generateadd(expression left, expression right) { if (left.type == typeof(string) && right.type == typeof(string)) { return generatestaticmethodcall("concat", left, right); } return expression.add(left, right); } expression generatesubtract(expression left, expression right) { return expression.subtract(left, right); } expression generatestringconcat(expression left, expression right) { return expression.call( null, typeof(string).getmethod("concat", new[] { typeof(object), typeof(object) }), new[] { left, right }); } methodinfo getstaticmethod(string methodname, expression left, expression right) { return left.type.getmethod(methodname, new[] { left.type, right.type }); } expression generatestaticmethodcall(string methodname, expression left, expression right) { return expression.call(null, getstaticmethod(methodname, left, right), new[] { left, right }); } void settextpos(int pos) { textpos = pos; ch = textpos < textlen ? text[textpos] : '\0'; } void nextchar() { if (textpos < textlen) textpos++; ch = textpos < textlen ? text[textpos] : '\0'; } void nexttoken() { while (char.iswhitespace(ch)) nextchar(); tokenid t; int tokenpos = textpos; switch (ch) { case '!': nextchar(); if (ch == '=') { nextchar(); t = tokenid.exclamationequal; } else { t = tokenid.exclamation; } break; case '%': nextchar(); t = tokenid.percent; break; case '&': nextchar(); if (ch == '&') { nextchar(); t = tokenid.doubleamphersand; } else { t = tokenid.amphersand; } break; case '(': nextchar(); t = tokenid.openparen; break; case ')': nextchar(); t = tokenid.closeparen; break; case '*': nextchar(); t = tokenid.asterisk; break; case '+': nextchar(); t = tokenid.plus; break; case ',': nextchar(); t = tokenid.comma; break; case '-': nextchar(); t = tokenid.minus; break; case '.': nextchar(); t = tokenid.dot; break; case '/': nextchar(); t = tokenid.slash; break; case ':': nextchar(); t = tokenid.colon; break; case '<': nextchar(); if (ch == '=') { nextchar(); t = tokenid.lessthanequal; } else if (ch == '>') { nextchar(); t = tokenid.lessgreater; } else { t = tokenid.lessthan; } break; case '=': nextchar(); if (ch == '=') { nextchar(); t = tokenid.doubleequal; } else { t = tokenid.equal; } break; case '>': nextchar(); if (ch == '=') { nextchar(); t = tokenid.greaterthanequal; } else { t = tokenid.greaterthan; } break; case '?': nextchar(); t = tokenid.question; break; case '[': nextchar(); t = tokenid.openbracket; break; case ']': nextchar(); t = tokenid.closebracket; break; case '|': nextchar(); if (ch == '|') { nextchar(); t = tokenid.doublebar; } else { t = tokenid.bar; } break; case '"': case '\'': char quote = ch; do { nextchar(); while (textpos < textlen && ch != quote) nextchar(); if (textpos == textlen) throw parseerror(textpos, res.unterminatedstringliteral); nextchar(); } while (ch == quote); t = tokenid.stringliteral; break; default: if (char.isletter(ch) || ch == '@' || ch == '_') { do { nextchar(); } while (char.isletterordigit(ch) || ch == '_'); t = tokenid.identifier; break; } if (char.isdigit(ch)) { t = tokenid.integerliteral; do { nextchar(); } while (char.isdigit(ch)); if (ch == '.') { t = tokenid.realliteral; nextchar(); validatedigit(); do { nextchar(); } while (char.isdigit(ch)); } if (ch == 'e' || ch == 'e') { t = tokenid.realliteral; nextchar(); if (ch == '+' || ch == '-') nextchar(); validatedigit(); do { nextchar(); } while (char.isdigit(ch)); } if (ch == 'f' || ch == 'f') nextchar(); break; } if (textpos == textlen) { t = tokenid.end; break; } throw parseerror(textpos, res.invalidcharacter, ch); } token.id = t; token.text = text.substring(tokenpos, textpos - tokenpos); token.pos = tokenpos; } bool tokenidentifieris(string id) { return token.id == tokenid.identifier && string.equals(id, token.text, stringcomparison.ordinalignorecase); } string getidentifier() { validatetoken(tokenid.identifier, res.identifierexpected); string id = token.text; if (id.length > 1 && id[0] == '@') id = id.substring(1); return id; } void validatedigit() { if (!char.isdigit(ch)) throw parseerror(textpos, res.digitexpected); } void validatetoken(tokenid t, string errormessage) { if (token.id != t) throw parseerror(errormessage); } void validatetoken(tokenid t) { if (token.id != t) throw parseerror(res.syntaxerror); } exception parseerror(string format, params object[] args) { return parseerror(token.pos, format, args); } exception parseerror(int pos, string format, params object[] args) { return new parseexception(string.format(system.globalization.cultureinfo.currentculture, format, args), pos); } static dictionary<string, object> createkeywords() { dictionary<string, object> d = new dictionary<string, object>(stringcomparer.ordinalignorecase); d.add("true", trueliteral); d.add("false", falseliteral); d.add("null", nullliteral); d.add(keywordit, keywordit); d.add(keywordiif, keywordiif); d.add(keywordnew, keywordnew); foreach (type type in predefinedtypes) d.add(type.name, type); return d; } } static class res { public const string duplicateidentifier = "the identifier '{0}' was defined more than once"; public const string expressiontypemismatch = "expression of type '{0}' expected"; public const string expressionexpected = "expression expected"; public const string invalidcharacterliteral = "character literal must contain exactly one character"; public const string invalidintegerliteral = "invalid integer literal '{0}'"; public const string invalidrealliteral = "invalid real literal '{0}'"; public const string unknownidentifier = "unknown identifier '{0}'"; public const string noitinscope = "no 'it' is in scope"; public const string iifrequiresthreeargs = "the 'iif' function requires three arguments"; public const string firstexprmustbebool = "the first expression must be of type 'boolean'"; public const string bothtypesconverttoother = "both of the types '{0}' and '{1}' convert to the other"; public const string neithertypeconvertstoother = "neither of the types '{0}' and '{1}' converts to the other"; public const string missingasclause = "expression is missing an 'as' clause"; public const string argsincompatiblewithlambda = "argument list incompatible with lambda expression"; public const string typehasnonullableform = "type '{0}' has no nullable form"; public const string nomatchingconstructor = "no matching constructor in type '{0}'"; public const string ambiguousconstructorinvocation = "ambiguous invocation of '{0}' constructor"; public const string cannotconvertvalue = "a value of type '{0}' cannot be converted to type '{1}'"; public const string noapplicablemethod = "no applicable method '{0}' exists in type '{1}'"; public const string methodsareinaccessible = "methods on type '{0}' are not accessible"; public const string methodisvoid = "method '{0}' in type '{1}' does not return a value"; public const string ambiguousmethodinvocation = "ambiguous invocation of method '{0}' in type '{1}'"; public const string unknownpropertyorfield = "no property or field '{0}' exists in type '{1}'"; public const string noapplicableaggregate = "no applicable aggregate method '{0}' exists"; public const string cannotindexmultidimarray = "indexing of multi-dimensional arrays is not supported"; public const string invalidindex = "array index must be an integer expression"; public const string noapplicableindexer = "no applicable indexer exists in type '{0}'"; public const string ambiguousindexerinvocation = "ambiguous invocation of indexer in type '{0}'"; public const string incompatibleoperand = "operator '{0}' incompatible with operand type '{1}'"; public const string incompatibleoperands = "operator '{0}' incompatible with operand types '{1}' and '{2}'"; public const string unterminatedstringliteral = "unterminated string literal"; public const string invalidcharacter = "syntax error '{0}'"; public const string digitexpected = "digit expected"; public const string syntaxerror = "syntax error"; public const string tokenexpected = "{0} expected"; public const string parseexceptionformat = "{0} (at index {1})"; public const string colonexpected = "':' expected"; public const string openparenexpected = "'(' expected"; public const string closeparenoroperatorexpected = "')' or operator expected"; public const string closeparenorcommaexpected = "')' or ',' expected"; public const string dotoropenparenexpected = "'.' or '(' expected"; public const string openbracketexpected = "'[' expected"; public const string closebracketorcommaexpected = "']' or ',' expected"; public const string identifierexpected = "identifier expected"; }
此时,我们需要将我们的实体和数据库字段映射对应起来,新建一个imagemap类代码如下:
public class imagemap : entitytypeconfiguration<imagemodel> { public imagemap() { // primary key this.haskey(t => t.id); // properties this.property(t => t.idprooffront) .hasmaxlength(100); this.property(t => t.idproofback) .hasmaxlength(100); // table & column mappings this.totable("imagemodel"); this.property(t => t.id).hascolumnname("id"); this.property(t => t.idprooffront).hascolumnname("idprooffront"); this.property(t => t.idproofback).hascolumnname("idproofback"); } }
其中totable就是指明数据库表名,
那么如何将上传的图片保存更新到数据库呢?
接下来我们新建一个接口类iresourcesimage 并继承操作基类 irepository<imagemodel>
定义一个上传身份信息的规则如下:
bool updateidproof(string idprooffront, string idproofback, int pid);
接下来我们新建一个resourcesimage 实现上述接口。代码如下:
public resourcesimage() { } /// <summary> /// 上传身份信息采用此种方式 /// </summary> /// <param name="idproofback"></param> /// <param name="idproofback"></param> /// <param name="pid"></param> /// <returns></returns> public bool updateidproof(string idprooffront, string idproofback, int pid) { int flag = 0; if (idprooffront != "" && idprooffront != null) { flag = this.update(m => m.id == pid, u => new imagemodel { idprooffront = idprooffront }); if (flag == 1) { if (idproofback != "" && idproofback != null) flag = this.update(m => m.id == pid, u => new imagemodel { idproofback = idproofback }); } } else { if (idproofback != "" && idproofback != null) flag = this.update(m => m.id == pid, u => new imagemodel { idproofback = idproofback }); } return flag == 0 ? false : true; }
我们在中间层做一下这个操作:
private readonly iresourcesimage _resourcesimage; public codebll() { this._resourcesimage = new resourcesimage(); } /// <summary> /// 根据字段更新用户的文件资料信息 /// </summary> /// <param name="filenamefield">字段</param> /// <param name="filenamevalue">字段值</param> /// <param name="pid"></param> /// <returns></returns> public bool updatefilename(string idprooffront, string idproofback, int pid) { bool flag = false; flag = _resourcesimage.updateidproof(idprooffront, idproofback, pid); return flag; }
这样做其实并不科学,需要手动实例化这种仓储操作,科学的方式可以使用ioc(控制反转).
中间层做好之后,我们只需要在homecontroller中调用此方法即可,代码如下:
至此,我们就实现了本地通过ftp方式上传图片代码,并将图片以相对路径保存在数据库中,数据库存放格式如下:
demo下载
以上所述是小编给大家介绍的c# 中实现ftp 图片上传功能,希望对大家有所帮助
下一篇: 详解C# 中的正则表达式运用
推荐阅读
-
C# 中实现ftp 图片上传功能(多快好省)
-
C#实现的上传图片、保存图片、加水印、生成缩略图功能示例
-
jsp中实现上传图片即时显示效果功能
-
node.js中实现kindEditor图片上传功能的方法教程
-
JavaScript中的FileReader图片预览上传功能实现代码
-
vue中利用simplemde实现markdown编辑器(增加图片上传功能)-个人文章-SegmentFault思否
-
ThinkPHP框架实现FTP图片上传功能示例
-
JS中利用FileReader实现上传图片前本地预览功能
-
在cropper中js通过vue的图片裁剪上传功能如何实现
-
在vue中如何实现剪裁图片并上传服务器功能