21

私は次の方法を書きました。

public T GetByID(int id)
{
    var dbcontext = DB;
    var table = dbcontext.GetTable<T>();
    return table.ToList().SingleOrDefault(e => Convert.ToInt16(e.GetType().GetProperties().First().GetValue(e, null)) == id);
}

基本的に、これはGenericクラスのメソッドであり、TはDataContextのクラスです。

このメソッドは、T(GetTable)のタイプからテーブルを取得し、入力されたパラメーターの最初のプロパティ(常にID)をチェックします。

これに伴う問題は、プロパティでを実行するために最初に要素のテーブルをリストに変換する必要がGetTypeあったことですが、テーブルのすべての要素を列挙してに変換する必要があるため、これはあまり便利ではありませんList

ToListテーブル全体でを回避するために、このメソッドをリファクタリングするにはどうすればよいですか?

[アップデート]

テーブルで直接実行できない理由Whereは、次の例外を受け取ったためです。

メソッド'System.Reflection.PropertyInfo[] GetProperties()'には、SQLへの変換がサポートされていません。

GetPropertiesSQLに変換できないためです。

[アップデート]

Tのインターフェイスを使用することを提案する人もいますが、問題は、Tパラメーターが[DataContextName] .designer.csで自動生成されるクラスになるため、インターフェイスを実装できないことです(そして、 LINQのこれらすべての「データベースクラス」のインターフェイス。また、DataContextに新しいテーブルを追加するとファイルが再生成されるため、書き込まれたすべてのデータが失われます。

だから、これを行うためのより良い方法がなければなりません...

[アップデート]

Neil Williamsの提案のようにコードを実装しましたが、まだ問題があります。コードの抜粋は次のとおりです。

インターフェース:

public interface IHasID
{
    int ID { get; set; }
}

DataContext [コードの表示]:

namespace MusicRepo_DataContext
{
    partial class Artist : IHasID
    {
        public int ID
        {
            get { return ArtistID; }
            set { throw new System.NotImplementedException(); }
        }
    }
}

一般的な方法:

public class DBAccess<T> where T :  class, IHasID,new()
{
    public T GetByID(int id)
    {
        var dbcontext = DB;
        var table = dbcontext.GetTable<T>();

        return table.SingleOrDefault(e => e.ID.Equals(id));
    }
}

この行で例外がスローされています:return table.SingleOrDefault(e => e.ID.Equals(id));そして例外は次のとおりです:

System.NotSupportedException: The member 'MusicRepo_DataContext.IHasID.ID' has no supported translation to SQL.

[更新]解決策:

DenisTrollerの投稿された回答とCodeRantブログの投稿へのリンクの助けを借りて、私はついに解決策を見つけることができました:

public static PropertyInfo GetPrimaryKey(this Type entityType)
{
    foreach (PropertyInfo property in entityType.GetProperties())
    {
        ColumnAttribute[] attributes = (ColumnAttribute[])property.GetCustomAttributes(typeof(ColumnAttribute), true);
        if (attributes.Length == 1)
        {
            ColumnAttribute columnAttribute = attributes[0];
            if (columnAttribute.IsPrimaryKey)
            {
                if (property.PropertyType != typeof(int))
                {
                    throw new ApplicationException(string.Format("Primary key, '{0}', of type '{1}' is not int",
                                property.Name, entityType));
                }
                return property;
            }
        }
    }
    throw new ApplicationException(string.Format("No primary key defined for type {0}", entityType.Name));
}

public T GetByID(int id)
{
    var dbcontext = DB;

    var itemParameter = Expression.Parameter(typeof (T), "item");
    var whereExpression = Expression.Lambda<Func<T, bool>>
        (
        Expression.Equal(
            Expression.Property(
                 itemParameter,
                 typeof (T).GetPrimaryKey().Name
                 ),
            Expression.Constant(id)
            ),
        new[] {itemParameter}
        );
    return dbcontext.GetTable<T>().Where(whereExpression).Single();
}
4

6 に答える 6

18

必要なのは、LINQtoSQLが理解できる式ツリーを構築することです。「id」プロパティの名前が常に「id」であると仮定します。

public virtual T GetById<T>(short id)
{
    var itemParameter = Expression.Parameter(typeof(T), "item");
    var whereExpression = Expression.Lambda<Func<T, bool>>
        (
        Expression.Equal(
            Expression.Property(
                itemParameter,
                "id"
                ),
            Expression.Constant(id)
            ),
        new[] { itemParameter }
        );
    var table = DB.GetTable<T>();
    return table.Where(whereExpression).Single();
}

これでうまくいくはずです。このブログから恥知らずに借りました。これは基本的に、次のようなクエリを作成するときにLINQtoSQLが行うことです。

var Q = from t in Context.GetTable<T)()
        where t.id == id
        select t;

Tが「id」プロパティを持っていることを強制することはできず、インターフェイスからデータベースに任意の「id」プロパティをマップできないため、コンパイラはLTSの作業を行うことができないため、LTSの作業を行うだけです。

====更新====

OK、これは主キー名を見つけるための簡単な実装です。1つだけ(複合主キーではない)であり、すべてがタイプごとに適切である(つまり、主キーが「短い」タイプと互換性がある)と仮定します。 GetById関数で使用):

public virtual T GetById<T>(short id)
{
    var itemParameter = Expression.Parameter(typeof(T), "item");
    var whereExpression = Expression.Lambda<Func<T, bool>>
        (
        Expression.Equal(
            Expression.Property(
                itemParameter,
                GetPrimaryKeyName<T>()
                ),
            Expression.Constant(id)
            ),
        new[] { itemParameter }
        );
    var table = DB.GetTable<T>();
    return table.Where(whereExpression).Single();
}


public string GetPrimaryKeyName<T>()
{
    var type = Mapping.GetMetaType(typeof(T));

    var PK = (from m in type.DataMembers
              where m.IsPrimaryKey
              select m).Single();
    return PK.Name;
}
于 2009-04-09T20:53:20.783 に答える
1

GetTable()。Where(...)を使用するようにこれを作り直し、そこにフィルタリングを配置するとどうなりますか?

Where拡張メソッドは、テーブル全体をリストにフェッチするよりもフィルタリングを適切に処理する必要があるため、これはより効率的です。

于 2009-04-09T17:34:34.693 に答える
1

いくつかの考え...

ToList()呼び出しを削除するだけで、SingleOrDefaultはIEnumerablyで動作します。これはテーブルであると推測されます。

e.GetType()。GetProperties()。First()の呼び出しをキャッシュして、返されるPropertyInfoを取得します。

Tに制約を追加して、Idプロパティを公開するインターフェイスを実装するように強制することはできませんか?

于 2009-04-09T17:36:42.033 に答える
0

クエリを実行するのは良い考えかもしれません。

public static T GetByID(int id)
    {
        Type type = typeof(T);
        //get table name
        var att = type.GetCustomAttributes(typeof(TableAttribute), false).FirstOrDefault();
        string tablename = att == null ? "" : ((TableAttribute)att).Name;
        //make a query
        if (string.IsNullOrEmpty(tablename))
            return null;
        else
        {
            string query = string.Format("Select * from {0} where {1} = {2}", new object[] { tablename, "ID", id });

            //and execute
            return dbcontext.ExecuteQuery<T>(query).FirstOrDefault();
        }
    }
于 2009-04-09T17:59:55.893 に答える
0

それにかんする:

System.NotSupportedException:メンバー'MusicRepo_DataContext.IHasID.ID'には、SQLへのサポートされている変換がありません。

最初の問題の簡単な回避策は、式を指定することです。以下を参照してください、それは私にとって魅力のように機能します。

public interface IHasID
{
    int ID { get; set; }
}
DataContext [View Code]:

namespace MusicRepo_DataContext
{
    partial class Artist : IHasID
    {
        [Column(Name = "ArtistID", Expression = "ArtistID")]
        public int ID
        {
            get { return ArtistID; }
            set { throw new System.NotImplementedException(); }
        }
    }
}
于 2009-04-21T01:28:51.417 に答える
0

OK、このデモの実装を確認してください。datacontext(Linq To Sql)を使用して汎用GetByIdを取得しようとしています。マルチキープロパティとも互換性があります。

using System;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Linq;
using System.Reflection;
using System.Collections.Generic;

public static class Programm
{
    public const string ConnectionString = @"Data Source=localhost\SQLEXPRESS;Initial Catalog=TestDb2;Persist Security Info=True;integrated Security=True";

    static void Main()
    {
        using (var dc = new DataContextDom(ConnectionString))
        {
            if (dc.DatabaseExists())
                dc.DeleteDatabase();
            dc.CreateDatabase();
            dc.GetTable<DataHelperDb1>().InsertOnSubmit(new DataHelperDb1() { Name = "DataHelperDb1Desc1", Id = 1 });
            dc.GetTable<DataHelperDb2>().InsertOnSubmit(new DataHelperDb2() { Name = "DataHelperDb2Desc1", Key1 = "A", Key2 = "1" });
            dc.SubmitChanges();

            Console.WriteLine("Name:" + GetByID(dc.GetTable<DataHelperDb1>(), 1).Name);
            Console.WriteLine("");
            Console.WriteLine("");
            Console.WriteLine("Name:" + GetByID(dc.GetTable<DataHelperDb2>(), new PkClass { Key1 = "A", Key2 = "1" }).Name);
        }
    }

    //Datacontext definition
    [Database(Name = "TestDb2")]
    public class DataContextDom : DataContext
    {
        public DataContextDom(string connStr) : base(connStr) { }
        public Table<DataHelperDb1> DataHelperDb1;
        public Table<DataHelperDb2> DataHelperD2;
    }

    [Table(Name = "DataHelperDb1")]
    public class DataHelperDb1 : Entity<DataHelperDb1, int>
    {
        [Column(IsPrimaryKey = true)]
        public int Id { get; set; }
        [Column]
        public string Name { get; set; }
    }

    public class PkClass
    {
        public string Key1 { get; set; }
        public string Key2 { get; set; }
    }
    [Table(Name = "DataHelperDb2")]
    public class DataHelperDb2 : Entity<DataHelperDb2, PkClass>
    {
        [Column(IsPrimaryKey = true)]
        public string Key1 { get; set; }
        [Column(IsPrimaryKey = true)]
        public string Key2 { get; set; }
        [Column]
        public string Name { get; set; }
    }

    public class Entity<TEntity, TKey> where TEntity : new()
    {
        public static TEntity SearchObjInstance(TKey key)
        {
            var res = new TEntity();
            var targhetPropertyInfos = GetPrimaryKey<TEntity>().ToList();
            if (targhetPropertyInfos.Count == 1)
            {
                targhetPropertyInfos.First().SetValue(res, key, null);
            }
            else if (targhetPropertyInfos.Count > 1) 
            {
                var sourcePropertyInfos = key.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
                foreach (var sourcePi in sourcePropertyInfos)
                {
                    var destinationPi = targhetPropertyInfos.FirstOrDefault(x => x.Name == sourcePi.Name);
                    if (destinationPi == null || sourcePi.PropertyType != destinationPi.PropertyType)
                        continue;

                    object value = sourcePi.GetValue(key, null);
                    destinationPi.SetValue(res, value, null);
                }
            }
            return res;
        }
    }

    public static IEnumerable<PropertyInfo> GetPrimaryKey<T>()
    {
        foreach (var info in typeof(T).GetProperties().ToList())
        {
            if (info.GetCustomAttributes(false)
            .Where(x => x.GetType() == typeof(ColumnAttribute))
            .Where(x => ((ColumnAttribute)x).IsPrimaryKey)
            .Any())
                yield return info;
        }
    }
    //Move in repository pattern
    public static TEntity GetByID<TEntity, TKey>(Table<TEntity> source, TKey id) where TEntity : Entity<TEntity, TKey>, new()
    {
        var searchObj = Entity<TEntity, TKey>.SearchObjInstance(id);
        Console.WriteLine(source.Where(e => e.Equals(searchObj)).ToString());
        return source.Single(e => e.Equals(searchObj));
    }
}

結果:

SELECT [t0].[Id], [t0].[Name]
FROM [DataHelperDb1] AS [t0]
WHERE [t0].[Id] = @p0

Name:DataHelperDb1Desc1


SELECT [t0].[Key1], [t0].[Key2], [t0].[Name]
FROM [DataHelperDb2] AS [t0]
WHERE ([t0].[Key1] = @p0) AND ([t0].[Key2] = @p1)

Name:DataHelperDb2Desc1
于 2014-01-02T22:47:07.210 に答える