走进Linq-How do I(4)拾遗补零篇第一节

最近很忙,真的很忙,所以这个系列好久没更新了,从今天起我又开始了我的走进Linq 之旅。Linq to SQL 的用法基本上都说完了,还有一些细枝末节的地方需要聊聊。

 

强类型DataContext

Linq to SQL的第一篇的时候就说道DataContext是一个入口点,我们使用Linq to SQL做的一些操作几乎都是施加在这个类上的。在使用VS的设计器生成的代码里,我们会看到一个从DataContext继承的局部类,大家都习惯的将这个类称之为强类型的DataContext,她对DataContext做了进一步的封装。

今天我们先就对DataContext一些没有介绍过的地方详细讨论一下。

首先我们先手写一个强类型的DataContext:

ContractedBlock.gif ExpandedBlockStart.gif 强类型的DataContext
    [Database(Name="CnBlogs")]
    
public class CnBlogsDataContext : DataContext
ExpandedBlockStart.gifContractedBlock.gif    
{
        
public CnBlogsDataContext(string fileOrConnectionString)
            : 
base(fileOrConnectionString)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{ }
        
public CnBlogsDataContext(string fileOrConnectionString, MappingSource mapping)
            : 
base(fileOrConnectionString, mapping)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{ }
        
public CnBlogsDataContext(IDbConnection conn)
            : 
base(conn)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{ }
        
public CnBlogsDataContext(IDbConnection conn, MappingSource mapping)
            : 
base(conn, mapping)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{ }

        
public Table<Post> Posts
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
ExpandedSubBlockStart.gifContractedSubBlock.gif            
get return this.GetTable<Post>(); }
        }


        
public Table<Blog> Blogs
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
ExpandedSubBlockStart.gifContractedSubBlock.gif            
get return this.GetTable<Blog>(); }
        }


        
public Table<User> Users
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
ExpandedSubBlockStart.gifContractedSubBlock.gif            
get return this.GetTable<User>(); }
        }


        [Function(Name 
= "dbo.GetPostsByBlogId")]
        
public ISingleResult<Post> GetPostsByBlogId(
            [Parameter(Name
="blogid",DbType="int")]
            
int blogid)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            IExecuteResult result 
= this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), blogid);
            
return (ISingleResult<Post>)result.ReturnValue;
        }


        [Function(Name 
= "dbo.GetBblogsOrPosts")]
        [ResultType(
typeof(Blog))]
        [ResultType(
typeof(Post))]
        
public IMultipleResults GetBlogsOrPosts(
            [Parameter(Name 
= "kind", DbType = "int")]
            
int kind)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            IExecuteResult result 
= this.ExecuteMethodCall(this, ((MethodInfo)MethodInfo.GetCurrentMethod()), kind);
            
return (IMultipleResults)result.ReturnValue;
        }


        [Function(Name 
= "dbo.GetBblogsAndPosts")]
        [ResultType(
typeof(Blog))]
        [ResultType(
typeof(Post))]
        
public IMultipleResults GetBlogsOrPosts()
ExpandedSubBlockStart.gifContractedSubBlock.gif        
{
            IExecuteResult result 
= this.ExecuteMethodCall(this, ((MethodInfo)MethodInfo.GetCurrentMethod()));
            
return (IMultipleResults)result.ReturnValue;
        }

    }

在这个类里出现了四个前面没有看到的特性:Database,Function,Parameter,ResultType至于Database就不用说了,就是做数据库映射的。下面对其它三个做一些讨论:

FunctionParameterResultType

Linq to SQL不仅仅能做字段与属性之间的映射,还可以将存储过程或用户自定义方法与.net里的方法之间做映射,功能是不是很强大?这个映射就是通过FunctionParameter共同完成的。

Function有两个属性IsComposableNameName就是用来指定数据库中存储过程或者用户自定义方法的名字,当IsComposabletrue的时候,则表明该方法对应着一个用户自定义方法,否则对应一个存储过程,默认是falseFunction特性只能放在方法上面。

Parameter就是用来映射存储过程或方法接受的参数。

还是用例子来说明:

假设有这样的一个存储过程,通过blogid找出其所有的随笔

ALTER   PROCEDURE  dbo.GetPostsByBlogId
    (
    
@blogid   int
    )
AS
    
SELECT  postid,blogid,title,body,createdate  FROM  posts  WHERE  blogid  =   @blogid
    
RETURN

我要在.net里写一个方法来对应这个存储过程

        [Function(Name  =   " dbo.GetPostsByBlogId " )]
        
public  ISingleResult < Post >  GetPostsByBlogId(
            [Parameter(Name
= " blogid " ,DbType = " int " )]
            
int  blogid)
        {
            IExecuteResult result 
=   this .ExecuteMethodCall( this , ((MethodInfo)(MethodInfo.GetCurrentMethod())), blogid);
            
return  (ISingleResult < Post > )result.ReturnValue;
        }

现在你可以以调用.net方法的形式直接调用这个方法来调用存储过程

ContractedBlock.gif ExpandedBlockStart.gif Code
ISingleResult<Post> posts = dbCtx.GetPostsByBlogId(1);
foreach (var p in posts)
{
     Console.WriteLine(p.Title);
}

这样就将存储过程封装成.NET的方法了,调用看看:

使用存储过程的时候,我们往往使用一个条件参数,根据条件参数的不同返回不同的结果集:

ALTER   PROCEDURE  dbo.GetBblogsOrPosts
    (
    
@kind   int ,
    )
AS
    
if   @kind   =   1
    
SELECT   *   FROM  blogs 
    
ELSE
    
SELECT   *   FROM  posts 
    
RETURN

.NET里使用这样的方法映射:

[Function(Name  =   " dbo.GetBblogsOrPosts " )]
        [ResultType(
typeof (Blog))]
        [ResultType(
typeof (Post))]
        
public  IMultipleResults GetBlogsOrPosts(
            [Parameter(Name 
=   " kind " , DbType  =   " int " )]
            
int  kind)
        {
            IExecuteResult result 
=   this .ExecuteMethodCall( this , ((MethodInfo)MethodInfo.GetCurrentMethod()), kind);
            
return  (IMultipleResults)result.ReturnValue;
        }

返回一个ImultipleResults对象,该对象有一个GetResult方法:

IMultipleResults results  =  dbCtx.GetBlogsOrPosts( 1 );
foreach  (var b  in  results.GetResult < Blog > ())
{
     Console.WriteLine(b.Name;
}
除了支持这种存储过程外,还支持这样的:
CREATE   PROCEDURE  dbo.GetBblogsAndPosts
AS
    
SELECT   *   FROM  blogs
    
SELECT   *   FROM  posts
    
RETURN

使用方法还是和上面的一样。


 

DataContext还有几个我们比较感兴趣的方法:

ExecuteCommandExecuteQuery<TResult>Translate

这几个方法都是为了和传统的ADO.NET集成的。

ExecuteCommand可以做insert,update,delete操作,第一个参数接受一个格式化的sql语句,第二个参数是参数数组:

使用起来很简单:

dbCtx.ExecuteCommand("insert into posts(blogid,title,body) values({0},{1},{2})", "2", "走进Linq-How do I(4)", "废话一篇");


ExecuteQuery<TResult>方法呢?顾名思义,就是执行查询的,也有两个参数,第一个接受查询的SQL语句,第二个接受参数,返回一个IEnumerable<T>对象:

IEnumerable<Post> posts = dbCtx.ExecuteQuery<Post>("select * from posts where postid = {0}", "1");

            foreach (var post in posts)

                Console.WriteLine(post.Title);

Translate方法是将以前的DataReader转换为现在的Linq to SQL方式,这样有什么好处呢:

1.       可以利用现在的映射,不再需要这样的语句post.Title = dr[1].ToString();

2.       可以使用查询表达式的语法

 

对于插入,更新,删除这些经常使用的操作,为了提高性能,或者做一些自定义的操作,你还可以覆盖微软默认为你提供的Insert,Update等方法:

1.       写一个存储过程

CREATE PROCEDURE dbo.CreatePost

(

@blogid int,

@title nvarchar(50),

@body nvarchar(500)

       )

AS

insert into posts(blogid,title,body) values(@blogid,@title,@body)

RETURN

 

当然,这个存储过程非常简单,不值得这么去做,你就假设存在一个复杂的存储过程

2.       Posts表拖到设计器上,上面的存储过程拖到方法设计器上

3.       右键点击设计器上的表,在右键菜单里会有一项:配置行为(Config Behavior)



这样就可以用你自定义的存储过程做插入操作了。

 

Linq to SQL中的事务

事务这个东西在并发的时候特别重要,Linq to SQL本身就支持事务,不过是乐观锁。我们也可以显式的启动Linq to SQL的事务。

            dbCtx.Transaction = dbCtx.Connection.BeginTransaction();

            try

            {

                dbCtx.ExecuteCommand("insert into posts(blogid,title,body) values({0},{1},{2})", "2", "走进Linq-How do I(4)", "废话一篇");

                dbCtx.ExecuteCommand("insert into posts(blogid,title,body) values({0},{1},{2})", "3", "走进Linq-How do I(5)", "废话两篇");

                dbCtx.Transaction.Commit();

            }

            catch

            {

                dbCtx.Transaction.Rollback();

            }

DbConnectionBeginTransaction方法还可以接受一个枚举参数IsolationLevel,来指定事务隔离的级别。

对于那些多个DataContext之间的事务,我们可以使用.NET 2.0引入的TransactionScope

 

下一篇谈谈RowVersion的问题


转载于:https://www.cnblogs.com/yuyijq/archive/2008/08/13/1266977.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值