[转自JeffreyZhao]在LINQ to SQL中使用Translate方法以及修改查询用SQL

简介: 目前LINQ to SQL的资料不多——老赵的意思是,目前能找到的资料都难以摆脱“官方用法”的“阴影”。LINQ to SQL最权威的资料自然是MSDN,但是MSDN中的文档说明和实例总是显得“大开大阖”,依旧有清晰的“官方”烙印——这简直是一定的。
目前LINQ to SQL的资料不多——老赵的意思是,目前能找到的资料都难以摆脱“官方用法”的“阴影”。LINQ to SQL最权威的资料自然是MSDN,但是MSDN中的文档说明和实例总是显得“大开大阖”,依旧有清晰的“官方”烙印——这简直是一定的。不过从按照过往的经验,在某些时候如果不按照微软划定的道道来走,可能就会发现别样的风景。老赵在最近的项目中使用了LINQ to SQL作为数据层的基础,在LINQ to SQL开发方面积累了一定经验,也总结出了一些官方文档上并未提及的有用做法,特此和大家分享。

  言归正传,我们先看一个简单的例子。

1.jpg

  Item实体对应Item表,每个Item拥有一些评论,也就是ItemComment。Item实体中有一个Comments属性,是ItemComment实体的集合。这个例子将会使用这个再简单不过的模型。

  为用户显示他的Item列表是非常常见的需求,如果使用LINQ to SQL来获取Item的话,我们可能会这么做:

public List<Item> GetItemsForListing(int ownerId)
{
    ItemDataContext dataContext = new ItemDataContext();
    var query = from item in dataContext.Items
                where item.UserID == ownerId
                orderby item.CreateTime descending
                select item;
 
    return query.ToList();
}

  这么做自然可以实现我们想要的功能,这的确没错。但是这种做法有个很常见的问题,那就是可能会获得太多不需要的数据。一个Item数据量最大的是Introduction字段,而显示列表的时候我们是不需要显示它的。如果我们在获取Item列表时把Introduction一起获得的话,那么应用服务器和数据库服务器之间的数据通信量将会成百甚至上千地增长了。因此我们在面向此类需求的话,都会忽略每个Item对象的Introduction字段。那么我们该怎么做呢?对LINQ有简单了解的朋友们可能会想到这么做:

public List<Item> GetItemsForListing(int ownerId)
{
    ItemDataContext dataContext = new ItemDataContext();
    var query = from item in dataContext.Items
                where item.UserID == ownerId
                orderby item.CreateTime descending
                select new Item
                {
                    ItemID = item.ItemID,
                    Title = item.Title,
                    UserID = item.UserID,
                    CreateTime = item.CreateTime
                };

 
    return query.ToList();
}

  这个做法很直观,利用了C# 3.0中的Object Initializer特性。编译通过了,理应没有错,可是在运行时却抛出了NotSupportedException:“Explicit construction of entity type 'Demo.Item' in query is not allowed.”,意思就是不能在LINQ to SQL中显式构造Demo.Item对象。

  事实上在RTM之前的版本中,以上的语句是能运行通过的——我是指通过,不是正确。LINQ to SQL在RTM之前的版本有个Bug,如果在查询中显式构造一个实体的话,在某些情况下会得到一系列完全相同的对象。很可惜这个Bug我只在资料中看到过,而在RTM版本的LINQ to SQL中这个Bug已经被修补了,确切地说是绕过了。直接抛出异常不失为一种“解决问题”的办法,虽然这实际上是去除了一个功能——没有功能自然不会有Bug,就像没有头就不会头痛了一个道理。

  但是我们还得做,难道我们只能自己SQL语句了吗?

使用Translate方法

  幸亏DataContext提供了Translate方法,Translate方法的作用就是从一个DbDataReader对象中生成一系列的实例。其中最重要的就是一个带范型的重载:

public static List<Item> GetItemsForListing(int ownerId)
{
    ItemDataContext dataContext = new ItemDataContext();
    dataContext.Connection.Open();
 
    SqlCommand command = new SqlCommand(
        "SELECT [ItemID], [Title], [UserID], [CreateTime]" +
        " FROM [Item] WHERE [UserID] = " + ownerId +
        " ORDER BY [CreateTime]",
        (SqlConnection)dataContext.Connection);
 
    using (DbDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection))
    {
        return dataContext.Translate<Item>(reader).ToList();
    }
}

  在这段代码里,我们拼接出了一段SQL语句,实现了我们需要的逻辑。在ExecuteReader之后即使用dataContext.Translate方法将DbDataReader里的数据转换成Item对象。使用Translate方法除了方便之外,生成的对象也会自动Attach到DataContext中,也就是说,我们可以继续对获得的对象进行操作,例如访问Item对象的Comments属性时会自动去数据库获取数据,改变对象属性之后调用SubmitChange也能将修改提交至数据库。Translate方法从DbDataReader中生成对象的规则和内置的DataContext.ExecuteQuery方法一样,大家可以查看MSDN中的说明(中文英文)。

  此外,这里有两个细节值得一提:

  • 为什么调用ExecuteReader方法时要传入CommandBehavior.CloseConnection:LINQ to SQL中的DataContext对象有个特点,如果在使用时它的Connection对象被“显式”地打开了,即使调用了DataContext对象的Dispose方法也不会自动关闭。因此我们在开发程序的时候一定要注意这一点。例如,在调用ExecuteReader是传入CommandBehavior.CloseConnection,这样就保证了在关闭DbDataReader时同时关闭Connection——当然,我们也可以不这么做。
  • 在调用Translate方法后为什么要直接调用ToList方法:因为GetItemsForListing方法的返回值是List<Item>,这是原因之一。另一个原因是Translate方法并不会直接生成所有的对象,而是在外部代码访问Translate方法返回的IEnmuerable<T>时才会生成其中每个对象。这也是一种Lasy Load,但是也导致了所有的对象必须在Reader对象关闭之前生成,所以我一般都会在Translate方法后直接调用ToList方法,保证所有的对象已经生成了。虽然事实上我们也可以不使用using关键字而直接返回Translate方法生成的IEnumerable<Item>,不过这么做的话当前链接就得不到释放(释放,而不是关闭),也就是把处理数据连接的问题交给了方法的使用者——很可能就是业务逻辑层。为了确保分层结构的职责分明,我一般倾向于在这里确保所有对象的已经生成了。

  上面的例子使用拼接SQL字符串的方式来访问数据库,那我们又该如何使用LINQ to SQL呢?幸亏LINQ to SQL中的DataContext提供了GetCommand方法。我们直接来看一个完整的扩展:

public static class DataContextExtensions
{
    public static List<T> ExecuteQuery<T>(this DataContext dataContext, IQueryable query)
    {
        DbCommand command = dataContext.GetCommand(query);
        dataContext.OpenConnection();
 
        using (DbDataReader reader = command.ExecuteReader())
        {
            return dataContext.Translate<T>(reader).ToList();
        }
    }
 
    private static void OpenConnection(this DataContext dataContext)
    {
        if (dataContext.Connection.State == ConnectionState.Closed)
        {
            dataContext.Connection.Open();
        }
    }
}

  自从有了C# 3.0中的Extension Method,很多扩展都会显得非常优雅,我非常喜欢这个特性。DataContextExtensions是我对于LINQ to SQL中DataContext对象的扩展,如果以后有新的扩展也会写在这个类中。OpenConnection方法用于打开DataContext中的数据连接,今后的例子中也会经常看到这个方法。而这次扩展的关键在于新的ExecuteQuery方法,它接受一个IQueryable类型的对象作为参数,返回一个范型的List。方法中会使用DataContext的GetCommand方法来获得一个DbCommand。在我之前的文章,以及MSDN中的示例都只是通过这个DbCommand对象来查看LINQ to SQL所生成的查询语句。也就是说以前我们用它进行Trace和Log,而我们这次将要真正地执行这个DbCommand了。剩下的自不必说,调用ExecuteReader方法获得一个DbDataReader对象,再通过Translate方法生成一个对象列表。

  新的ExecuteQuery方法很容易使用:

public static List<Item> GetItemsForListing(int ownerId)
{
    ItemDataContext dataContext = new ItemDataContext();
    var query = from item in dataContext.Items
                where item.UserID == ownerId
                orderby item.CreateTime descending
                select new
                {
                    ItemID = item.ItemID,
                    Title = item.Title,
                    CreateTime = item.CreateTime,
                    UserID = item.UserID
                };
 
    using (dataContext.Connection)
    {
        return dataContext.ExecuteQuery<Item>(query);
    }
}

  在通过LINQ to SQL获得一个query之后,我们不再直接获得查询数据了,而是将其交给我们的ExecuteQuery扩展来执行。现在这种做法既保证了使用LINQ to SQL进行查询,又构造出Item对象的部分字段,算是一种较为理想的解决方案。不过使用这个方法来获得仅有部分字段的对象时需要注意一点:在构造匿名对象时使用的属性名,可能和目标实体对象(例如之前的Item)的属性名并非一一对应的关系。

  这种情况会在实体对象的属性名与数据表字段名不同的时候发生。在使用LINQ to SQL时默认生成的实体对象,其属性名与数据库的字段名完全对应,这自然是最理想的情况。但是有些时候我们的实体对象属性名和数据库字段名不同,这就需要在ColumnAttribute标记中设置Name参数了(当然,如果使用XmlMappingSource的话也可以设置),如下:

[Table(Name = "dbo.Item")]
public partial class Item : INotifyPropertyChanging, INotifyPropertyChanged
{
    [Column(Storage = "_OwnerID", DbType = "Int NOT NULL", Name = "UserID")]
    public int OwnerID
    {
        get {...}
        set {...}
    }
}

  OwnerID属性上标记的ColumnAttribute的Name属性设为UserID,这表示它将与Item表中的UserID字段对应。那么如果我们要在这种情况下改写之前的GetItemsForListing方法,我们该怎么做呢?可能有朋友会很自然的想到:

public static List<Item> GetItemsForListing(int ownerId)
{
    ItemDataContext dataContext = new ItemDataContext();
    var query = from item in dataContext.Items
                where item.OwnerID == ownerId
                orderby item.CreateTime descending
                select new
                {
                    ItemID = item.ItemID,
                    Title = item.Title,
                    CreateTime = item.CreateTime,
                    OwnerID = item.OwnerID
                };
 
    using (dataContext.Connection)
    {
        return dataContext.ExecuteQuery<Item>(query);
    }
}

  按照“常理”判断,似乎只要将所有的UserID改为OwnerID即可——其实不然。查看方法返回的结果就能知道,所有对象的OwnerID的值都是默认值“0”,这是怎么回事呢?使用SQL Profiler观察以上代码所执行SQL语句之后我们便可明白一切:

SELECT [t0].[ItemID], [t0].[Title], [t0].[CreateTime], [t0].[UserID] AS [OwnerID]
FROM [dbo].[Item] AS [t0]
WHERE [t0].[UserID] = @p0
ORDER BY [t0].[CreateTime] DESC

  由于我们所使用的query实际上是用于生成一系列匿名对象的,而这些匿名对象所包含的是“OwnerID”而不是“UserID”,因此LINQ to SQL实际在生成SQL语句的时候会将UserID字段名转换成OwnerID。由于Item的OwnerID上标记的ColumnAttribute把Name设置成了UserID,所以Translate方法读取DbDataReader对象时事实上会去寻找UserID字段而不是OwnerID字段——这很显然就造成了目前的问题。因此,如果您使用了ColumnAttribute中的Name属性改变了数据库字段名与实体对象属性名的映射关系,那么在创建匿名对象的时候还是要使用数据库的字段名,而不是实体对象名,如下:

public static List<Item> GetItemsForListing(int ownerId)
{
    ItemDataContext dataContext = new ItemDataContext();
    var query = from item in dataContext.Items
                where item.OwnerID == ownerId
                orderby item.CreateTime descending
                select new
                {
                    ItemID = item.ItemID,
                    Title = item.Title,
                    CreateTime = item.CreateTime,
                    UserID = item.OwnerID
                };
 
    using (dataContext.Connection)
    {
        return dataContext.ExecuteQuery<Item>(query);
    }
}

  这样就能解决问题了——不过显得不很漂亮,因此在使用LINQ to SQL时,我建议保持实体对象属性名与数据库字段名之间的映射关系。

改变LINQ to SQL所执行的SQL语句

  按照一般的做法我们很难改变LINQ to SQL查询所执行的SQL语句,但是既然我们能够将一个query转化为DbCommand对象,我们自然可以在执行之前改变它的CommandText。我这里通过一个比较常用的功能来进行演示。

  数据库事务会带来锁,锁会降低数据库并发性,在某些“不巧”的情况下还会造成死锁。对于一些查询语句,我们完全可以显式为SELECT语句添加WITH (NOLOCK)选项来避免发出共享锁。因此我们现在扩展刚才的ExecuteQuery方法,使它接受一个withNoLock参数,表明是否需要为SELECT添加WITH (NOLOCK)选项。请看示例:

public static class DataContextExtensions
{
    public static List<T> ExecuteQuery<T>(
        this DataContext dataContext, IQueryable query, bool withNoLock)
    {
        DbCommand command = dataContext.GetCommand(query, withNoLock);
 
        dataContext.OpenConnection();
 
        using (DbDataReader reader = command.ExecuteReader())
        {
            return dataContext.Translate<T>(reader).ToList();
        }
    }
 
    private static Regex s_withNoLockRegex =
        new Regex(@"(] AS \[t\d+\])", RegexOptions.Compiled);
 
    private static string AddWithNoLock(string cmdText)
    {
        IEnumerable<Match> matches =
            s_withNoLockRegex.Matches(cmdText).Cast<Match>()
            .OrderByDescending(m => m.Index);
        foreach (Match m in matches)
        {
            int splitIndex = m.Index + m.Value.Length;
            cmdText =
                cmdText.Substring(0, splitIndex) + " WITH (NOLOCK)" +
                cmdText.Substring(splitIndex);
        }
 
        return cmdText;
    }
 
    private static SqlCommand GetCommand(
        this DataContext dataContext, IQueryable query, bool withNoLock)
    {
        SqlCommand command = (SqlCommand)dataContext.GetCommand(query);
 
        if (withNoLock)
        {
            command.CommandText = AddWithNoLock(command.CommandText);
        }
 
        return command;
    }
}

  上面这段逻辑的关键在于使用正则表达式查找需要添加WITH (NOLOCK)选项的位置。在这里我查找SQL语句中类似“] AS [t0]”的字符串,并且在其之后添加WITH (NOLOCK)选项。其他的代码大家应该完全能够看懂,我在这里就不多作解释了。我们直接来看一下使用示例:

public static List<Item> GetItemsForListingWithNoLock(int ownerId)
{
    ItemDataContext dataContext = new ItemDataContext();
    var query = from item in dataContext.Items
                where item.UserID == ownerId
                orderby item.CreateTime descending
                select new
                {
                    ItemID = item.ItemID,
                    Title = item.Title,
                    CreateTime = item.CreateTime,
                    UserID = item.UserID
                };
 
    using (dataContext.Connection)
    {
        return dataContext.ExecuteQuery<Item>(query, true);
    }
}

  使用SQL Profiler查看上述代码所执行的SQL语句,就会发现:

SELECT [t0].[ItemID], [t0].[Title], [t0].[CreateTime], [t0].[UserID]
FROM [dbo].[Item] AS [t0] WITH (NOLOCK)
WHERE [t0].[UserID] = @p0
ORDER BY [t0].[CreateTime] DESC

  很漂亮。事实上只要我们需要,就可以在DbCommand对象生成的SQL语句上作任何修改(例如添加事务操作,容错代码等等),只要其执行出来的结果保持不变即可(事实上变又如何,如果您真有自己巧妙设计的话,呵呵)。

以上扩展所受限制

  以上的扩展并非无可挑剔。由于Translate方法的特点,此类做法都无法充分发挥LINQ to SQL查询的所有能力——那就是所谓的“LoadWith”能力。

  在LINQ to SQL中,默认会使用延迟加载,然后在必要的时候才会再去数据库进行查询。这个做法有时候会降低系统性能,例如:

List<Item> itemList = GetItems(1);
foreach (Item item in itemList)
{
    foreach (ItemComment comment in item.Comments)
    {
        Console.WriteLine(comment.Content);
    }
}

  这种做法的性能很低,因为默认情况下每个Item对象的ItemComment集合不会被同时查询出来,而是会等到内层的foreach循环执行时再次查询数据库。为了避免不合适的Lazy Load降低性能,LINQ to SQL提供了DataLoadOptions机制进行控制:

public static List<Item> GetItems(int ownerId)
{
    ItemDataContext dataContext = new ItemDataContext();
 
    DataLoadOptions loadOptions = new DataLoadOptions();
    loadOptions.LoadWith<Item>(item => item.Comments);
    dataContext.LoadOptions = loadOptions;
 
    var query = from item in dataContext.Items
                where item.UserID == ownerId
                orderby item.CreateTime descending
                select item;
 
    return query.ToList();
}

  当我们为DataContext对象设置了LoadOptions并且指明了“Load With”关系,LINQ to SQL就会根据要求查询数据库——在上面的例子中,它将生成如下的SQL语句:

SELECT [t0].[ItemID], [t0].[Title], [t0].[Introduction], [t0].[UserID], [t0].[CreateTime], [t1].[ItemCommentID], [t1].[ItemID] AS [ItemID2], [t1].[Content], [t1].[UserID], [t1].[CreateTime] AS [CreateTime2], (
    SELECT COUNT(*)
    FROM [dbo].[ItemComment] AS [t2]
    WHERE [t2].[ItemID] = [t0].[ItemID]
    ) AS [value]
FROM [dbo].[Item] AS [t0]
LEFT OUTER JOIN [dbo].[ItemComment] AS [t1] ON [t1].[ItemID] = [t0].[ItemID]
WHERE [t0].[UserID] = @p0
ORDER BY [t0].[CreateTime] DESC, [t0].[ItemID], [t1].[ItemCommentID]

  相信大家已经了解Translate方法为何无法充分发挥LINQ to SQL的能力了。那么我们又该如何解决这个问题呢?如果您希望同时使用本文类似的扩展和Load With能力,可能就需要通过查询两次数据库并加以组合的方式来生成对象了——虽然查询了两次,但总比查询100次的性能要高。

 

以下是根据老赵的文章,实践的代码:

这是DataContext扩展方法类:

 

using  System;
using  System.Collections.Generic;
using  System.Data;
using  System.Data.SqlClient;
using  System.Data.Common;
using  System.Linq;
using  System.Data.Linq;
using  System.Text;
using  System.Text.RegularExpressions;

namespace  CNTVS.DATA
... {
    
public static class DataContextExentions
    
...{
        
/**//// <summary>
        
/// 打开连接
        
/// </summary>
        
/// <param name="dataContext"></param>

        private static void OpenConnection(this DataContext dataContext)
        
...{
            
if (dataContext.Connection.State == ConnectionState.Closed)
            
...{
                dataContext.Connection.Open();
            }

        }



        
/**//// <summary>
        
/// 扩展ExecuteQuery方法 
        
/// </summary>
        
/// <typeparam name="T"></typeparam>
        
/// <param name="dataContext"></param>
        
/// <param name="query"></param>
        
/// <param name="withNoLock"></param>
        
/// <returns></returns>

        public static List<T> ExecuteQuery<T>(this DataContext dataContext, IQueryable query, bool withNoLock)
        
...{
            DbCommand command 
= dataContext.GetCommand(query, withNoLock);

            dataContext.OpenConnection();

            
using (DbDataReader reader = command.ExecuteReader())
            
...{
                
return dataContext.Translate<T>(reader).ToList();
            }

        }



        
/**//// <summary>
        
/// 扩展ExecuteQuery方法2
        
/// </summary>
        
/// <typeparam name="T"></typeparam>
        
/// <param name="dataContext"></param>
        
/// <param name="query"></param>
        
/// <param name="withNoLock"></param>
        
/// <returns></returns>

        public static List<T> ExecuteQuery<T>(this DataContext dataContext, IQueryable query)
        
...{
            DbCommand command 
= dataContext.GetCommand(query);
            dataContext.OpenConnection();

            
using (DbDataReader reader = command.ExecuteReader())
            
...{
                
return dataContext.Translate<T>(reader).ToList();
            }

        }


        
/**//// <summary>
        
/// 扩展GetCommend方法,允许设置WithNoLick
        
/// </summary>
        
/// <param name="dataContext"></param>
        
/// <param name="query"></param>
        
/// <param name="withNoLock"></param>
        
/// <returns></returns>

        private static SqlCommand GetCommand(this DataContext dataContext, IQueryable query, bool withNoLock)
        
...{
            SqlCommand command 
= (SqlCommand)dataContext.GetCommand(query);

            
if (withNoLock)
            
...{
                command.CommandText 
= AddWithNoLock(command.CommandText);
            }


            
return command;
        }


        
/**//// <summary>
        
/// 将Sql语句修改为with nolock
        
/// </summary>
        
/// <param name="cmdText"></param>
        
/// <returns></returns>

        private static string AddWithNoLock(string cmdText)
        
...{
            IEnumerable
<Match> matches =
                s_withNoLockRegex.Matches(cmdText).Cast
<Match>()
                .OrderByDescending(m 
=> m.Index);
            
foreach (Match m in matches)
            
...{
                
int splitIndex = m.Index + m.Value.Length;
                cmdText 
=
                    cmdText.Substring(
0, splitIndex) + " WITH (NOLOCK)" +
                    cmdText.Substring(splitIndex);
            }


            
return cmdText;
        }


        
private static Regex s_withNoLockRegex =  new Regex(@"(] AS [td+])", RegexOptions.Compiled);
    }

}

 

这是WebData类:

 

using  System;
using  System.Collections.Generic;
using  System.Linq;
using  System.Data.Linq;
using  System.Configuration;
using  System.Text;

namespace  CNTVS.DATA
... {
    
public static class WebData
    
...{
        
private static string _connStr = ConfigurationManager.ConnectionStrings["ConnStr"].ToString();

        
//public static List<Item> GetItemsForListing(int ownerId)
        
//{
        
//    DataDataContext dataContext = new DataDataContext(ConfigurationManager.ConnectionStrings["ConnStr"].ToString());
        
//    var query = from item in dataContext.Items
        
//                where item.UserID == ownerId
        
//                orderby item.CreateTime descending
        
//                select item;

        
//    return query.ToList();
        
//}


        
public static List<Item> GetItemsForListing(int ownerId)
        
...{
            DataDataContext dataContext 
= new DataDataContext(_connStr);
            var query 
= from item in dataContext.Items
                        
where item.UserID == ownerId
                        orderby item.CreateTime descending
                        select 
new
                        
...{
                            ItemID 
= item.ItemID,
                            Title 
= item.Title,
                            CreateTime 
= item.CreateTime,
                            UserID 
= item.UserID
                        }
;

            
using (dataContext.Connection)
            
...{
                
return dataContext.ExecuteQuery<Item>(query);
            }

        }


        
public static List<Item> GetItemsForListing(int ownerId,bool WithNoLock)
        
...{
            DataDataContext dataContext 
= new DataDataContext(_connStr);
            var query 
= from item in dataContext.Items
                        
where item.UserID == ownerId
                        orderby item.CreateTime descending
                        select 
new
                        
...{
                            ItemID 
= item.ItemID,
                            Title 
= item.Title,
                            CreateTime 
= item.CreateTime,
                            UserID 
= item.UserID
                        }
;

            
using (dataContext.Connection)
            
...{
                
return dataContext.ExecuteQuery<Item>(query,WithNoLock);
            }

        }


        
public static List<Item> GetItems(int ownerId)
        
...{
            DataDataContext dataContext 
= new DataDataContext(_connStr);

            DataLoadOptions loadOptions 
= new DataLoadOptions();
            loadOptions.LoadWith
<Item>(item => item.ItemComments);
            dataContext.LoadOptions 
= loadOptions;

            var query 
= from item in dataContext.Items
                        
where item.UserID == ownerId
                        orderby item.CreateTime descending
                        select item;

            
return query.ToList();
        }





    }

}

这是Website的页面Default.aspx

 

<% @ Page Language = " C# "  AutoEventWireup = " true "   CodeFile = " Default.aspx.cs "  Inherits = " _Default "   %>

<! DOCTYPE html PUBLIC  " -//W3C//DTD XHTML 1.0 Transitional//EN "   " http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd " >

< html xmlns = " http://www.w3.org/1999/xhtml " >
< head runat = " server " >
    
< title > Untitled Page </ title >
    
< style type = " text/css "  media = " all " >
        ul
... {background:#ccc;width:200px;}
        li
... {float:left;width:100px;line-height:35px;}
    
</ style >
</ head >
< body >
    
< form id = " form1 "  runat = " server " >
    
< asp:Repeater ID = " Repeater1 "  runat = " server "  EnableViewState = " false " >
        
< HeaderTemplate >
            
< ul >
                
< li > ItemID </ li >
                
< li > Title </ li >              
        
</ HeaderTemplate >
        
< ItemTemplate >
                
< li ><% # Eval( " ItemID " %></ li >
                
< li ><% # Eval( " Title " %></ li >
        
</ ItemTemplate >
        
< FooterTemplate >
            
</ ul >
        
</ FooterTemplate >
    
</ asp:Repeater >
    
    
<%= ShowComments() %>
    
</ form >
</ body >
</ html >

 

这是页面的后端Cs:

 

using  System;
using  System.Configuration;
using  System.Collections.Generic;
using  System.Data;
using  System.Linq;
using  System.Web;
using  System.Web.Security;
using  System.Web.UI;
using  System.Web.UI.HtmlControls;
using  System.Web.UI.WebControls;
using  System.Web.UI.WebControls.WebParts;
using  System.Xml.Linq;
using  System.Text;
using  CNTVS.DATA;

public   partial   class  _Default : System.Web.UI.Page 
... {
    
protected void Page_Load(object sender, EventArgs e)
    
...{
        ShowData();
    }


    
void ShowData() 
    
...{
        
this.Repeater1.DataSource = WebData.GetItemsForListing(0,true);
        
this.Repeater1.DataBind();
    }


    
protected string ShowComments() 
    
...{
        StringBuilder sb 
= new StringBuilder();
        List
<Item> itemList = WebData.GetItems(0);
        
foreach (Item item in itemList)
        
...{
            
foreach (ItemComment comment in item.ItemComments)
            
...{
                sb.AppendLine(comment.Content.ToString() 
+ "<br/>");
            }

        }

        
return sb.ToString();
    }

}


目录
相关文章
|
10天前
|
SQL 运维 监控
SQL查询太慢?实战讲解YashanDB SQL调优思路
本文是Meetup第十期“调优实战专场”的第二篇技术文章,上一篇《高效查询秘诀,解码YashanDB优化器分组查询优化手段》中,我们揭秘了YashanDB分组查询优化秘诀,本文将通过一个案例,助你快速上手YashanDB慢日志功能,精准定位“慢SQL”后进行优化。
|
7天前
|
SQL 索引
【YashanDB知识库】字段加上索引后,SQL查询不到结果
【YashanDB知识库】字段加上索引后,SQL查询不到结果
|
7天前
|
SQL 大数据 数据挖掘
玩转大数据:从零开始掌握SQL查询基础
玩转大数据:从零开始掌握SQL查询基础
78 35
|
27天前
|
SQL 关系型数据库 分布式数据库
利用 PolarDB PG 版向量化引擎,加速复杂 SQL 查询!完成任务领发财新年抱枕!
利用 PolarDB PG 版向量化引擎,加速复杂 SQL 查询!完成任务领发财新年抱枕!
|
18天前
|
SQL 关系型数据库 OLAP
云原生数据仓库AnalyticDB PostgreSQL同一个SQL可以实现向量索引、全文索引GIN、普通索引BTREE混合查询,简化业务实现逻辑、提升查询性能
本文档介绍了如何在AnalyticDB for PostgreSQL中创建表、向量索引及混合检索的实现步骤。主要内容包括:创建`articles`表并设置向量存储格式,创建ANN向量索引,为表增加`username`和`time`列,建立BTREE索引和GIN全文检索索引,并展示了查询结果。参考文档提供了详细的SQL语句和配置说明。
33 1
|
4天前
|
SQL 缓存 关系型数据库
SQL为什么不建议执行多表关联查询
本文探讨了SQL中不建议执行多表关联查询的原因,特别是MySQL与PG在多表关联上的区别。MySQL仅支持嵌套循环连接,而不支持排序-合并连接和散列连接,因此在多表(超过3张)关联查询时效率较低。文章还分析了多表关联查询与多次单表查询的效率对比,指出将关联操作放在Service层处理的优势,包括减少数据库计算资源消耗、提高缓存效率、降低锁竞争以及更易于分布式扩展等。最后,通过实例展示了如何分解关联查询以优化性能。
|
26天前
|
SQL 数据可视化 IDE
SQL做数据分析的困境,查询语言无法回答的真相
SQL 在简单数据分析任务中表现良好,但面对复杂需求时显得力不从心。例如,统计新用户第二天的留存率或连续活跃用户的计算,SQL 需要嵌套子查询和复杂关联,代码冗长难懂。Python 虽更灵活,但仍需变通思路,复杂度较高。相比之下,SPL(Structured Process Language)语法简洁、支持有序计算和分组子集保留,具备强大的交互性和调试功能,适合处理复杂的深度数据分析任务。SPL 已开源免费,是数据分析师的更好选择。
|
6月前
|
关系型数据库 MySQL 网络安全
5-10Can't connect to MySQL server on 'sh-cynosl-grp-fcs50xoa.sql.tencentcdb.com' (110)")
5-10Can't connect to MySQL server on 'sh-cynosl-grp-fcs50xoa.sql.tencentcdb.com' (110)")
|
8月前
|
SQL 存储 监控
SQL Server的并行实施如何优化?
【7月更文挑战第23天】SQL Server的并行实施如何优化?
206 13

热门文章

最新文章