强制转换 IQueryable到 IQueryable

发布于 2024-12-26 04:21:49 字数 515 浏览 0 评论 0原文

我们尝试将 IQueryable 实例转换为 IQueryableSpecificEntityObject 类型仅在运行时已知。

我们尝试使用下面的代码,但该代码无法编译,因为类型或命名空间“objType”不存在。

var t = query.ElementType;
Type objType = typeof(IQueryable<>).MakeGenericType(t);
var typed = query.Cast<IEnumerable<objType>>();


var grouped = typed.GroupByMany(groupBy.Select(grp => grp.Expression).ToArray());

有什么想法吗?

We are trying to cast an instance of IQueryable<EntityObject> to an IQueryable<SpecificEntityObject>, the SpecificEntityObject type is only known at runtime.

We have tried using the code below, which does not compile because The type or namespace 'objType' does not exist.

var t = query.ElementType;
Type objType = typeof(IQueryable<>).MakeGenericType(t);
var typed = query.Cast<IEnumerable<objType>>();


var grouped = typed.GroupByMany(groupBy.Select(grp => grp.Expression).ToArray());

Any ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

陌上青苔 2025-01-02 04:21:49

使用以下 IQueryable 扩展通用方法 query.ToDTO();

public static class QueryableExtensions
{
    public static IQueryable<TDest> ToDTO<TSource, TDest>(this IQueryable<TSource> source)
    {
        List<TDest> destinationList = new List<TDest>();
        List<TSource> sourceList = source.ToList<TSource>();

        var sourceType = typeof(TSource);
        var destType = typeof(TDest);
        foreach (TSource sourceElement in sourceList)
        {
            TDest destElement = Activator.CreateInstance<TDest>();
            //Get all properties from the object 
            PropertyInfo[] sourceProperties = typeof(TSource).GetProperties();
            foreach (PropertyInfo sourceProperty in sourceProperties)
            {
                //and assign value to each propery according to property name.
                PropertyInfo destProperty = destType.GetProperty(sourceProperty.Name);
                destProperty.SetValue(destElement, sourceProperty.GetValue(sourceElement, null), null);
            }
            destinationList.Add(destElement);
        }

        return destinationList.AsQueryable();
    }
}

Use following IQueryable extension generic method query.ToDTO<sourceType,DestType>();:

public static class QueryableExtensions
{
    public static IQueryable<TDest> ToDTO<TSource, TDest>(this IQueryable<TSource> source)
    {
        List<TDest> destinationList = new List<TDest>();
        List<TSource> sourceList = source.ToList<TSource>();

        var sourceType = typeof(TSource);
        var destType = typeof(TDest);
        foreach (TSource sourceElement in sourceList)
        {
            TDest destElement = Activator.CreateInstance<TDest>();
            //Get all properties from the object 
            PropertyInfo[] sourceProperties = typeof(TSource).GetProperties();
            foreach (PropertyInfo sourceProperty in sourceProperties)
            {
                //and assign value to each propery according to property name.
                PropertyInfo destProperty = destType.GetProperty(sourceProperty.Name);
                destProperty.SetValue(destElement, sourceProperty.GetValue(sourceElement, null), null);
            }
            destinationList.Add(destElement);
        }

        return destinationList.AsQueryable();
    }
}
热风软妹 2025-01-02 04:21:49

对于任何其他想要从数据库查询投影非数据库值的人,this< u/Luis Aguilar 的 /a> 项目对我非常非常有帮助。

我有一个非常大的旧数据库 (450GB),需要将其提供给 OData/WebAPI。

OData 要求意味着我在将源数据返回给用户之前无法(过多)过滤源数据。我们可以将其隔离,但除此之外,这是他们可以按照自己的意愿查询的数据。

然而,更重要的是,遗留数据过于复杂,无法按原样公开,并且需要大量的业务逻辑来整理必要的数据(导航属性/外键的包括、冗长的子句谓词、 ETC)。

这意味着分页和结果限制在查询已经具体化之前才可用。

此类事情的正常快捷方式涉及涉及物化/渴望加载的各种策略。然而,由于数据集的大小和缺乏过滤,这将导致大量的进程内存膨胀和内存不足崩溃。

所以,一些代码。这是我的配置调用,类似于 AutoMapper 或 OData 的要求:

using ExpressionFramework.Projections;
using ExpressionFramework.Projections.Configuration;

public class ProjectionModelBuilder : ProjectionModel
{
    protected override void OnModelCreating(ProjectionModelBuilder modelBuilder)
    {
        ClientDTO.ProjectionModel(modelBuilder);
        OrderDTO.ProjectionModel(modelBuilder);
        AnotherDTO.ProjectionModel(modelBuilder);
    }
}

这种设计允许我将投影规则与其余业务逻辑一起保留在 DTO 类中。 DTO 级代码如下所示:

public static void ProjectionModel(ProjectionModelBuilder modelBuilder)
{
    modelBuilder
        .Projection<ClientDTO>()
        .ForSource<Client>(configuration =>
        {
            configuration.Property(dto => dto.Name).ExtractFrom(entity => entity.Name);
            // etc
        });
}

其中 Client 是我的实体/EDM 类型,映射到数据库表和无数外键。

然后,为了获得翻译/投影的Queryable,就是这样:

IClientQueryService service = _ioc.Resolve<IClientQueryService>(); // Repository pattern 
var q = service.GetClients(); // withManyNavigationIncludes
var r = q.Where<Item>(
    i =>
        i.Name != null
        && i.Name != ""
        // lather rinse repeat, with many sub-objects navigated also
    ).AsQueryable();
var projectionModel = new ProjectionModelBuilder();
var s = projectionModel.Project<ClientDTO, Client>(r).AsQueryable();

只有最后两行是相关的,但包含了上下文的其余部分。

我要做的最后一件事是在 Luis 代码中的 ProjectionSourceTypeConfiguration.cs 的构造函数中设置 this.IsAutoConfigured = false; ;这允许我手动订购投影定义,以便父类中的导航属性可以成功配置其投影。

我对 https://stackoverflow.com/users/543712/luis-aguilar 他的工作感激不尽。在使用各种通用方法调用、转换和树形遍历编写了我自己的 LINQ Provider/ExpressionVisitor 之后,仍然存在各种问题,他的项目是天赐之物。

如果您确实发现出于性能或其他原因必须管道化自己的表达式处理,我建议这些 首先有两个答案。

For anyone else wanting to to project non-db values from a db query, this project from u/Luis Aguilar was very, very helpful to me.

I had a very large legacy database (450GB) which was required to be served to OData/WebAPI.

The OData requirement meant I could not filter the source data (much) before returning it to the user. We could silo it, but apart from that it is their data to query as they wish.

More importantly, however, the legacy data was far too convoluted to expose as-is, and there was significant business logic required to collate the necessary data (Include of navigation properties/foreign keys, lengthy clause predicates, etc).

This meant the pagination and result limiting would not be available until after the query was already materialized.

Normal shortcuts for this kind of thing involve various strategies that involve materialization/eager loading. However, due to the size of the dataset and lack of filtering, this would result in massive process memory bloat and out-of-memory crashes.

So, some code. Here's my config call, similar to what AutoMapper or OData require:

using ExpressionFramework.Projections;
using ExpressionFramework.Projections.Configuration;

public class ProjectionModelBuilder : ProjectionModel
{
    protected override void OnModelCreating(ProjectionModelBuilder modelBuilder)
    {
        ClientDTO.ProjectionModel(modelBuilder);
        OrderDTO.ProjectionModel(modelBuilder);
        AnotherDTO.ProjectionModel(modelBuilder);
    }
}

This design allows me to keep the projection rules in the DTO class with the rest of the business logic. Here's what the DTO-level code looks like:

public static void ProjectionModel(ProjectionModelBuilder modelBuilder)
{
    modelBuilder
        .Projection<ClientDTO>()
        .ForSource<Client>(configuration =>
        {
            configuration.Property(dto => dto.Name).ExtractFrom(entity => entity.Name);
            // etc
        });
}

Where Client is my Entity/EDM type, mapped to db table and a gazillion foreign keys.

To then get a translated/projected Queryable, this is it:

IClientQueryService service = _ioc.Resolve<IClientQueryService>(); // Repository pattern 
var q = service.GetClients(); // withManyNavigationIncludes
var r = q.Where<Item>(
    i =>
        i.Name != null
        && i.Name != ""
        // lather rinse repeat, with many sub-objects navigated also
    ).AsQueryable();
var projectionModel = new ProjectionModelBuilder();
var s = projectionModel.Project<ClientDTO, Client>(r).AsQueryable();

Only the last two lines are relevant, but included the rest for context.

The last thing I had to do was set this.IsAutoConfigured = false; in the constructor for ProjectionSourceTypeConfiguration.cs in Luis' code; this allowed me to order my projection definitions manually so navigation properties inside parent classes would configure their projections successfully.

I can't thank https://stackoverflow.com/users/543712/luis-aguilar enough for his work. After writing my own LINQ Provider/ExpressionVisitor with various generic method invocations, translations and treewalks to still have various problems, his project was a godsend.

If you do find to have to pipeline your own expression processing for performance or other reasons, I'd recommend these two answers to begin with.

凉风有信 2025-01-02 04:21:49

如果您没有编译时类型信息,则必须一直依赖丑陋的反射代码。 dynamic 关键字可能会让事情变得更整洁。尝试类似的方法:

var typed = (IQueryable)typeof(Queryable)
    .GetMethod(nameof(Queryable.Cast))
    .MakeGenericMethod(typeof(SpecificEntityObject)) // <--- your runtime type here
    .Invoke(null, new object[] { query });

// more reflection based calls to follow for further LINQ operations.

还有一个不错的小扩展方法:

public static IQueryable Cast(this IQueryable source, Type type)

在 System.Linq.Dynamic.Core 库中。

If you don't have compile time type info, you have to rely on ugly reflection code all the way down. dynamic keyword might make things a bit tidy. Try something like:

var typed = (IQueryable)typeof(Queryable)
    .GetMethod(nameof(Queryable.Cast))
    .MakeGenericMethod(typeof(SpecificEntityObject)) // <--- your runtime type here
    .Invoke(null, new object[] { query });

// more reflection based calls to follow for further LINQ operations.

There is also nice little extension method:

public static IQueryable Cast(this IQueryable source, Type type)

in System.Linq.Dynamic.Core library.

如痴如狂 2025-01-02 04:21:49
var t = query.ElementType;
Type objType = typeof(IQueryable<>).MakeGenericType(t);
var typed = query.Cast<object>();


var grouped = typed.GroupByMany(groupBy.Select(grp => grp.Expression).ToArray());
var t = query.ElementType;
Type objType = typeof(IQueryable<>).MakeGenericType(t);
var typed = query.Cast<object>();


var grouped = typed.GroupByMany(groupBy.Select(grp => grp.Expression).ToArray());
或十年 2025-01-02 04:21:49

如果您开始使用反射,那么您也需要将它与所有方法一起使用。 需要在运行时创建

var myEnumType = typeof(IEnumerable<>).MakeGenericType(objType);

因此,您还

 typeof(Enumerable).GetMethod("Cast", BindingFlags.Public |
                BindingFlags.Static, 
                null, 
                CallingConventions.Any,  
                new Type[] {typeof(object)}, 
                null);

并查找扩展方法 Cast 匹配所需的类型。那么你就可以调用该方法

If you started using reflection, you need to use it with all methods too. So you need to create

var myEnumType = typeof(IEnumerable<>).MakeGenericType(objType);

and also find extention method Cast matching needed type also at runtime.

 typeof(Enumerable).GetMethod("Cast", BindingFlags.Public |
                BindingFlags.Static, 
                null, 
                CallingConventions.Any,  
                new Type[] {typeof(object)}, 
                null);

then you would be able to call that method

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文