Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# EF6:将引用/查找数据与IQueryable一起使用_C#_Linq_Entity Framework 6_Lookup_Iqueryable - Fatal编程技术网

C# EF6:将引用/查找数据与IQueryable一起使用

C# EF6:将引用/查找数据与IQueryable一起使用,c#,linq,entity-framework-6,lookup,iqueryable,C#,Linq,Entity Framework 6,Lookup,Iqueryable,我想使用查询中列表中预加载的查找数据。我需要查询作为IQueryable返回,因为它在网格中使用&正在分页(此处不包括)。我需要从查找中加载标签以避免连接(实际代码中有更多) 我知道我可以做一个ToList()并对其进行后期处理,但我需要IQueryable。代码如下: // Run in intialization other code... var contactLookups = new ContactsDbContext(); List<StatusType> _status

我想使用查询中列表中预加载的查找数据。我需要查询作为IQueryable返回,因为它在网格中使用&正在分页(此处不包括)。我需要从查找中加载标签以避免连接(实际代码中有更多)

我知道我可以做一个ToList()并对其进行后期处理,但我需要IQueryable。代码如下:

// Run in intialization other code...
var contactLookups = new ContactsDbContext();
List<StatusType> _statusTypes = contactLookups.StatusTypes.ToList();


    public IQueryable GetQuery()
    {
        var contactsCtx = new ContactsDbContext();
        IQueryable query = contactsCtx.Select(contact => new
        {
            Id = contact.Id,
            FullName = contact.FirstName + " " + contact.LastName,
            CompanyName = contact.CompanyName,
            Status = _statusTypes.FirstOrDefault(l => contact.StatusId == l.Id).Label
        });
        return query;
    }
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace Examples.Queryables
{
    public class InterceptingQueryable<T> : IOrderedQueryable<T>
    {
        private readonly Action<T> _interceptor;
        private readonly IQueryable<T> _underlyingQuery;
        private InterceptingQueryProvider _provider;

        public InterceptingQueryable(Action<T> interceptor, IQueryable<T> underlyingQuery)
        {
            _interceptor = interceptor;
            _underlyingQuery = underlyingQuery;
            _provider = null;
        }
        public IEnumerator<T> GetEnumerator()
        {
            return new InterceptingEnumerator(_interceptor, _underlyingQuery.GetEnumerator());
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
        public Expression Expression 
        {
            get { return _underlyingQuery.Expression; }
        }
        public Type ElementType 
        {
            get { return _underlyingQuery.ElementType; }
        }
        public IQueryProvider Provider 
        {
            get
            {
                if ( _provider == null )
                {
                    _provider = new InterceptingQueryProvider(_interceptor, _underlyingQuery.Provider); 
                }
                return _provider;
            }
        }

        private class InterceptingQueryProvider : IQueryProvider
        {
            private readonly Action<T> _interceptor;
            private readonly IQueryProvider _underlyingQueryProvider;

            public InterceptingQueryProvider(Action<T> interceptor, IQueryProvider underlyingQueryProvider)
            {
                _interceptor = interceptor;
                _underlyingQueryProvider = underlyingQueryProvider;
            }
            public IQueryable CreateQuery(Expression expression)
            {
                throw new NotImplementedException();
            }
            public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
            {
                var query = _underlyingQueryProvider.CreateQuery<TElement>(expression);

                if ( typeof(T).IsAssignableFrom(typeof(TElement)) )
                {
                    return new InterceptingQueryable<TElement>((Action<TElement>)(object)_interceptor, query);
                }
                else
                {
                    return query;
                }
            }
            public object Execute(Expression expression)
            {
                throw new NotImplementedException();
            }
            public TResult Execute<TResult>(Expression expression)
            {
                var result = _underlyingQueryProvider.Execute<TResult>(expression);

                if ( result is T )
                {
                    _interceptor((T)(object)result);
                }

                return result;
            }
        }

        private class InterceptingEnumerator : IEnumerator<T>
        {
            private readonly Action<T> _interceptor;
            private readonly IEnumerator<T> _underlyingEnumerator;
            private bool _hasCurrent;

            public InterceptingEnumerator(Action<T> interceptor, IEnumerator<T> underlyingEnumerator)
            {
                _interceptor = interceptor;
                _underlyingEnumerator = underlyingEnumerator;
                _hasCurrent = false;
            }
            public void Dispose()
            {
                _underlyingEnumerator.Dispose();
            }
            public bool MoveNext()
            {
                _hasCurrent = _underlyingEnumerator.MoveNext();

                if ( _hasCurrent )
                {
                    _interceptor(_underlyingEnumerator.Current);
                }

                return _hasCurrent;
            }
            public void Reset()
            {
                _underlyingEnumerator.Reset();
            }
            public T Current 
            {
                get
                {
                    return _underlyingEnumerator.Current;
                }
            }
            object IEnumerator.Current
            {
                get { return Current; }
            }
        }
    }

    public static class QueryableExtensions
    {
        public static IOrderedQueryable<T> InterceptWith<T>(this IQueryable<T> query, Action<T> interceptor)
        {
            return new InterceptingQueryable<T>(interceptor, query);
        }
    }
}
//在初始化其他代码中运行。。。
var contactLookups=newcontactsDBContext();
列表_statusTypes=contactLookups.statusTypes.ToList();
公共IQueryable GetQuery()
{
var contactsCtx=新的ContactsDbContext();
IQueryable query=contactsCtx.Select(contact=>new
{
Id=contact.Id,
全名=contact.FirstName+“”+contact.LastName,
CompanyName=contact.CompanyName,
Status=\u statusTypes.FirstOrDefault(l=>contact.StatusId==l.Id).Label
});
返回查询;
}
上面的代码抛出了一个错误,因为EF/LINQ无法将内存中的列表形成SQL(原因很明显),除非添加/更改某些内容

我想以某种方式告诉EF在SQL或其他类似的东西之后应用查找。我曾经读过关于使用EF帮助程序代码和表达式执行类似操作的文章,但是我再也找不到这篇文章了

注意,我对查找本身很灵活。唯一不可协商的是“contact.StatusId”(int),但与列表类型一样,“u statusTypes.FirstOrDefault(l=>contact.StatusId==l.Id)”结构的其余部分是打开的


非常感谢您的帮助。

您可以将EF的查询包装到自己的IQueryable拦截实现中,在该实现中,您可以在将对象返回到应用程序之前注入内存中查找的值

这听起来可能很复杂,但实际上并不难实现。需要做以下工作:

  • 将实体中的
    Status
    属性标记为未映射(使用Fluent API的
    Ignore()
    或属性上的
    [NotMapped]
    属性)

  • 编写
    InterceptingQueryable
    (让我们这样命名)实现
    IOrderedQueryable
    ,它包装来自EF的
    IQueryable
    对象(由示例中的Select方法返回)

  • 编写
    InterceptingQueryProvider
    实现
    IQueryProvider
    ,它反过来包装从EF获得的查询提供程序

  • 编写
    interceptingemulator
    实现
    IEnumerator
    ,它将中继到EF返回的枚举器对象此枚举数将在执行
    MoveNext
    之后立即注入Status属性的值(可以很容易地用这种方式填充任何查找属性),以便完全填充
    Current
    返回的对象。

  • 上述链条的连接方式如下:

    var contactLookups = contactsCtx.StatusTypes.ToList();
    
    Action<Contact> interceptor = contact => {
        contact.Status = contactLookups.FirstOrDefault(l => contact.StatusId == l.Id);
    };
    
    // note that we add InterceptWith(...) to entity set
    var someContacts = 
        from c in contactsCtx.Contacts.InterceptWith(interceptor) 
        where c.FullName.StartsWith("Jo")
        orderby c.FullName, c.CompanyName
        select c;
    
    Console.WriteLine("--- SOME CONTACTS ---");
    foreach ( var c in someContacts )
    {
        Console.WriteLine(
            "{0}: {1}, {2}, {3}={4}", 
            c.Id, c.FullName, c.CompanyName, c.StatusId, c.Status.Name);
    }
    
  • InterceptingQueryable
    中继到EF的查询对象,传递给构造函数

  • InterceptingQueryable.Provider
    属性返回
    InterceptingQueryProvider

  • InterceptingQueryable.GetEnumerator
    方法返回
    InterceptingEnumerator

  • InterceptingQueryProvider.CreateQuery
    方法从EF查询提供程序获取查询对象,然后将其包装在另一个
    InterceptingQueryable
    实例中返回

  • InterceptingQueryProvider.Execute
    调用EF查询提供程序上的
    Execute
    ,然后在它获取要进行查找注入的实体时,它以与
    InterceptingEnumerator
    相同的方式注入查找值(提取一个方法以供重用)

  • 更新

    代码如下:

    // Run in intialization other code...
    var contactLookups = new ContactsDbContext();
    List<StatusType> _statusTypes = contactLookups.StatusTypes.ToList();
    
    
        public IQueryable GetQuery()
        {
            var contactsCtx = new ContactsDbContext();
            IQueryable query = contactsCtx.Select(contact => new
            {
                Id = contact.Id,
                FullName = contact.FirstName + " " + contact.LastName,
                CompanyName = contact.CompanyName,
                Status = _statusTypes.FirstOrDefault(l => contact.StatusId == l.Id).Label
            });
            return query;
        }
    
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Examples.Queryables
    {
        public class InterceptingQueryable<T> : IOrderedQueryable<T>
        {
            private readonly Action<T> _interceptor;
            private readonly IQueryable<T> _underlyingQuery;
            private InterceptingQueryProvider _provider;
    
            public InterceptingQueryable(Action<T> interceptor, IQueryable<T> underlyingQuery)
            {
                _interceptor = interceptor;
                _underlyingQuery = underlyingQuery;
                _provider = null;
            }
            public IEnumerator<T> GetEnumerator()
            {
                return new InterceptingEnumerator(_interceptor, _underlyingQuery.GetEnumerator());
            }
            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
            public Expression Expression 
            {
                get { return _underlyingQuery.Expression; }
            }
            public Type ElementType 
            {
                get { return _underlyingQuery.ElementType; }
            }
            public IQueryProvider Provider 
            {
                get
                {
                    if ( _provider == null )
                    {
                        _provider = new InterceptingQueryProvider(_interceptor, _underlyingQuery.Provider); 
                    }
                    return _provider;
                }
            }
    
            private class InterceptingQueryProvider : IQueryProvider
            {
                private readonly Action<T> _interceptor;
                private readonly IQueryProvider _underlyingQueryProvider;
    
                public InterceptingQueryProvider(Action<T> interceptor, IQueryProvider underlyingQueryProvider)
                {
                    _interceptor = interceptor;
                    _underlyingQueryProvider = underlyingQueryProvider;
                }
                public IQueryable CreateQuery(Expression expression)
                {
                    throw new NotImplementedException();
                }
                public IQueryable<TElement> CreateQuery<TElement>(Expression expression)
                {
                    var query = _underlyingQueryProvider.CreateQuery<TElement>(expression);
    
                    if ( typeof(T).IsAssignableFrom(typeof(TElement)) )
                    {
                        return new InterceptingQueryable<TElement>((Action<TElement>)(object)_interceptor, query);
                    }
                    else
                    {
                        return query;
                    }
                }
                public object Execute(Expression expression)
                {
                    throw new NotImplementedException();
                }
                public TResult Execute<TResult>(Expression expression)
                {
                    var result = _underlyingQueryProvider.Execute<TResult>(expression);
    
                    if ( result is T )
                    {
                        _interceptor((T)(object)result);
                    }
    
                    return result;
                }
            }
    
            private class InterceptingEnumerator : IEnumerator<T>
            {
                private readonly Action<T> _interceptor;
                private readonly IEnumerator<T> _underlyingEnumerator;
                private bool _hasCurrent;
    
                public InterceptingEnumerator(Action<T> interceptor, IEnumerator<T> underlyingEnumerator)
                {
                    _interceptor = interceptor;
                    _underlyingEnumerator = underlyingEnumerator;
                    _hasCurrent = false;
                }
                public void Dispose()
                {
                    _underlyingEnumerator.Dispose();
                }
                public bool MoveNext()
                {
                    _hasCurrent = _underlyingEnumerator.MoveNext();
    
                    if ( _hasCurrent )
                    {
                        _interceptor(_underlyingEnumerator.Current);
                    }
    
                    return _hasCurrent;
                }
                public void Reset()
                {
                    _underlyingEnumerator.Reset();
                }
                public T Current 
                {
                    get
                    {
                        return _underlyingEnumerator.Current;
                    }
                }
                object IEnumerator.Current
                {
                    get { return Current; }
                }
            }
        }
    
        public static class QueryableExtensions
        {
            public static IOrderedQueryable<T> InterceptWith<T>(this IQueryable<T> query, Action<T> interceptor)
            {
                return new InterceptingQueryable<T>(interceptor, query);
            }
        }
    }
    
    然后,我们可以使用拦截器机制,如下所示:

    var contactLookups = contactsCtx.StatusTypes.ToList();
    
    Action<Contact> interceptor = contact => {
        contact.Status = contactLookups.FirstOrDefault(l => contact.StatusId == l.Id);
    };
    
    // note that we add InterceptWith(...) to entity set
    var someContacts = 
        from c in contactsCtx.Contacts.InterceptWith(interceptor) 
        where c.FullName.StartsWith("Jo")
        orderby c.FullName, c.CompanyName
        select c;
    
    Console.WriteLine("--- SOME CONTACTS ---");
    foreach ( var c in someContacts )
    {
        Console.WriteLine(
            "{0}: {1}, {2}, {3}={4}", 
            c.Id, c.FullName, c.CompanyName, c.StatusId, c.Status.Name);
    }
    
    查询将转换为:

    SELECT 
        [Extent1].[Id] AS [Id], 
        [Extent1].[FullName] AS [FullName], 
        [Extent1].[CompanyName] AS [CompanyName], 
        [Extent1].[StatusId] AS [StatusId]
        FROM [dbo].[Contacts] AS [Extent1]
        WHERE [Extent1].[FullName] LIKE 'Jo%'
        ORDER BY [Extent1].[FullName] ASC, [Extent1].[CompanyName] ASC
    

    与无法在数据库端处理整个查询的明显缺点相比,我不确定避免连接的好处是什么,但您所要求的可以通过使用Linq对实体进行尽可能多的操作(过滤、排序、分组、投影)来实现,然后将其转换为
    IEnumerable
    ,并使用Linq To对象执行其余操作。您始终可以使用
    Enumerable.AsQueryable
    IEnumerable
    上切换到
    IQueryable
    实现。像这样的

    public IQueryable GetQuery()
    {
        var db = new ContactsDbContext();
        var query = db.Contacts.Select(contact => new
        {
            Id = contact.Id,
            FullName = contact.FirstName + " " + contact.LastName,
            CompanyName = contact.CompanyName,
            StatusId = contact.StatusId
        })
        .AsEnumerable()
        .Select(contact => new
        {
            Id = contact.Id,
            FullName = contact.FullName,
            CompanyName = contact.CompanyName,
            Status = _statusTypes.FirstOrDefault(l => contact.StatusId == l.Id).Label
        })
        .AsQueryable();
        return query;
    }
    

    您能否读取contact.StatusId并稍后手动填写状态?否,查询将作为方法调用的结果绑定到网格。另外,这种类型的调用只是以上查询为例的众多调用中的一种。非常好,谢谢。如果你能举个例子就好了。我想这和我回忆的很接近。正是我想要的,谢谢你的努力!谢谢,我明天就试试。不确定它是否会破坏分页,但值得一试,因为这是一个简单的解决方案。此外,在“为什么”上,查找表完全加载到第二层缓存中,因此再次加入以检索它们是浪费和性能损失。考虑到我在这个查询中有更多的查询,为了便于理解,这个例子被简化了。我理解你得到了什么,这很好,但是试图用这种方法来警告你会失去什么。如果我的解释不清楚,上面的查询是有效的,但是
    AsEnumerable
    之后的所有事情都会发生在内存中,包括分页或任何进一步的处理。我尝试了这个方法&在第一次查询结束时,它作为.ToList()有效地运行,这是我一直避免的。谢谢你的努力。@SteveKinyon我没有努力,我想我警告过你两次了。您想要
    IQueryable
    ,并且得到了
    IQueryable
    :-)如果您想要一个
    IQueryable
    ,对其应用进一步的过滤等,以及在db中发生的所有事情,那么您应该以这种方式对其进行表述。祝你好运