gpt4 book ai didi

c# - EF6 : Use reference/lookup data with IQueryable

转载 作者:行者123 更新时间:2023-11-30 16:05:18 25 4
gpt4 key购买 nike

我想在查询中使用列表中的预加载查找数据。我需要查询作为 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;
}

上面的代码会抛出一个错误,因为 EF/LINQ 无法将内存列表形成 SQL(原因很明显)- 除非添加/更改某些内容。

我想以某种方式 告诉 EF 在 SQL 之后应用查找或类似的东西。我读过有关使用 EF Helper 代码和表达式做类似事情的文章,但我找不到那篇文章了。

请注意,我对查找本身很灵活。唯一不可协商的是“contact.StatusId”(整数),但结构“_statusTypes.FirstOrDefault(l => contact.StatusId == l.Id)”的其余部分与 List 类型一样是开放的。

感谢任何帮助。

最佳答案

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

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

  1. 标记Status实体中的属性为非映射(使用 Ignore() 和 Fluent API 或属性上的 [NotMapped] 属性)。

  2. InterceptingQueryable<T> (让我们这样命名)实现IOrderedQueryable<T> ,它包装了一个 IQueryable<T>来自 EF 的对象(在您的示例中由 Select 方法返回)。

  3. InterceptingQueryProvider<T>实现IQueryProvider<T> ,它又包装了从 EF 获得的查询提供程序。

  4. InterceptingEnumerator<T>实现IEnumerator<T> ,它中继到 EF 返回的枚举器对象。 此枚举器将在执行 MoveNext 后立即注入(inject) Status 属性的值(可以很容易地概括为以这种方式填充任何查找属性)。 ,因此 Current 返回的对象已完全填充。

上面的链是这样连接的:

  1. InterceptingQueryable传递到构造函数中传递给 EF 的查询对象。

  2. InterceptingQueryable.Provider属性(property)返回InterceptingQueryProvider .

  3. InterceptingQueryable.GetEnumerator方法返回 InterceptingEnumerator .

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

  5. InterceptingQueryProvider.Execute调用 Execute在 EF 查询提供程序上,然后在它获得受查找注入(inject)影响的实体的情况下,它以与 InterceptingEnumerator 相同的方式注入(inject)查找值确实(提取方法以供重用)。

更新

代码如下:

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);
}
}
}

这是测试用例/示例。首先,我们不应该忘记将非映射的 Status 属性添加到 Contact 实体:

public partial class Contact
{
[NotMapped]
public StatusType Status { get; set; }
}

然后,我们可以使用拦截器机制如下:

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);
}

打印:

--- SOME CONTACTS ---
1: John Smith, Acme Corp, 3=Status Three
3: Jon Snow, The Wall, 2=Status Two

查询被翻译成:

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

关于c# - EF6 : Use reference/lookup data with IQueryable,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33852039/

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