gpt4 book ai didi

c# - 如何将 repository 中的泛型 T 转换为接口(interface)以在 LINQ to SQL 过滤器中有条件地访问接口(interface)属性?

转载 作者:太空狗 更新时间:2023-10-30 01:31:32 24 4
gpt4 key购买 nike

我有一个通用存储库 Repository<T> where T: class由领域模型使用T .

一些类用作T有额外的重要属性,可以使用接口(interface)(例如“我有属性“abc”,IAbc 是我的关联接口(interface))。

public interface IAbc
{
string Abc { get; }
}

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

我想要实现的是通过在我的 Repository<T> 中的特定方法中进行接口(interface)转换来公开那些额外的字段并将它们用于过滤、条件决策等。

// 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
}

但是,当我尝试这样做时,出现以下异常:

Unable to cast the type 'T' to type 'IAbc'. LINQ to Entities only supports casting EDM primitive or enumeration types.

感谢您的帮助。

最佳答案

我看到的一种可能方法是在单独的非泛型类中创建静态约束泛型方法,并使用 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();
}

关于c# - 如何将 repository<T> 中的泛型 T 转换为接口(interface)以在 LINQ to SQL 过滤器中有条件地访问接口(interface)属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40954155/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com