C# ftp 图片上传多快好省

简介: 前言 此篇讲到的是图片上传功能,每个网站必定会有这样类似的功能,上传文件、上传图片等等。那么接下来,看看我们EF+uploadfile+ftp如何玩转上传图片吧 效果预览 具体实现 一个简单数据库 只有一个主键Id,一个身份证正面路径和一个身份证背面路径三个字段。

前言

此篇讲到的是图片上传功能,每个网站必定会有这样类似的功能,上传文件、上传图片等等。那么接下来,看看我们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">&nbsp;<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);" target="_blank" class="winView">
                                    <img style="border: none; width: 150px; height: 100px" src="/img/noupload.png" />
                                </a>
                            }
                            else
                            {
                                <a href="@(ViewBag.pathSrc + Model.IDProofFront)"  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)" 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);" target="_blank" class="winView">
                                    <img style="border: none; width: 150px; height: 100px" src="/img/noupload.png" />
                                </a>
                            }
                            else
                            {
                                <a href="@(ViewBag.pathSrc + Model.IDProofBack)" 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)" 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" 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: '选择 &hellip;',
        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} &hellip;',
        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: '拖拽文件到这里 &hellip;<br>支持多文件同时上传',
        dropZoneClickTitle: '<br>(或点击{files}按钮选择文件)',
        fileActionSettings: {
            removeTitle: '删除文件',
            uploadTitle: '上传文件',
            zoomTitle: '查看详情',
            dragTitle: '移动 / 重置',
            indicatorNewTitle: '没有上传',
            indicatorSuccessTitle: '上传',
            indicatorErrorTitle: '上传错误',
            indicatorLoadingTitle: '上传 ...'
        },
        previewZoomButtonTitles: {
            prev: '预览上一个文件',
            next: '预览下一个文件',
            toggleheader: '缩放',
            fullscreen: '全屏',
            borderless: '无边界模式',
            close: '关闭当前预览'
        }
    };
})(window.jQuery);
View Code

好了,界面大概就整成这样子,现在需要我们实现功能了。首先用JS将上传控件初始化一下:

//上传控件初始化
    function initFileUpload() {
        $("#uploadfile").fileinput({
            //uploadExtraData: { kvId: '10' },
            language: 'zh', //设置语言  
            showUpload: false, //是否显示上传按钮
            uploadAsync: true, //默认异步上传
            showRemove: false,
            autoReplace: true,
            maxFileCount: 1,
            maxFileSize: 10240,
            dropZoneTitle: '拖拽文件到这里 &hellip;<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");

        });
    }
View Code

然后再 加载页面的时候调用这个初始化即可

 $(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 = "网络异常,请稍后再试" });
            }
        }
View Code

其中 _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;

        }

    }
View Code

值得注意的是:帮助类中也从配置文件中读取了存放地址、文件类型、用户名、密码等必要信息。这个大家可以自己再配置文件中配置,配置好了如下所示:

    <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
    }
View Code

然后我们通过在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);
    }
View Code

然后需要实现这个基类接口,代码如下:

 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;
            }
        }
    }
View Code

注意,我们在这个实现类中定义了一个私有方法,指明了数据库访问的上下文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);
            }
        }
    }
View Code

我们将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";
    }
View Code

此时,我们需要将我们的实体和数据库字段映射对应起来,新建一个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下载

点我下载Demo  

Git下载

  • 感谢你的阅读。如果你觉得这篇文章对你有帮助或者有启发,就请推荐一下吧~你的精神支持是博主强大的写作动力。欢迎转载!
  • 博主的文章没有高度、深度和广度,只是凑字数。由于博主的水平不高(其实是个菜B),不足和错误之处在所难免,希望大家能够批评指出。
  • 欢迎加入.NET 从入门到精通技术讨论群→523490820 期待你的加入
  • 不舍得打乱,就永远学不会复原。被人嘲笑的梦想,才更有实现的价值。
  • 我的博客:http://www.cnblogs.com/zhangxiaoyong/
目录
相关文章
|
网络协议 C# 文件存储
C# 利用FluentFTP实现FTP上传下载功能
C# 利用FluentFTP实现FTP上传下载功能
459 0
C# 利用FluentFTP实现FTP上传下载功能
|
2月前
|
数据采集 JavaScript C#
C#图像爬虫实战:从Walmart网站下载图片
C#图像爬虫实战:从Walmart网站下载图片
C#WPF 图片在显示时没有问题,但在运行时图片显示不出来的解决
选中项目,点击右上角的显示全部文件按钮,会将默认隐藏的文件显示出来,选中所需图片,右键,添加到项目,然后选择图片查看属性,生成操作选择resource。完毕。本人目前的解决方案。
444 41
C#WPF 图片在显示时没有问题,但在运行时图片显示不出来的解决
|
6月前
|
存储 算法 C#
C# 生成指定图片的缩略图
C# 生成指定图片的缩略图
|
6月前
|
C# 开发工具 数据安全/隐私保护
C# 给图片添加文字水印
C# 给图片添加文字水印
|
6月前
|
开发框架 .NET C#
C# 自动填充文字内容到指定图片
C# 自动填充文字内容到指定图片
|
6月前
|
API C# 数据安全/隐私保护
C# 实现网页内容保存为图片并生成压缩包
C# 实现网页内容保存为图片并生成压缩包
|
11月前
|
API C#
C# 调用系统“API“设置图片为“桌面壁纸“
C# 调用系统“API“设置图片为“桌面壁纸“
|
C#
C# 图片RGB处理判断
C# 图片RGB处理判断 需要:根据一张原始图的RGB平均值和新的图片的RGB平均值的差距,来判断图中是否出现除原图中物体外的其他物体 前提:.Net framework 4.8 及以上 示例代码: 程序集:using System;using System.Drawing;using System.Drawing.Drawing2D;using System.Drawing.Imagin...
53 0