LINQ to SQL - отображение исключения при использовании абстрактных базовых классов
Проблема: я хотел бы поделиться кодом между несколькими сборками. Этот общий код должен работать с LINQ to SQL-сопоставленными классами.
Я столкнулся с той же проблемой, что и здесь, , но я также нашел обход, который я нахожу тревожным (я не зашел так далеко сказать "ошибка" ).
Весь следующий код можно загрузить в это решение.
С учетом этой таблицы:
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:
<Table Name="dbo.Users" Member="Users">
<Type Name="User">
<Column Name="Id" Modifier="Override" Type="System.Int32" DbType="Int NOT NULL IDENTITY" IsPrimaryKey="true" IsDbGenerated="true" CanBeNull="false" />
<Column Name="Name" Modifier="Override" Type="System.String" DbType="NVarChar(40) NOT NULL" CanBeNull="false" />
<Column Name="Email" Modifier="Override" Type="System.String" DbType="NVarChar(100) NOT NULL" CanBeNull="false" />
</Type>
</Table>
Я создал следующие базовые классы в одной сборке "Общие":
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<TUser> : UserBase where TUser : UserBase
{
public static TUser FindByName_Broken(DataContext db, string name)
{
return db.GetTable<TUser>().FirstOrDefault(u => u.Name == name);
}
public static TUser FindByName_Works(DataContext db, string name)
{
return db.GetTable<TUser>().FirstOrDefault(u => u.Name == name && 1 == 1);
}
public static TUser FindByNameEmail_Works(DataContext db, string name, string email)
{
return db.GetTable<TUser>().FirstOrDefault(u => u.Name == name || u.Email == email);
}
}
}
Эти классы указаны в другой сборке "Main", например:
namespace TestLinq2Sql
{
partial class User : TestLinq2Sql.Shared.UserBase<User>
{
}
}
Файл DBML также находится в сборке "Main".
При вызове User.FindByName_Broken(db, "test")
генерируется исключение:
System.InvalidOperationException: член класса UserBase.Name не отображается.
Однако работают два других базовых статических метода.
Кроме того, SQL, сгенерированный вызовом User.FindByName_Works(db, "test")
, является тем, на что мы надеялись в сломанном вызове:
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
"взломать" для одиночных запросов предиката, есть ли лучший способ совместного использования кода LINQ to SQL в сборке base/shared/core?
Ответы
Ответ 1
Я сталкивался с этой проблемой много раз в прошлом, потому что у нас есть аналогичная архитектура в рамках, которые мы используем в нашей компании. Возможно, вы заметили, что если вы используете запросы LINQ с декларативным стилем, вы не столкнетесь с этой проблемой. Например, следующий код будет работать:
return (from i in db.GetTable<TUser>() where i.Name = "Something").FirstOrDefault();
Однако, поскольку мы используем выражения динамического фильтра, мы не могли использовать этот метод. Альтернативное решение - использовать что-то вроде этого:
return db.GetTable<TUser>().Select(i => i).Where(i => i.Name == "Something").SingleOrDefault();
Это решение решило нашу проблему, так как мы можем ввести ".Select(i = > i)" в начало почти всех выражений. Это заставит механизм Linq не смотреть на базовый класс для сопоставлений и заставит его посмотреть на фактический класс сущности и найти сопоставления.
Надеюсь, что это поможет
Ответ 2
Попробуйте включить параметр OfType before Where
return _dbContext.GetTable<T>().OfType<T>().Where(expression).ToList();
Ответ 3
Мне посчастливилось определять классы данных в общей сборке и потреблять их во многих сборках по сравнению с сопоставлением классов данных сборок в общем контракте. Используя пространство имен примеров, поместите собственный DataContext и ваши общие классы данных в TestLinq2Sql.Shared:
namespace TestLinq2Sql.Shared
{
public class SharedContext : DataContext
{
public Table<User> 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");
}
Ответ 4
Это выглядит как ошибка - мы особым случаем Single на первичном ключе, чтобы сделать локальный поиск, но похоже, что этот путь кода не правильно захватывает метаданные.
1 = 1 взломать означает, что он идет через обычную базу данных в оба конца, но на самом деле ошибка должна быть подана...
Ответ 5
Вы задаете несколько вопросов здесь, Джаррод, можете быть более конкретным? То есть, вы просто хотите знать, почему ваш метод выходит из строя? Или, может быть, вы хотите использовать объекты данных в разных проектах? Я предполагаю, что вы не пытаетесь использовать LINQ to SQL в качестве слоя сопоставления базы данных и используете его в качестве модели домена? В этом случае оба приложения реализуют один и тот же домен (бизнес-процессы, проверка и т.д.)?