4

Repoデータベース LINQ プロバイダーを公開するジェネリッククラスがあります。

class Repo<T> where T : IPersisted
{
    IQueryable<T> Get()
    {
        return _queryable;
    }
}

(IPersisted永続化されたオブジェクトの単純なマーカー インターフェイスです)。

今... の特定の派生型にデフォルトの LINQ 式を挿入するエレガントな方法を見つけたいと思いますIPersisted。たとえば、次の IPersisted 実装の場合:

class Deletable : IPersisted
{
    bool IsDeleted { get; set; }
}

タイプが の場合にのみ、IQueryable<T> Get()メソッドが を返すようにします。_queryable.Where(q => !q.IsDeleted)TDeletable

ある種のディクショナリ map を作成し、メソッド内IDictionary<Type, Expression>でルックアップを行うことを考えましたが、この場合、式を強く型付けできるかどうかはわかりません。typeof(T)Get

Getの型に基づいて、「デフォルト」の LINQ 式をメソッドに挿入するにはどうすればよいTですか? タイプをデフォルトの式にマッピングする拡張可能なオブジェクト指向の方法が必要です。

4

2 に答える 2

3

IPersistedタイプには独自の式があるため、 の側でそれを制約にしIPersistedます。これで、ある種のポリモーフィズムができました。

何かのようなもの?

interface IPersisted<T> where T: IPersisted<T>
{
    Expression<Func<T, bool>> Predicate { get; }
}

class Repo<T> where T : IPersisted<T>, new()
{
    public IQueryable<T> Get()
    {
        var dummy = new T();
        return _queryable.Where(dummy.Predicate);
    }
}

class Deletable : IPersisted<Deletable>
{
    public Deletable()
    {

    }

    public Expression<Func<Deletable, bool>> Predicate
    {
        get { return x => !x.IsDeleted; }
    }

    bool IsDeleted { get; set; }
}

ここで必要なのはある種の静的ポリモーフィズムだと思いますが、C# はそれを提供していないため、式を取得するためだけにダミーのインスタンスを作成する必要があるかもしれません。


デフォルトのコンストラクターがない場合は、に頼ることができますFormatterServices.GetUninitializedObject(t)Get次のようにメソッドを調整できます。

public IQueryable<T> Get()
{
    var dummy = (T)FormatterServices.GetUninitializedObject(typeof(T));
    return _queryable.Where(dummy.Predicate);
}

おそらく多くの注意事項のうちの2つのことFormatterServices.GetUninitializedObject

  1. 何も初期化したり、コンストラクターを実行したりしませんが、それは問題にはなりません。

  2. 比較的遅いです。インスタンスをキャッシュできるので、大したことではありません:)

    class Repo<T> where T : IPersisted<T>
    {
        //caching mechanism: this is run only once per instance; you can make it 
        //static if this shud be run only once the entire lifetime of application
        readonly T dummy = (T)FormatterServices.GetUninitializedObject(typeof(T));
    
        public IQueryable<T> Get()
        {
            return _queryable.Where(dummy.Predicate);
        }
    }
    

正確な式が重要でない場合は、オブジェクトのインスタンス化を取り除くことができます。何かのようなもの:

interface IPersisted<T> where T: IPersisted<T>
{
    Func<T, bool> Predicate { get; }
}

class Repo<T> where T : IPersisted<T>
{
    public IQueryable<T> Get()
    {
        return _queryable.Where(x => x.Predicate(x));
    }
}

class Deletable : IPersisted<Deletable>
{
    public Func<Deletable, bool> Predicate
    {
        get { return x => !x.IsDeleted; }
    }
}

の元の定義を保持することIPersistedが重要な場合は、非ジェネリックにすることができます。それがそれほど強く型付けされないかどうかはわかりません。

interface IPersisted
{
    Expression<Func<object, bool>> Predicate { get; }
}

class Repo<T> where T : IPersisted
{
    public IQueryable<T> Get()
    {
        return _queryable.Where(dummy.Predicate);
    }
}

class Deletable : IPersisted
{
    public Expression<Func<object, bool>> Predicate
    {
        get { return x => !((Deletable)x).IsDeleted; }
    }
}

上記のアプローチは、メソッドを使用することでより強く型付けすることができますがIPersisted、制約が十分である必要はありません。

interface IPersisted
{
    Expression<Func<T, bool>> GetPredicate<T>() where T : IPersisted;
}

class Repo<T> where T : IPersisted
{
    public IQueryable<T> Get()
    {
        return _queryable.Where(dummy.GetPredicate<T>());
    }
}

class Deletable : IPersisted
{
    Expression<Func<T, bool>> IPersisted.GetPredicate<T>() //made it explicit
    {
        return x => ((Deletable)(object)x).IsDeleted;
    }
}

注:クラスPredicateの外で意味をなさない場合は、実装を明示的にします。Repo<T>

于 2013-09-26T14:05:05.223 に答える
0

100% 同じページにいるかどうかはわかりませんが、この get メソッドの例では、クエリをフィルター処理する where 句として使用できる linq Func を使用します。

class Repo<T> where T : IPersisted
{
    IQueryable<T> Get(Func<T, bool> filter)
    {
        return _queryable.Where(filter);
    }
}

編集:これがSQLにうまく変換されるかどうかはわかりませんが、あなたが望むものについて正しい考えを持っていることを確認するために(そしておそらく光を放ちます):このようなことを考えましたか?

class Repo<T> where T : IPersisted
{
    IQueryable<T> Get()
    {
        if (typeof (T).IsAssignableFrom(typeof (IDeletable)))
        {
            return _queryable.Where(o => ((IDeletable) o).Deleted = false).AsQueryable();
        }
        return _queryable;
    }
}
于 2013-09-26T13:37:50.827 に答える