LINQ to SQL - исключение при отображении при использовании абстрактных базовых классов - PullRequest
21 голосов
/ 20 июня 2009

Проблема: я хотел бы поделиться кодом между несколькими сборками. Этот общий код должен работать с классами 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>

Я создал следующие базовые классы в одной сборке «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<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 также находится в основной сборке.

При вызове 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 для кода, поддерживающего SQL, в базовой / разделяемой / основной сборке?

Ответы [ 5 ]

19 голосов
/ 01 июля 2009

Я сталкивался с этой проблемой много раз в прошлом, потому что у нас похожая архитектура в среде, которую мы используем в нашей компании. Возможно, вы заметили, что если вы используете запросы 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 не будет смотреть на базовый класс для сопоставлений, и заставит его посмотреть на действительный класс сущностей и найти сопоставления.

Надеюсь, это поможет

5 голосов
/ 19 июля 2013

Попробуйте включить OfType перед предложением Where

return _dbContext.GetTable<T>().OfType<T>().Where(expression).ToList();

3 голосов
/ 23 июля 2009

Это похоже на ошибку - мы особый случай Single на первичном ключе, чтобы выполнить локальный поиск, но похоже, что этот путь к коду неправильно захватывает метаданные.

Взлом 1 = 1 будет означать, что он проходит через обычную обратную передачу базы данных, но на самом деле ошибка должна быть подана ...

3 голосов
/ 29 июня 2009

Мне посчастливилось определить классы данных в общей сборке и использовать их во многих сборках, в отличие от сопоставления классов данных многих сборок с общим контрактом. Используя ваши примерные пространства имен, поместите пользовательский 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");
}  
0 голосов
/ 24 июня 2009

Вы задаете здесь несколько вопросов, Джаррод, можете ли вы быть более конкретным? То есть, вы просто хотите знать, почему ваш метод не работает? Или, может быть, вы хотите использовать объекты данных в разных проектах? Я предполагаю, что вы не пытаетесь использовать LINQ to SQL в качестве слоя отображения базы данных, и что вы используете его в качестве модели домена? В каком случае оба приложения реализуют один и тот же домен (бизнес-процессы, проверка и т. Д.)?

...