LINQ to SQL – 使用抽象基类时的映射exception

问题:我想在多个程序集之间共享代码。 这个共享代码需要使用LINQ to SQL映射类。

我在这里遇到了同样的问题,但我也找到了一个解决问题的方法(我不会说“bug”)。

可以在此解决方案中下载以下所有代码。

鉴于此表:

create table Users ( Id int identity(1,1) not null constraint PK_Users primary key , Name nvarchar(40) not null , Email nvarchar(100) not null ) 

和这个DBML映射:

 

我在一个程序集“Shared”中创建了以下基类:

 namespace TestLinq2Sql.Shared { public abstract class UserBase { public abstract int Id { get; set; } public abstract string Name { get; set; } public abstract string Email { get; set; } } public abstract class UserBase : UserBase where TUser : UserBase { public static TUser FindByName_Broken(DataContext db, string name) { return db.GetTable().FirstOrDefault(u => u.Name == name); } public static TUser FindByName_Works(DataContext db, string name) { return db.GetTable().FirstOrDefault(u => u.Name == name && 1 == 1); } public static TUser FindByNameEmail_Works(DataContext db, string name, string email) { return db.GetTable().FirstOrDefault(u => u.Name == name || u.Email == email); } } } 

这些类在另一个程序集“Main”中引用,如下所示:

 namespace TestLinq2Sql { partial class User : TestLinq2Sql.Shared.UserBase { } } 

DBML文件也位于“Main”程序集中。

调用User.FindByName_Broken(db, "test") ,会抛出exception:

System.InvalidOperationException:类成员UserBase.Name未映射。

但是,其他两个基本静态方法都可以工作。

此外,通过调用User.FindByName_Works(db, "test")生成的SQL是我们在破坏的调用中希望的:

 SELECT TOP (1) [t0].[Id], [t0].[Name], [t0].[Email] FROM [dbo].[Users] AS [t0] WHERE [t0].[Name] = @p0 -- @p0: Input NVarChar (Size = 4; Prec = 0; Scale = 0) [test] 

虽然我愿意将这个1 == 1 “hack”用于单个谓词查询,但是有更好的方法在基础/共享/核心程序集中共享LINQ to SQL感知代码吗?

我过去曾多次遇到过这个问题,因为我们在公司的框架中有类似的架构。 您可能已经注意到,如果使用声明式LINQ查询,则不会遇到此问题。 例如,以下代码将起作用:

 return (from i in db.GetTable() where i.Name = "Something").FirstOrDefault(); 

但是,由于我们使用的是动态filter表达式,因此我们无法使用此方法。 替代解决方案是使用这样的东西:

 return db.GetTable().Select(i => i).Where(i => i.Name == "Something").SingleOrDefault(); 

这个解决方案解决了我们的问题,因为我们可以在几乎所有表达式的开头注入“.Select(i => i)”。 这将导致Linq引擎不查看映射的基类,并强制它查看实际的实体类并找到映射。

希望能帮助到你

尝试在Where子句之前包含OfType

return _dbContext.GetTable().OfType().Where(expression).ToList();

我很幸运在共享程序集中定义数据类并在许多程序集中使用它们,而不是将许多程序集的数据类映射到共享契约。 使用示例命名空间,将自定义DataContext和共享数据类放在TestLinq2Sql.Shared中:

 namespace TestLinq2Sql.Shared { public class SharedContext : DataContext { public Table Users; public SharedContext (string connectionString) : base(connectionString) { } } [Table(Name = "Users")] public class User { [Column(DbType = "Int NOT NULL IDENTITY", IsPrimaryKey=true, CanBeNull = false)] public int Id { get; set; } [Column(DbType = "nvarchar(40)", CanBeNull = false)] public string Name { get; set; } [Column(DbType = "nvarchar(100)", CanBeNull = false)] public string Email { get; set; } } } 

然后从任何其他程序集中使用DataContext:

 using (TestLinq2Sql.Shared.SharedContext shared = new TestLinq2Sql.Shared.SharedContext( ConfigurationManager.ConnectionStrings["myConnString"].ConnectionString)) { var user = shared.Users.FirstOrDefault(u => u.Name == "test"); } 

这看起来像一个错误 – 我们特殊情况单个主键上进行本地查找但看起来这个代码路径没有正确地抓取元数据。

1 = 1黑客将意味着它通过正常的数据库往返,但真的应该提交一个错误…

你在这里问几个问题Jarrod,你能更具体一点吗? 也就是说,你只是想知道你的方法失败的原因吗? 或许你想要一种在不同项目中使用数据对象的方法? 我假设您不是尝试将LINQ to SQL用作数据库映射层,而是将其用作域模型? 在这种情况下,两个应用程序是否实现相同的域(业务流程,validation等)?