21

問題: 複数のアセンブリ間でコードを共有したいと考えています。この共有コードは、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>

1 つのアセンブリ「共有」に次の基本クラスを作成しました。

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 がマップされていません。

ただし、他の 2 つの基本的な静的メソッドは機能します。

さらに、呼び出しによって生成される SQLUser.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 対応コードを共有するより良い方法はありますか?

4

5 に答える 5

19

私たちの会社で使用しているフレームワークに同様のアーキテクチャがあるため、過去に何度もこの問題に遭遇しました。お気付きかもしれませんが、宣言型の 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 エンジンはマッピングの基本クラスを調べなくなり、実際のエンティティ クラスを調べてマッピングを見つけるように強制されます。

それが役に立てば幸い

于 2009-07-01T10:58:19.603 に答える
5

Where 句の前に OfType を含めてみてください

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

于 2013-07-18T21:42:57.080 に答える
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");
}  
于 2009-06-29T05:14:15.710 に答える
3

これはバグのように見えます - ローカル ルックアップを行うために主キーで Single を特別なケースにしていますが、このコード パスはメタデータを適切に取得していないようです。

1=1 ハックは、通常のデータベース ラウンドトリップを経由することを意味しますが、実際にはバグを報告する必要があります...

于 2009-07-23T02:50:23.993 に答える
0

ジャロッドさん、ここでいくつか質問していますが、もっと具体的に教えていただけますか? つまり、メソッドが失敗する理由を知りたいだけですか? あるいは、異なるプロジェクト間でデータ オブジェクトを使用する方法が必要ですか? LINQ to SQL をデータベース マッピング レイヤーとして使用しようとしているのではなく、それをドメイン モデルとして使用していると思いますか? どちらの場合、両方のアプリケーションが同じドメイン (ビジネス プロセス、検証など) を実装しますか?

于 2009-06-24T10:59:24.853 に答える