在LINQ to SQL中使用Translate方法以及修改查询用SQL
[2] 使用Translate方法1
[3] 使用Translate方法2
[4] 改变LINQ to SQL所执行的SQL语句
[5] 扩展所受的限制
新的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时,我建议保持实体对象属性名与数据库字段名之间的映射关系。