IDbSet <t> </t>上没有FindAsync()方法

时间:2014-02-15 17:21:58

标签: c# entity-framework asp.net-identity

是否有理由从FindAsync()接口中省略IDbSet<T>方法? Find是界面的一部分,异步版本不可用似乎很奇怪。我需要强制转换为DbSet<T>才能访问它,这有点麻烦:

User user = await ((DbSet<User>)db.Users)
    .FindAsync("de7d5d4a-9d0f-48ff-9478-d240cd5eb035");

5 个答案:

答案 0 :(得分:14)

如果您拥有IDbSet<T>的消费者,我认为您这样做是因为您希望从消费者中获得FindAsync()的访问权限,那么一个简单的解决方案是创建您自己的界面,其中包括IDbSet并包含您要使用的FindAsync()方法:

public interface IAsyncDbSet<T> : IDbSet<T>
    where T : class
{
    Task<T> FindAsync(params Object[] keyValues);
}

这解决了不必转换为DbSet的问题 - 顺便说一下,它破坏了合同编码的抽象优势。但这也引入了一系列问题。

需要更多工作的更好的解决方案(imo)是定义一个接口,该接口只包含您希望使用的成员,否则将成为您的DbSet对象,实现时子类DbSet接口,然后在代码中使用该接口:

public interface IMyAsyncDbSet<TEntity>
    where TEntity : class
{
    TEntity Add(TEntity entity);
    TEntity Remove(TEntity entity);

    // Copy other methods from IDbSet<T> as needed.

    Task<Object> FindAsync(params Object[] keyValues);
}

public class MyDbSet<T> : DbSet<T>, IMyAsyncDbSet<T>
    where T : class
{
}

这是一个适配器模式,真的。它将您的代码所期望的接口与Entity Framework提供的接口分离。现在,它们是相同的 - 这就是除了继承DbSet<T>之外,实现什么都不做的原因。但后来他们可能会分歧。此时,您仍然可以使用最新的DbSet而不会破坏您的代码。

答案 1 :(得分:14)

以下是我在其中一个项目中解决这个问题的方法:

using System.Threading.Tasks;

namespace System.Data.Entity
{
    public static class IDbSetExtensions
    {
        /// <summary>
        /// If possible asynchronously finds an entity with the given primary key values 
        /// otherwise finds the entity synchronously.  
        /// If an entity with the given primary key values exists in the context, then it is
        /// returned immediately without making a request to the store. Otherwise, a
        /// request is made to the store for an entity with the given primary key values
        /// and this entity, if found, is attached to the context and returned. If no
        /// entity is found in the context or the store, then null is returned.
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="this"></param>
        /// <param name="keyValues">The values of the primary key for the entity to be found.</param>
        /// <returns>A task that represents the asynchronous find operation. The task result contains 
        /// the entity found, or null.</returns>
        /// <exception cref="System.InvalidOperationException"></exception>
        public static async Task<TEntity> FindAsync<TEntity>(this IDbSet<TEntity> @this, params object[] keyValues)
        where TEntity : class
        {
            DbSet<TEntity> thisDbSet = @this as DbSet<TEntity>;
            if (thisDbSet != null)
            {
                return await thisDbSet.FindAsync(keyValues);
            }
            else
            {
                return @this.Find(keyValues);
            }
        }
    }
}

可以考虑将Find方法包装在异步同步模式中,该模式将提供卸载(并且不像真正的异步方法那样具有可伸缩性)。但是,调用者必须知道这一点,以确保在调用可能会干扰的FindAsync方法后,他们不会在上下文中调用方法。让呼叫者意识到特定的实现并不是一个非常好的设计,因为它很容易导致问题。对于OP,IDbSet是DbSet,因此调用将是异步的。

答案 2 :(得分:3)

我相信这些天的正确方法(自EF 6起)涉及从DbSet继承而不是实现IDbSet。

答案 3 :(得分:0)

将FindAsync方法更改为FirstOrDefaultAsync(x =&gt; x.Id == yourId);

答案 4 :(得分:-5)

使用此扩展程序解决FindAsync问题

/// <summary>
/// IDbSet extension
/// </summary>
public static class IDbSetExtension
{
    public static Task<TEntity> FindAsync<TEntity>(this IDbSet<TEntity> set, params object[] keyValues) 
        where TEntity : class
    {
        return Task<TEntity>.Run(() =>
        {
            var entity = set.Find(keyValues);
            return entity;
        });
    }
}