2019年12月11日 By mikel 分类: 数据库

来源: SQL Server附加数据库出现错误5123的正确解决方法 – 岁月已走远 – 博客园

因为自己有一本基于SQL Server 2005的数据库教程,里边使用的示例数据库是AdventureWorks for SQL Server 2005,而我的机子上装的是SQL Server 2008,示例数据库是AdventureWorks for SQL Server 2008。起初我以为示例数据库AdventureWorks for SQL Server 2005 与AdventureWorks for SQL Server 2008 数据库结构应该差不多,可是在练习的过程中,我发现两个数据库中很多表的结构还是有很多不一样的地方。于是决定到微软下载中心将示例数据库AdventureWorks for SQL Server 2005下过来,附加到SQL Server 2008上,以便顺利进行练习。我以SQL Server 2008的超级管理员账户“sa”连接登录到实例SQLServer2008:

在附加示例数据库AdventureWorks for SQL Server 2005时,弹出了下图这个错误:

    仔细看了一下主要信息“尝试打开或创建物理文件……时,CREATE FILE遇到操作系统错误 5(拒绝访问。)”  ,一看就知道应当是对要附加的数据文件的操作权限不够。     按一般的思维习惯,我们会对操作权限不够的文件授予足够的操作权限。比如,有网友说“给要附加的数据文件和相应的日志文件授予Everyone的权限”,授权过程如下三张截图所示(注意数据文件和日志文件都必须授权):

(图1:授权数据文件)
(图2:数据文件授权后)
(图3:日志文件授权后)
    对要附加的数据文件和日志文件分别授予Everyone【读取和执行】、【读取】的权限后,在SQL Server 2008中重新尝试附加数据库,发现可以附加成功了! 是不是问题就这样解决了呢?这样子做对吗?     如果在真实的数据库管理过程中,我们把数据文件、日志文件的权限放大到Everyone,那肯定是不对的做法。因为这样数据库的安全性将大打折扣,虽然对Everyone只授予了【读取和执行】、【读取】的权限,但这仍然有泄漏数据的危险。
     我们应当保证能正常访问的情况下,使数据文件具有最小的访问权。我们之前授权给Everyone,那所有用户或账户都能操作相应的文件了,这肯定不安全的。那么如何才能授予最小的访问权限呢?思考一下,我们用SQL Server 2008去附加相应的数据文件,报出“拒绝访问”即权限不够的错误,换句话说,当前SQL Server 2008没有权限访问这些文件。我们右键文件,到文件属性中查看一下文件的权限情况,如下图所示:

(相应数据文件原本的权限情况)
     我们发现只有SYSTEM和xrm这两个组或用户才有权操作此数据文件。SYSTEM是一个用户组,即【本地系统】组,而xrm是一个管理员用户,如图示:

 
(xrm用户的信息)
    SYSTEM用户组和xrm这个管理员用户都有权限操作此数据文件和日志文件,而以SQL Server 2008的超级管理员SA连接登录实例后,SQL Server却没有权限访问此数据文件。换句话说,以SQL Server 2008的超级管理员SA连接登录实例后,登录的身份不在SYSTEM用户组,也不是xrm这个管理员。那会是什么呢?
         我们查看一下当前SQL Server 2008的实例服务的相关信息就知道了,打开Sql Server Configuration Manager (即SQL Server 配置管理器)查看一下当前连接到的实例服务的相关信息,如下图所示:

(当前实例服务的相关信息)
    发现当前实例SQLServer2008的登录身份为“NT AUTHORITY\LocalService”,即操作系统授权的【本地服务】,本地服务也是了个用户组。换句话说,如果我们仅授予【本地服务】这个用户组的权限(而不是Everyone),应该也可以在SQL Server 2008中用sa的账户附加数据库了。为此,将刚刚授予相应数据文件和日志文件Everyone的权限都删除,再授予LocalService用户组相应数据文件和日志文件的权限,重新尝试附加相应的数据库,发现的确可以附加成功!不必说,授予操作系统授权的【本地服务】用户组比起授予Everyone来说肯定要安全的多。
    上面提到的方法中,我们都是改变了数据文件原来的权限范围(原来的权限范围只有SYSTEM即【本地系统】用户组和xrm这个系统管理员) 。而更好的办法是不要改变数据文件的权限范围,仍然以SA身份连接登录SQL Server 2008的实例也能访问相应的数据文件。而要达到这个目的,我们只需要将相应实例的登录身份改为SYSTEM【本地系统】用户组,SYSTEM也是在相应数据文件的权限范围之内的用户组,而且SQL Server实例以本地系统身份运行,安全性将更高。我们可以在SQL Server 配置管理器中将相应的SQL Server实例的登录身份修改为【本地系统】即Local System,如下列图所示:

(修改实例的登录身份)
   
(实例的登录身份变为LocalSystem)
    然后重启相应实例服务,重新以SA身份连接登录SQL Server 2008的相应实例并尝试附加数据库, 同样可以成功的将数据库附加上!!!

其实,如果不是要特别地以SA身份连接登录SQL Server 2008的相应实例来附加相应数据库,那么在连接登录SQL Server 2008的相应实例时,身份验证选择【Windows 身份验证】,不做前文中所述的其他修改就可以把数据库附加上去了,原因就在于:【Windows 身份验证】用的是当前操作系统的用户的权限,权限一般都足够大的。另外,在【SQL Server 配置管理器】中针对实例服务可以做的操作,在Windows的【服务】上也可以做到。

2019年12月9日 By mikel 分类: JavaScript

来源: table 编辑时更新合计项 – Fly社区

layui.use(['table'], function () {
    var table = layui.table;
    partsTable = table.render({
        elem: '#parts',
        toolbar: '#partsToolbar',
        page: true,
        limit: 10,
        height: 'full-250',
        totalRow: true,
        cols: cols,
        done: function (res, curr, count) {

        }
    });
    table.on('edit(parts)', function (obj) {
        obj.update({
            peij_sl: response.data.peij_sl + obj.data.peij_dw,
        });
        tabTotal(partsTable);
    });
})
/*
* 更新表格合计
* @param {obj} obj  table 对象
*/ 
var tabTotal = function (obj) {
    var layui_table = obj.config.elem.next('.layui-table-view');
    var table_data = {};
    var table_total_field = [];
    layui.use(['table'], function () {
        var table = layui.table;
        table_data = table.cache[obj.config.id];
        var table_cols = obj.config.cols[0];
        // 获取合计项
        $.each(table_cols, function (i, v) {
            if (v.totalRow) {
                table_total_field.push(v.field);
            }
        });
    })
    var layui_table_total = layui_table.find('.layui-table-total');
    $.each(table_total_field, function (i, v) {
        // 合计
        var sum = table_data.reduce(function (num1, num2) {
            // 精度处理
            const num1Digits = (num1.toString().split('.')[1] || '').length;
            const new_num2 = num2[v] ? num2[v] : 0;
            const num2Digits = (new_num2.toString().split('.')[1] || '').length;
            const baseNum = Math.pow(10, Math.max(num1Digits, num2Digits));
            return (num1 * baseNum + new_num2 * baseNum) / baseNum;

        }, 0);
        layui_table_total.find('td[data-field=' + v + '] div').text(sum);
    });
}

哈哈 业务需求简单的实现一下下[嘻嘻]

table 编辑时更新合计项 – Fly社区已关闭评论
2019年12月4日 By mikel 分类: C#, 数据库, 架构设计

来源: CRL快速开发框架系列教程一(Code First数据表不需再关心) – hubro – 博客园

本系列目录

  1. CRL快速开发框架系列教程一(Code First数据表不需再关心)
  2. CRL快速开发框架系列教程二(基于Lambda表达式查询)
  3. CRL快速开发框架系列教程三(更新数据)
  4. CRL快速开发框架系列教程四(删除数据)
  5. CRL快速开发框架系列教程五(使用缓存)
  6. CRL快速开发框架系列教程六(分布式缓存解决方案)
  7. CRL快速开发框架系列教程七(使用事务)
  8. CRL快速开发框架系列教程八(使用CRL.Package)
  9. CRL快速开发框架系列教程九(导入/导出数据)
  10. CRL快速开发框架系列教程十(导出对象结构)
  11. CRL快速开发框架系列教程十一(大数据分库分表解决方案)
  12. CRL快速开发框架系列教程十二(MongoDB支持)
  13. CRL快速开发框架系列教程十三(嵌套查询)

 正文

在面向对象的概念越来越深入的今天,Code First开发模式想必也不再陌生,开发关注点由数据库为主变为以对象结构为主

在开发程序时,以编程的思想去考虑,如何用对象结构表示这一数据结构,至于数据结构的载体是什么数据库,无所谓了

在面得对象的框架中,EF是做得比较好了,特别是Code First模式下,数据表能自动生成,相比一般的形式,建立对象,再按对象生成数据库脚本,好多重复工作

CRL同样采用Code First开发模式,更值得一提的是,数据结构是自动创建的,无论是增加对象,或增加对象的属性(当然没能自动删除)

对象定义

CRL对象需要继承IModel或IModelBase,它们之间的区别:

  • IModel是一个抽象类,不包含任何属性,继承它可以定义自定义类型的主建字段,如GUID类型的主键
  • IModelBase包含int类型主键ID和AddTime字段,继承后满足一般自增主键要求

来看一个简单对象定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[CRL.Attribute.Table(TableName = "TestModel_1")]//定义映射名
public class TestModel : CRL.IModel
{
    [CRL.Attribute.Field(IsPrimaryKey = true)]//定义为主键
    public int Id
    {
        get;
        set;
    }
    [CRL.Attribute.Field(Length = 50)]//定义列长度
    public string Name
    {
        get;
        set;
    }
}

在上面定义中使用了CRL.Attribute.Table和CRL.Attribute.Field属性标注,当要指定对应的数据结限定,使用此标注即可

创建对象管理类

1
2
3
4
5
6
7
8
9
10
public class TestModelManage : CRL.BaseProvider<TestModel>
{
    public static TestModelManage Instance
    {
        get
        {
            return new TestModelManage();
        }
    }
}

调用试试看(重点来了,一般框架肯定会报错,找不到数据表,CRL不会,因为它自动创建了)

1
var data = TestModelManage.Instance.QueryList(b => b.Id > 0);

看数据库里结构

再增加一个属性 Name2,重编译运行上面代码

可以看到增加的属性自动创建了对应的字段

CRL如何做到这点

  • CRL在对象被调用时,会检查一次数据结构,看和对象定义是不是一致,如果有少了就创建表或字段(当然不是直接从数据库里查,那样效率太低了,也耗资源)
  • 对象检查会耗费一些资源,只建议在开发阶段使用,上线后可以通过CRL.SettingConfig.CheckModelTableMaping设置开关

对象数据检查

除数据表结构检查,CRL还可以对数据作检查

上面对象定义了Name长度为50,在插入此数据时,如果数据长度超过了50,会怎么样呢

1
2
3
var data2 = new TestModel();
            data2.Name = "这是一个超过50的字符串这是一个超过50的字符串这是一个超过50的字符串这是一个超过50的字符串这是一个超过50的字符串这是一个超过50的字符串这是一个超过50的字符串这是一个超过50的字符串这是一个超过50的字符串";
            TestModelManage.Instance.Add(data2);

正常会报数据库错误,如SQL会报将截断字符串,也不会告诉你是哪个字段,而CRL会抛出异常

也可以对数据自定义检查

重写TestModel的CheckData方法,这里就可以自由发挥了,如按业务规则,从根本上封堵了错误数据的产生

1
2
3
4
5
6
7
8
public override string CheckData()
        {
            if (Name!="hubro")
            {
                return "输入的值?";
            }
            return base.CheckData();
        }<br>

data2.Name = “ggy”;
TestModelManage.Instance.Add(data2);

运行结果

重复数据提交判断

当在短时间内, 插入相同的数据,CRL默认为重复提交了,重复依据为数据内容MD5值

同时插入两条相同的数据

1
2
3
4
5
6
var data2 = new TestModel();
            data2.Name = "hubro";
            TestModelManage.Instance.Add(data2);
            var data3 = new TestModel();
            data3.Name = "hubro";
            TestModelManage.Instance.Add(data3);

运行如下

若要关闭,重写TestModel方法

1
2
3
4
5
6
7
protected override bool CheckRepeatedInsert
        {
            get
            {
                return false;
            }
        }

CRL Code First开发方式介绍到这里

更详细的例子见CRL开发文档

CRL快速开发框架系列教程一(Code First数据表不需再关心) – hubro – 博客园已关闭评论
2019年12月4日 By mikel 分类: 数据库

来源: SQL Server 中WITH (NOLOCK)浅析 – nuptsv_ice的专栏 – CSDN博客

概念介绍

 

开发人员喜欢在SQL脚本中使用WITH(NOLOCK), WITH(NOLOCK)其实是表提示(table_hint)中的一种。它等同于 READUNCOMMITTED 。 具体的功能作用如下所示(摘自MSDN):

1: 指定允许脏读。不发布共享锁来阻止其他事务修改当前事务读取的数据,其他事务设置的排他锁不会阻碍当前事务读取锁定数据。允许脏读可能产生较多的并发操作,但其代价是读取以后会被其他事务回滚的数据修改。这可能会使您的事务出错,向用户显示从未提交过的数据,或者导致用户两次看到记录(或根本看不到记录)。有关脏读、不可重复读和幻读的详细信息,请参阅并发影响

2: READUNCOMMITTED 和 NOLOCK 提示仅适用于数据锁。所有查询(包括那些带有 READUNCOMMITTED 和 NOLOCK 提示的查询)都会在编译和执行过程中获取 Sch-S(架构稳定性)锁。因此,当并发事务持有表的 Sch-M(架构修改)锁时,将阻塞查询。例如,数据定义语言 (DDL) 操作在修改表的架构信息之前获取 Sch-M 锁。所有并发查询(包括那些使用 READUNCOMMITTED 或 NOLOCK 提示运行的查询)都会在尝试获取 Sch-S 锁时被阻塞。相反,持有 Sch-S 锁的查询将阻塞尝试获取 Sch-M 锁的并发事务。有关锁行为的详细信息,请参阅锁兼容性(数据库引擎)

3:  不能为通过插入、更新或删除操作修改过的表指定 READUNCOMMITTED 和 NOLOCK。SQL Server 查询优化器忽略 FROM 子句中应用于 UPDATE 或 DELETE 语句的目标表的 READUNCOMMITTED 和 NOLOCK 提示。

 

功能与缺陷

 

使用WIHT(NOLOCK)有利也有弊,所以在决定使用之前,你一定需要了解清楚WITH(NOLOCK)的功能和缺陷,看其是否适合你的业务需求,不要觉得它能提升性能,稀里糊涂的就使用它。

 

1:使用WITH(NOLOCK)时查询不受其它排他锁阻塞

打开会话窗口1,执行下面脚本,不提交也不回滚事务,模拟事务真在执行过程当中

BEGIN TRAN

       UPDATE TEST SET NAME='Timmy' WHERE OBJECT_ID =1;

       --ROLLBACK

 

打开会话窗口2,执行下面脚本,你会发现执行结果一直查询不出来(其实才两条记录)。当前会话被阻塞了

SELECT * FROM TEST;

打开会话窗口3,执行下面脚本,查看阻塞情况,你会发现在会话2被会话1给阻塞了,会话2的等待类型为LCK_M_S:“当某任务正在等待获取共享锁时出现”



  SELECT wt.blocking_session_id                    AS BlockingSessesionId
        ,sp.program_name                           AS ProgramName
        ,COALESCE(sp.LOGINAME, sp.nt_username)     AS HostName
        ,ec1.client_net_address                    AS ClientIpAddress
        ,db.name                                   AS DatabaseName
        ,wt.wait_type                              AS WaitType
        ,ec1.connect_time                          AS BlockingStartTime
        ,wt.WAIT_DURATION_MS/1000                  AS WaitDuration
        ,ec1.session_id                            AS BlockedSessionId
        ,h1.TEXT                                   AS BlockedSQLText
        ,h2.TEXT                                   AS BlockingSQLText
  FROM sys.dm_tran_locks AS tl
  INNER JOIN sys.databases db
    ON db.database_id = tl.resource_database_id
  INNER JOIN sys.dm_os_waiting_tasks AS wt
    ON tl.lock_owner_address = wt.resource_address
  INNER JOIN sys.dm_exec_connections ec1
    ON ec1.session_id = tl.request_session_id
  INNER JOIN sys.dm_exec_connections ec2
    ON ec2.session_id = wt.blocking_session_id
  LEFT OUTER JOIN master.dbo.sysprocesses sp
    ON SP.spid = wt.blocking_session_id
  CROSS APPLY sys.dm_exec_sql_text(ec1.most_recent_sql_handle) AS h1
  CROSS APPLY sys.dm_exec_sql_text(ec2.most_recent_sql_handle) AS h2

 

 

clipboard

 

此时查看会话1(会话1的会话ID为53,执行脚本1前,可以用SELECT  @@spid查看会话ID)的锁信息情况,你会发现表TEST(ObjId=1893581784)持有的锁信息如下所示

 

clipboard[1]

 

打开会话窗口4,执行下面脚本.你会发现查询结果很快就出来,会话4并不会被会话1阻塞。

SELECT * FROM TEST WITH(NOLOCK)

从上面模拟的这个小例子可以看出,正是由于加上WITH(NOLOCK)提示后,会话1中事务设置的排他锁不会阻碍当前事务读取锁定数据,所以会话4不会被阻塞,从而提升并发时查询性能。

 

2:WITH(NOLOCK) 不发布共享锁来阻止其他事务修改当前事务读取的数据,这个就不举例子了。

本质上WITH(NOLOCK)是通过减少锁和不受排它锁影响来减少阻塞,从而提高并发时的性能。所谓凡事有利也有弊,WITH(NOLOCK)在提升性能的同时,也会产生脏读现象。

如下所示,表TEST有两条记录,我准备更新OBJECT_ID=1的记录,此时事务既没有提交也没有回滚

clipboard[2]

BEGIN TRAN

UPDATE TEST SET NAME='Timmy' WHERE OBJECT_ID =1;

--ROLLBACK

此时另外一个会话使用WITH(NOLOCK)查到的记录为未提交的记录值

clipboard[3]

假如由于某种原因,该事务回滚了,那么我们读取到的OBJECT_ID=1的记录就是一条脏数据。

脏读又称无效数据的读出,是指在数据库访问中,事务T1将某一值修改,然后事务T2读取该值,此后T1因为某种原因撤销对该值的修改,这就导致了T2所读取到的数据是无效的。

 

WITH(NOLOCK)使用场景

 

什么时候可以使用WITH(NOLOCK)? 什么时候不能使用WITH(NOLOCK),这个要视你系统业务情况,综合考虑性能情况与业务要求来决定是否使用WITH(NOLOCK), 例如涉及到金融或会计成本之类的系统,出现脏读那是要产生严重问题的。关键业务系统也要慎重考虑。大体来说一般有下面一些场景可以使用WITH(NOLOCK)

1: 基础数据表,这些表的数据很少变更。

2:历史数据表,这些表的数据很少变更。

3:业务允许脏读情况出现涉及的表。

4:数据量超大的表,出于性能考虑,而允许脏读。

另外一点就是不要滥用WITH(NOLOCK),我发现有个奇怪现象,很多开发知道WITH(NOLOCK),但是有不了解脏读,习惯性的使用WITH(NOLOCK)。

 

WITH(NOLOCK)与 NOLOCK区别

 

为了搞清楚WITH(NOLOCK)与NOLOCK的区别,我查了大量的资料,我们先看看下面三个SQL语句有啥区别

SELECT * FROM TEST NOLOCK

SELECT * FROM TEST (NOLOCK);

SELECT * FROM TEST WITH(NOLOCK);

上面的问题概括起来也就是说NOLOCK、(NOLOCK)、 WITH(NOLOCK)的区别:

1: NOLOCK这样的写法,其实NOLOCK其实只是别名的作用,而没有任何实质作用。所以不要粗心将(NOLOCK)写成NOLOCK

2:(NOLOCK)与WITH(NOLOCK)其实功能上是一样的。(NOLOCK)只是WITH(NOLOCK)的别名,但是在SQL Server 2008及以后版本中,(NOLOCK)不推荐使用了,”不借助 WITH 关键字指定表提示”的写法已经过时了。 具体参见MSDN http://msdn.microsoft.com/zh-cn/library/ms143729%28SQL.100%29.aspx

2.1  至于网上说WITH(NOLOCK)在SQL SERVER 2000不生效,我验证后发现完全是个谬论。

2.2  在使用链接服务器的SQL当中,(NOLOCK)不会生效,WITH(NOLOCK)才会生效。如下所示

clipboard[4]

消息 4122,级别 16,状态 1,第 1 行

Remote table-valued function calls are not allowed.

 

3.语法上有些许出入,如下所示

这种语法会报错
SELECT  * FROM   sys.indexes  WITH(NOLOCK) AS i
-Msg 156, Level 15, State 1, Line 1
-Incorrect syntax near the keyword 'AS'.

这种语法正常
SELECT  * FROM   sys.indexes  (NOLOCK) AS i

可以全部改写为下面语法

SELECT  * FROM   sys.indexes   i WITH(NOLOCK)


SELECT  * FROM   sys.indexes   i (NOLOCK)

 

WITH(NOLOCK)会不会产生锁

很多人误以为使用了WITH(NOLOCK)后,数据库库不会产生任何锁。实质上,使用了WITH(NOLOCK)后,数据库依然对该表对象生成Sch-S(架构稳定性)锁以及DB类型的共享锁, 如下所示,可以在一个会话中查询一个大表,然后在另外一个会话中查看锁信息(也可以使用SQL Profile查看会话锁信息)

不使用WTIH(NOLOCK)

clipboard[5]

使用WITH(NOLOCK)

clipboard[6]

从上可以看出使用WITH(NOLOCK)后,数据库并不是不生成相关锁。  对比可以发现使用WITH(NOLOCK)后,数据库只会生成DB类型的共享锁、以及TAB类型的架构稳定性锁.

另外,使用WITH(NOLOCK)并不是说就不会被其它会话阻塞,依然可能会产生Schema Change Blocking

会话1:执行下面SQL语句,暂时不提交,模拟事务正在执行

BEGIN TRAN

  ALTER TABLE TEST ADD Grade VARCHAR(10) ;

会话2:执行下面语句,你会发现会话被阻塞,截图如下所示。

SELECT * FROM TEST WITH(NOLOCK)

image

SQL Server 中WITH (NOLOCK)浅析 – nuptsv_ice的专栏 – CSDN博客已关闭评论
2019年12月4日 By mikel 分类: 数据库

来源: 在sqlserver 中with(nolock)详解 – ~雨落忧伤~ – 博客园

所有Select加 With (NoLock)解决阻塞死锁
在查询语句中使用 NOLOCK 和 READPAST

处理一个数据库死锁的异常时候,其中一个建议就是使用 NOLOCK 或者 READPAST 。有关 NOLOCK 和 READPAST的一些技术知识点:

对于非银行等严格要求事务的行业,搜索记录中出现或者不出现某条记录,都是在可容忍范围内,所以碰到死锁,应该首先考虑,我们业务逻辑是否能容忍出现或者不出现某些记录,而不是寻求对双方都加锁条件下如何解锁的问题。

NOLOCK 和 READPAST 都是处理查询、插入、删除等操作时候,如何应对锁住的数据记录。但是这时候一定要注意NOLOCK 和 READPAST的局限性,确认你的业务逻辑可以容忍这些记录的出现或者不出现:

简单来说:

NOLOCK 可能把没有提交事务的数据也显示出来.

READPAST 会把被锁住的行不显示出来

不使用 NOLOCK 和 READPAST ,在 Select 操作时候则有可能报错误:事务(进程 ID **)与另一个进程被死锁在 锁 资源上,并且已被选作死锁牺牲品。

下面就来演示这个情况。

为了演示两个事务死锁的情况,我们下面的测试都需要在SQL Server Management Studio中打开两个查询窗口。保证事务不被干扰。

演示一 没有提交的事务,NOLOCK 和 READPAST处理的策略:

查询窗口一请执行如下脚本:

CREATE TABLE t1 (c1 int IDENTITY(1,1), c2 int)
go

BEGIN TRANSACTION
insert t1(c2) values(1)

在查询窗口一执行后,查询窗口二执行如下脚本:

select count(*) from t1 WITH(NOLOCK)
select count(*) from t1 WITH(READPAST)

结果与分析:

查询窗口二依次显示统计结果为: 1、0

查询窗口一的命令没有提交事务,所以 READPAST 不会计算没有提交事务的这一条记录,这一条被锁住了,READPAST 看不到;而NOLOCK则可以看到被锁住的这一条记录。

如果这时候我们在查询窗口二中执行:

select count(*) from t1 就会看到这个执行很久不能执行完毕,因为这个查询遇到了一个死锁。

清除掉这个测试环境,需要在查询窗口一中再执行如下语句:

ROLLBACK TRANSACTION
drop table t1

演示二:对被锁住的记录,NOLOCK 和 READPAST处理的策略

这个演示同样需要两个查询窗口。

请在查询窗口一中执行如下语句:

CREATE TABLE t2 (UserID int , NickName nvarchar(50))
go
insert t2(UserID,NickName) values(1,’郭红俊’)
insert t2(UserID,NickName) values(2,’蝈蝈俊’)
go

BEGIN TRANSACTION
update t2 set NickName = ‘蝈蝈俊.net’ where UserID = 2

请在查询窗口二中执行如下脚本:

select * from t2 WITH(NOLOCK) where UserID = 2
select * from t2 WITH(READPAST) where UserID = 2

结果与分析:

查询窗口二中, NOLOCK 对应的查询结果中我们看到了修改后的记录,READPAST对应的查询结果中我们没有看到任何一条记录。 这种情况下就可能发生脏读

在sqlserver 中with(nolock)详解 – ~雨落忧伤~ – 博客园已关闭评论
2019年12月4日 By mikel 分类: C#

来源: InstallerProjects打包 – HackerVirus – 博客园

打包桌面应用程序实在是一个不常使用的东西,偶尔使用起来经常会忘东忘西的耽误时间,因此,这篇文章多以图片记录过程,也是用于备忘。

下载打包工具

C#打包桌面应用程序有很多种方法,这里介绍一种使用Microsoft Visual Studio Installer Projects工具打包的方法。

首先,我们先创建一个Windows桌面应用,如图:

接下来我们选择工具,点击扩展和更新,如图:

然后我们得到界面如下,如图:

界面默认显示的是已安装的内容,我们仔细看下内容,会发现,这里都是我们已经安装过的工具,在仔细看,我们会发现,这里大部分工具的创建者都是microsoft,也就是说,这里的工具都是官方给我们提供的工具。

因为我们要下载本机不存在的工具,所以我们点击左侧菜单的【联机】按钮,然后在右上角的搜索框中输入Microsoft Visual Studio Installer Projects进行检索,如图:

然后我们选中Microsoft Visual Studio Installer Projects选项,点击选项中右上角的下载,将该工具下载到本地。

下载完成后,我们会发现,在当前窗体的下方有这样一个提示。

该提示,告诉我们,虽然工具已经下载完成了,但还没有进行安装,需要我们关闭Visual Studio后,才能安装。

关闭Visual Studio后,会自动弹出如下提示框,系统还会提示我们是否允许,我们选择【是】。

接下来,该窗体会显示工具的相关内容,下方会增加一个修改按钮,如下图:

我们点击修改,然后工具就会自动安装了。

创建打包项目

等待工具安装完成后,我们重新打开Visual Studio,打开刚刚我们建立的WPF项目。

在WPF项目所在的解决方案中,我们右键新建项目,然后选择左边菜单的【其他项目类型】—【Setup Project】,创建安装工程KibaInstallSetup,如下图:

点击确定 ,我们可以看到如下窗口。

该窗口左边窗口有三个文件夹图片,对应内容如下:

Application Folder:应用程序包含的文件设置。

User’s Desktop:用户桌面快捷方式设置。

User’s Programs Menu:用户启动菜单的快捷方式设置。

我们先看Application Folder的使用方式,首先选中Application Folder,然后右键—>Add—>项目输出。

点击后,弹出[添加项目输出组页面],如下图,因为解决方案下只有一个项目,所以我这里项目选择中只有一个选项。

点击确定,我们就成功的把项目主输出添加进来了,如下图:

此时,我们右键我们的安装工程KibaInstallSetup—生成,就已经可以生成该项目的可执行文件了,如下图:

不过,此时的安装文件是最基础的安装文件,什么自定义都没有。

下面我们为安装文件增加桌面快捷方式图标和开始菜单的快捷方式,并且修改可执行文件的作者、描述等等信息。

打包项目属性配置

项目属性

我们先进行安装文件的基础信息更改。

首先,我们左键选中项目,然后选择属性,注意,不是右键选择属性,而是如下图一样选择属性。

点击后,修改一下作者和描述,属性界面如下图所示:

桌面快捷方式

现在我们修改安装程序的快捷方式图片。

左键选中User’s Desktop,然后将鼠标移动到右侧窗体,右键选择[创建新的快捷方式],如下图。

然后,系统弹出一个让我们选择.ico文件的界面,但界面中只有三个文件夹,对应的是我们安装工程左上角的三个文件夹,如下图:

现在我们双击第一个应用程序目录选项,然后点击Add File,向安装目录下添加一个logo.ico文件,最后点击OK。

这样我们就成功创建了桌面快捷方式了,如下图。

现在,我们右键这个Shortcut to logo.ico,修改其名字为Kiba,然后左键点击选中,查看其属性,如下图。

可以看到,我们的快捷方式已经成功指向了我们的主输出,并且Icon也已经修改了。如果不满意指向和Icon,可以在属性里二次修改。

用户菜单快捷方式

用户菜单的快捷方式就是在User’s Programs Menu里设置,其设置的方法和桌面快捷方式的设置方法是一模一样的。

依赖文件和依赖框架

依赖文件

我们在打包应用程序时,有时候会需要一些依赖文件,这些文件并不能被主程序引用,但还需要和主程序在同一个安装路径下,那么在打包时,也就需要额外的把这些文件也打包进来。

添加额外依赖文件的方法很简单,选择Application Folder,在其对应的右侧窗体中,右键—Add—文件,如下图:

然后,在弹出的选择文件对话框中选择文件即可。

依赖框架

有时候我们的应用程序需要安装到一个没有Framework的电脑上,那么就需要打包的时候,把Framework也打包进来,或者在用户安装时提示对方下载。

我们右键项目,选择属性,然后在弹出的属性页中点击Prerequisites,如下图:

然后,在系统必备的窗体中,勾选Framework 4.6.1,在选择【从组件供应商的网站上下载系统必备组件】。

这样,我们的可执行文件在运行时,就会提示客户去微软官网下载Framework 4.6.1了,如下图:

当然,我们也可以把Framework 4.6.1打包进来,但这需要我们提前把Framework 的安装包下载下来,然后选择【从下列位置下载系统必备组件】,这样那个置灰的【浏览】按钮就可以使用了。

—————————————————————————————————-

到此,使用InstallerProjects打包桌面应用程序讲解就结束了。

代码已经传到Github上了,欢迎大家下载。

Github地址:https://github.com/kiba518/KibaInstall

InstallerProjects打包 – HackerVirus – 博客园已关闭评论
2019年12月4日 By mikel 分类: C#

来源: C# 编写Windows服务实现开机启动一个程序 – Jerry_Wu – 博客园

如果有一个应用程序,想要在电脑开机的时候自动启动(即使用户未登录Windows也要启动),可以用Windows服务来实现,我只是很简单的尝试了一下,由于前段时间做的一个办公QQ,在服务器端需要始终运行一个控制台程序来监听各种消息,但不能每次开机手动去启动这个控制台程序,所以就写了一个Windows服务,我用VS2008写的,以下是简单做法:

先在VS中创建一个Windows服务的项目,在自动生成的service1.cs文件里添加如下语句:

string StartAppPath = @”C:\Program Files\办公QQ服务\QQService.exe”;

这是我要运行的控制台程序的路径,你用的时候换成你的就可以了,然后在OnStart()函数中添加如下代码:

try

{

Process proc = new Process();

proc.StartInfo.FileName = StartAppPath; //注意路径

proc.Start();

}

catch (System.Exception ex)

{

//错误处理

}

双击Service1.cs打开设计视图,在设计视图中右键,选择添加安装程序,然后在生成的ProjectInstaller.Designer.cs文件中的InitializeComponent()函数中添加如下代码,具体作用不甚清楚:

this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;

 

this.serviceInstaller1.ServiceName = “办公QQ服务”;     //我的服务名称

this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic;    \\服务自动运行

好了,现在可以编译生成exe了,下面要用cmd命令将刚才写的这个Windows服务安装并启动:

先cmd运行命令: cd C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\

进入.net2.0的文件夹,因为里面有个InstallUtil.exe的应用程序是我们需要用的,上面这个路径是我安装的路径,你根据需要改成自己的就行了,然后继续写下一个cmd命令:

InstallUtil C:\QQ_WinService.exe

这个.exe应用程序是我开头提到的用VS2008刚创建的,你也要根据需要换成你的,我直接把这个.exe拷出来放在C盘根目录下了,你到时换成你自己的路径就可以了

提示服务安装成功后,继续写下一个命令,启动这个服务:

net start 办公QQ服务

“办公QQ服务”是我安装的服务的名称,这里也需要换成你自己的

现在这个Windows服务就已经写好了,当然,这些cmd命令也可以写成一个批处理文件,要停止这个服务的话可以用:

Net stop 办公QQ服务

然后删除这个服务:

cd C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\

InstallUtil /u C:\QQ_WinService.exe

C# 编写Windows服务实现开机启动一个程序 – Jerry_Wu – 博客园已关闭评论
2019年12月2日 By mikel 分类: C#, Debug

来源: System.InvalidOperationException:“线程间操作无效: 从不是创建控件“btnSearch”的线程访问它。” – 二等碗 – CSDN博客

System.InvalidOperationException:“线程间操作无效: 从不是创建控件“btnSearch”的线程访问它。”
这个问题属于跨线程问题

在Form1重载中写上一行代码

public Form1()
{
InitializeComponent();
System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false;//设置该属性 为false
}

这样问题就解决了

这个问题属于跨线程问题

————————————————
版权声明:本文为CSDN博主「盗理者」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_36051316/article/details/80026874

System.InvalidOperationException:“线程间操作无效: 从不是创建控件“btnSearch”的线程访问它。” – 二等碗 – CSDN博客已关闭评论
2019年11月29日 By mikel 分类: ASP.NET MVC, Debug

来源: Log4Net异常日志记录在asp.net mvc3.0的应用 – aehyok – 博客园

前言

log4net是.Net下一个非常优秀的开源日志记录组件。log4net记录日志的功能非常强大。它可以将日志分不同的等级,以不同的格式,输出到不同的媒介。本文主要是简单的介绍如何在Visual Studio2010(ASP.NET Mvc3.0)中使用log4net快速创建系统日志,如何扩展以输出自定义字段。

用户可以从http://logging.apache.org/log4net/下载log4net的源代码。解压软件包后,在解压的src目录下将log4net.sln载入Visual Studio .NET,编译后可以得到log4net.dll。用户要在自己的程序里加入日志功能,只需将log4net.dll引入工程即可。

在项目中配置

第一步:首先在项目中引用log4net.dll文件。

第二步:在Web.config文件中进行添加configSections的节点

  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
    <!--配置一个结点 名称为log4net-->
  </configSections>

第三步:添加log4net配置节点

<log4net debug="true">
</log4net>

然后在log4net节点下添加

写入本地文本文件中的配置

复制代码
         <appender name="LogFileAppender" type="log4net.Appender.FileAppender" >
             <!--定义的是日志记录到文件的附着器 name表示该附着器的名称-->         
             <!--在log4net中还有一个附着器RollingFileAppender 它表示会循环生成很多文件,举例来说,就是设置一共可以生成20个文件,每个文件的大小为2K,那么如果第一个、-->          
             <!--文件的大小超过2K,就会自动创建一个按顺序命名的文件-->
             <param name="File" value="c:\Log\DBLog.txt" /> <!--日志记录的存在路径-->
             <param name="AppendToFile" value="true" /><!--为true就表示日志会附加到文件,为false,则会重新创建一个新文件-->
             <layout type="log4net.Layout.PatternLayout">
               <!--输出内容控制-->
               <param name="ConversionPattern" value="%d [%t] %-5p %c [%x] - %m%n" />
             </layout>     
         </appender>
复制代码

注释很清楚,就不解释了。

写入指定邮箱的配置

复制代码
        <appender name="SmtpAppender" type="log4net.Appender.SmtpAppender">
        <!--设置发送电子邮件的附着器-->
            <authentication value="Basic" />
            <to value="455043818@qq.com" />
            <from value="aehyok@163.com" />
            <username value="帐号" />
            <password value="密码" />
            <subject value="程序异常日志记录邮件发送" />
            <smtpHost value="smtp.163.com" />
            <bufferSize value="512" />
            <lossy value="true" />
            <evaluator type="log4net.Core.LevelEvaluator">
               <threshold value="debug"/>  
            </evaluator>
            <layout type="log4net.Layout.PatternLayout">
                 <conversionPattern value="%newline%date [%thread] %-5level %logger [%property{NDC}] - %message%newline%newline%newline" />    
            </layout>
        </appender>
复制代码

通过的是163邮箱服务器发送

将日志写入数据库的相关配置,还要建立一张对应的数据库表

复制代码
         <appender name="ADONetAppender" type="log4net.Appender.ADONetAppender">
             <!--存储到数据库的操作-->
             <bufferSize value="10"/>
             <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
             <connectionString value="server=.;database=Log4Net;user id=sa;password=saa"/>
             <commandText value="INSERT INTO _Log ([Date],[Thread],[Level],[Logger],[Message],[Exception]) VALUES (@log_date,                                @log_thread, @log_level, @log_logger, @log_message, @log_exception)"/>
             <parameter>
                 <parameterName value="@log_date"/>
                 <dbType value="DateTime"/>
                 <layout type="log4net.Layout.RawTimeStampLayout"/><!--可以认为是记录日志的时间-->
             </parameter>
             <parameter>
                 <parameterName value="@log_thread"/>
                 <dbType value="String"/>
                 <size value="255"/>
                 <layout type="log4net.Layout.PatternLayout">
                    <conversionPattern value="%thread"/><!--记录日志时的线程号--> 
                </layout>  
             </parameter>
             <parameter>
                 <parameterName value="@log_level"/>
                 <dbType value="String"/>
                 <size value="50"/>
                 <layout type="log4net.Layout.PatternLayout">
                    <conversionPattern value="%level"/><!--日志级别-->
                 </layout>
             </parameter>
             <parameter>
                 <parameterName value="@log_logger"/>
                 <dbType value="String"/>
                 <size value="255"/>
                 <layout type="log4net.Layout.PatternLayout">
                     <conversionPattern value="%logger"/><!--哪个记录器存储的该日志-->
                 </layout>
             </parameter>
             <parameter>
                 <parameterName value="@log_message"/>
                 <dbType value="String"/>
                 <size value="4000"/>
                 <layout type="log4net.Layout.PatternLayout">
                     <conversionPattern value="%message"/><!--日志信息-->
                 </layout>
             </parameter>
             <parameter>
                 <parameterName value="@log_exception"/>
                 <dbType value="String"/>
                 <size value="255"/>
                 <layout type="log4net.Layout.ExceptionLayout"/><!--异常信息-->
             </parameter>  
         </appender>
复制代码

另外一种写入文件的方式配置

复制代码
         <appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
             <!--这个就是我在上面提到的RollingFileAppender-->
             <file value="example.log" /><!--文件名称-->
             <appendToFile value="false" /><!--会创建新文件,一般设置为true,这里设置为false,是为了看到创建的文件-->
             <maximumFileSize value="1KB" /><!--文件大小-->
             <maxSizeRollBackups value="20" /><!--创建最大文件数-->
             <layout type="log4net.Layout.PatternLayout">
                <conversionPattern value="%level %thread %logger - %message%newline" />
             </layout>
         </appender>
复制代码

在log4net节点中还有两个节点

复制代码
      <logger name="Loggering">
        <level value="Warn"/>
        <appender-ref ref="ADONetAppender"/>
      </logger>
      <root>
        <level value="info" />
          <!--<appender-ref ref="ADONetAppender" />-->
          <appender-ref ref="SmtpAppender"/>
          <!--<appender-ref ref="LogFileAppender"/>
          <appender-ref ref="ColoredConsoleAppender"/>
          <appender-ref ref="EventLogAppender"/>
          <append-ref ref="NetSendAppender"/>
          <appender-ref ref="RollingFile"/>-->
      </root>
复制代码

在框架的体系里,所有的日志对象都是根日志(root logger)的后代。 因此如果一个日志对象没有在配置文件里显式定义,则框架使用根日志中定义的属性。在<root>标签里,可以定义level级别值和Appender的列表。如果没有定义LEVEL的值,则缺省为Debug。可以通过<appender-ref>标签定义日志对象使用的Appender对象。<appender-ref>声明了在其他地方定义的Appender对象的一个引用。在一个logger对象中的设置会覆盖根日志的设置。而对Appender属性来说,子日志对象则会继承父日志对象的Appender列表。这种缺省的行为方式也可以通过显式地设定<logger>标签的additivity属性为false而改变。

那么上面就会有数据库日志的写入和邮箱的写入

在Global.asax文件初始化配置

复制代码
        protected void Application_Start()
        {
            //读取日志  如果使用log4net,应用程序一开始的时候,都要进行初始化配置
            log4net.Config.XmlConfigurator.Configure();
            
            AreaRegistration.RegisterAllAreas();

            RegisterGlobalFilters(GlobalFilters.Filters);
            RegisterRoutes(RouteTable.Routes);
        }
复制代码

调用

复制代码
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger("Loggering");
        public ActionResult About()
        { 
             log.Info("log日志信息");
             log.Debug("debug信息");
             log.Error("error信息");
             log.Warn("warn信息");
             Exception ex = new Exception("测试的异常信息");
             log.Fatal("fatal信息", ex);
            return View();
        }
复制代码

运行一下

总结

Log4net中 Debug、INFO、WARN、ERROR 区分得很好。正常的 Debug、INFO 的日志, 就让它记录在 日志文件里面吧。

对于 WARN、ERROR 级别的日志, 记录到日志文件的同时, 顺便发送电子邮件到我的信箱里面。 这样一来, 我也不必每天去看日志文件, 二来,出了什么问题, 能及时通过电子邮件得到通知。

示例代码下载链接http://url.cn/Tz89RW

数据库结构

复制代码
create database Log4Net
go
use Log4Net
create table _log
(
    id int identity(1,1) primary key not null,
    date datetime null,
    thread int null,
    level varchar(10) null,
    logger varchar(20) null,
    Message varchar(100) null,
    Exception varchar(100) null
)
复制代码

还待优化,有空了再来琢磨琢磨。

Log4Net异常日志记录在asp.net mvc3.0的应用 – aehyok – 博客园已关闭评论
2019年11月25日 By mikel 分类: C#

来源: FileHelper 一个极好的数据文本文件处理类库 – Blues – 博客园

这个我应该是看Scott的博客,看他介绍的经典小工具的时候看到的,感谢。
我经常接触到一些csv格式存储的数据文件,把这些文件导入数据库,或者把数据库的数据导出成为excel文件或者csv文件。
自己写功能解析自然好,但是需要处理的地方太多,如果是标准文件还好,但是如果文本文件里含有保留分隔符,或者数据格式有一些小错误,都需要自己处理。而且如果数据格式变化了,每次还要修改自己的代码。所以,我一直没写这个代码。
偷懒,一直用Excel的Text to Data 功能凑合,或者用SQL的导入导出功能,再就是用从网页上拷贝粘贴数据到excel,然后清除格式的方法。
这些方法其实都有弊端,典型的就是大量的手工才做,重复劳动,这些到还能忍受了,当涉及到数据库,用的最多的自然就是SQL自带的导入导出功能。这个功能大体上好用,可是如果文本文件里面有一点错误,他就罢工。这倒还可以忍受,后来我在64位机器上装了64位的SQL,这样微软居然不支持直接由Excel导入,说64位版本不需要这个功能,我在网上看到了许多类似需求,微软都坚持说不提供这个64位的flat文件驱动!一等就是3年,SQL2005年发布,现在已经2008年了。可怕。
终于看到了这个阿根廷人编写的开源文本文件导入导出帮助类库!
本来以为还挺难使用,谁知,人家的帮助写得好,类库做的也好。我稍微调试了一下,就上手了!

最基本的用法可以看他主页的教程。
根据自己的文本文件格式,建立类库模板,定义好接受字段之类的。
增加引用类库文件到自己的项目。
一次性读取文本文件到内存。
最后是写入到其他的数据库或者转换格式保存。

我的问题是,我的文本文件比较大,大于50M,一次性读入的时候总是内存溢出,看了一下发现这个类库居然还提供了异步导入方式,就是读取一行,写一行!
略微看了一下,也好解决。

语法上,本类库兼容.net 1.0 和 .net 2.0,所以可以直接用.net 2.0的最新的语法写。写起来很方便!读取出来的数据可以直接赋给列表对象!
当然,类库本身也提供了对写入SQL,到处Excel的支持!由于作者的写入SQL的方式需要把SQL密码写道代码中,我就没有采用,而是自己用SQL数据连接写入SQL库的。
当然那个导出到EXcel的方式用起来还是很方便的。

总之这个工具最到的好处是,相当于你自己写了一个导入导出的功能,用法强大,容错性高,方便包含在自己的项目中,对文本进行自动化处理,完全省掉了自己编写功能的辛苦!强烈推荐!

The FileHelpers are a free and easy to use .NET library to import/export data from fixed length or delimited records in files, strings or streams.

The idea is pretty simple:

You can strong type your flat file (fixed or delimited) simply describing a class that maps to each record and later read/write your file as an strong typed .NET array

The Library also has support for import/export data from differents storages like Excel, Access, SQLServer, etc.



最新版下载地址(RC1的原因是因为作者最近在陪老婆生孩子,没时间发布最终版,恭喜!这个链接可能被国内拦住了,所以你可能需要代理访问下载!):
http://www.devoo.net/FileHelpers_2.2_RC1.rar

应要求,我把作者主页上的文件转到rapidshare上去,这个站点国内可以访问(本来想转到博客园控件,看了一下,才30M,这也太小了点吧???)需要的同学可以从rapidshare下载。
2.0编译版
http://rapidshare.com/files/127794302/FileHelpers_2_0_0_bin_docs_wizard.zip.html
2.0带源代码版
http://rapidshare.com/files/127794795/FileHelpers_2_0_0_source_2003_2005.zip.html
2.2更新
http://rapidshare.com/files/127795043/FileHelpers_2.2_RC1.rar.html

主页在这里,这个国内可以访问,有十分详细的帮助文档,一看就会,不要担心!!!
http://www.filehelpers.com/

下面是我做实验的代码,加了注释,你可以看一下,十分简单。

复制代码

1using System;
2using System.Configuration;
3using System.Data.SqlClient;
4//在此处增加对这个类库的引用就可以了
5using FileHelpers;
6
7
8namespace FileHelpersSample
9{
10    public partial class _Default : System.Web.UI.Page
11    {
12        protected void Page_Load(object sender, EventArgs e)
13        {
14
15        }

16
17        protected void btImportFLFtoSQLbyLINQ_Click(object sender, EventArgs e)
18        {
19
20            //清空所有已存在记录,
21            string SQLCommand = “TRUNCATE TABLE dbo.prm”;
22            int sqlresult = RunSQLStatement(SQLCommand);
23            Response.Write(sqlresult);
24            Response.Write(“<hr />”);
25
26            string strCSVFile = System.Web.HttpContext.Current.Server.MapPath(“./”) + “Data\\rrr.csv”;
27
28            //一次性读入整个文本文件
29            //FileHelperEngine<FLFRecordClass> engine = new FileHelperEngine<FLFRecordClass>();
30            //FLFRecordClass[] res = engine.ReadFile(strCSVFile);
31
32            //异步方式,一次读入一行,处理一行
33            FileHelperAsyncEngine<FLFRecordClass> engine = new FileHelperAsyncEngine<FLFRecordClass>();
34            engine.BeginReadFile(strCSVFile);
35
36            int i = 0;
37            while (engine.ReadNext() != null)
38            {
39                FLFRecordClass f = engine.LastRecord;
40
41                string Command;
42                
60
61                int j = RunSQLStatement(Command);
62                Response.Write(j);
63                Response.Write(“<br />”);
64                //if (i > 2) break;
65                i++;
66            }

67               engine.Close;
Response.Write(“<hr />”);
68            Response.Write(i);
69
70
71        }

72
73
74        public static SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings[“FLFConnectionString”].ConnectionString);
75
76        //自定义函数,用于实现对SQL数据库的更新操作
77        public int RunSQLStatement(string Command)
78        {
79            SqlCommand DataCommand = new SqlCommand(Command, conn);
80            DataCommand.Connection.Open();
81            int i = DataCommand.ExecuteNonQuery();
82            DataCommand.Connection.Close();
83            return i;
84        }

85
86    }

87}

88

复制代码

读取文本文件时,需要实现定义一个类,这个类的语法也十分简单,而且作者还提供了一个帮助向导,帮助你自动生成这个类文件(真是体贴周到!)
我这里为了偷懒,读入的时候就都都成了字符型,而且也没加什么处理,其实这个类库还只是纠错,按格式读取,读取的时候转换格式等功能!你自己慢慢看,需要的时候再用。我是一次性转到SQL临时表里,自己用程序再处理的,当然你可以用作者提供的类库在读入的时候一次性处理,究竟哪种方式好,我还没研究,感觉自己的方式比较省事(你说的?愿意听你的看法。)


1using System;
2using FileHelpers;
3
4namespace FileHelpersSample
5{
6
7    [IgnoreFirst(1)]
8    [IgnoreEmptyLines()]
9    [DelimitedRecord(“,”)]
10    public sealed class FLFRecordClass
11    {
12
13        [FieldQuoted(‘”‘, QuoteMode.OptionalForRead, MultilineMode.AllowForRead)]
14        public String Field1;
15
16        [FieldQuoted(‘”‘, QuoteMode.OptionalForRead, MultilineMode.AllowForRead)]
17        public String Field2;
18
19        [FieldQuoted(‘”‘, QuoteMode.OptionalForRead, MultilineMode.AllowForRead)]
20        public String Field3;
21
22        [FieldQuoted(‘”‘, QuoteMode.OptionalForRead, MultilineMode.AllowForRead)]
23        public String Field4;
24
25        [FieldQuoted(‘”‘, QuoteMode.OptionalForRead, MultilineMode.AllowForRead)]
26        public String Field5;
27
28        [FieldQuoted(‘”‘, QuoteMode.OptionalForRead, MultilineMode.AllowForRead)]
29        public String Field6;
30
31        [FieldQuoted(‘”‘, QuoteMode.OptionalForRead, MultilineMode.AllowForRead)]
32        public String Field7;
33
34        [FieldQuoted(‘”‘, QuoteMode.OptionalForRead, MultilineMode.AllowForRead)]
35        public String Field8;
36
37        [FieldQuoted(‘”‘, QuoteMode.OptionalForRead, MultilineMode.AllowForRead)]
38        public String Field9;
39
40        [FieldQuoted(‘”‘, QuoteMode.OptionalForRead, MultilineMode.AllowForRead)]
41        public String Field10;
42
43        //.
44
45        [FieldQuoted(‘”‘, QuoteMode.OptionalForRead, MultilineMode.AllowForRead)]
46        public String Field111;
47
48    }

49
50
51}

52
FileHelper 一个极好的数据文本文件处理类库 – Blues – 博客园已关闭评论
备案信息冀ICP 0007948