LINQ to SQLフィルターで条件付きでインターフェイスプロパティにアクセスするために、repository <T>のジェネリックTをインターフェイスにキャストするにはどうすればよいですか?

アレックス

Repository<T> where T: classドメインモデルで使用される汎用リポジトリがありますT

として使用される一部のクラスには、T追加の重要なプロパティがあり、インターフェイスを使用して使用できます(たとえば、「プロパティ「abc」IAbcがあり、関連するインターフェイスです)。

public interface IAbc
{
   string Abc { get; }
}

public class MyClass: IAbc
{
   public string Abc { get; set; }
}

私が達成しようとしているのは、内部の特定のメソッドでインターフェイスキャストを介してこれらの追加フィールドを公開Repository<T>し、フィルタリング、条件付き意思決定などに使用することです。

// Note: The repository shown below only has a generic constraint on class.
// It can not have a constraint on IAbc, since not all classes T using the
// repository also implement IAbc.

public class MyRepository<T> where T: class
{
    // abbreviated for brevity

    public IQueryable<T> GetSomething()
    {
        // What I am trying to do here:
        // If generic T implements IAbc, I want to use T's "abc" property
        // in my Where filter, which should logically be possible if T
        // implements IAbc. 
        // However, since not ALL T implement IAbc, I can't make this a 
        // constraint on the entire repository. 
        // My approach to achieve this is to (1) have an assignability check
        // and (2) cast to IAbc in the Where predicate. The cast is not 
        // working though, see the error below.

        if (typeof(IAbc).IsAssignableFrom(typeof(T))
        {
             return DbSet<T>().Where(x => ((IAbc)x).Abc == "hey");
        }

    // abbreviated for brevity
}

ただし、これを実行しようとすると、次の例外が発生します。

タイプ「T」をタイプ「IAbc」にキャストできません。LINQ to Entitiesは、EDMプリミティブまたは列挙型のキャストのみをサポートします。

ご協力ありがとうございました。

Ivan Stoev

私が見ている1つの可能な方法は、静的に制約されたジェネリックメソッドを別の非ジェネリッククラスで作成し、DLR動的ディスパッチを使用してそれらを呼び出すことです。

たとえば、ヘルパー:

public static class MyRepository
{
    public static IQueryable<T> GetSomething<T>(IQueryable<T> source)
        where T : class, IAbc
    {
        return source.Where(x => x.Abc == "hey");
    } 
}

および使用法:

public class MyRepository<T> where T : class
{
    public IQueryable<T> GetSomething()
    {
        if (typeof(IAbc).IsAssignableFrom(typeof(T)))
        {
            return MyRepository.GetSomething((dynamic)DbSet<T>());
        }
        // ...
    }
}

更新: EFキャストの問題を解決するためのより良い(より簡単な)方法があります。サンプルのようにC#キャスト演算子を使用し、後でカスタム拡張メソッドを呼び出して、単純なExpressionVisitorを使用して不要なキャストを削除します。

public static class QueryableExtensions
{
    public static IQueryable<T> ReduceCasts<T>(this IQueryable<T> source)
    {
        var expression = new CastReducer().Visit(source.Expression);
        if (source.Expression == expression) return source;
        return source.Provider.CreateQuery<T>(expression);
    }

    class CastReducer : ExpressionVisitor
    {
        protected override Expression VisitUnary(UnaryExpression node)
        {
            if (node.NodeType == ExpressionType.Convert &&
                node.Type.IsAssignableFrom(node.Operand.Type))
            {
                // Strip the Convert
                return Visit(node.Operand);
            }
            return base.VisitUnary(node);
        }
    }
}

使用例:

if (typeof(IAbc).IsAssignableFrom(typeof(T))
{
    return DbSet<T>().Where(x => ((IAbc)x).Abc == "hey").ReduceCasts();
}

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

Related 関連記事

ホットタグ

アーカイブ