Mik*_*ala 5 .net c# async-await
我试图用来dynamic解决由于设计或缺乏而造成的不便(如果感兴趣的是简化方法从通用存储库中检索数据,可以在这里找到"不便" ).
为了缩短它,我需要返回Entity实例的集合.类非常简单:
[JsonObject]
public class Entity
{
[PrimaryKey]
[JsonProperty(PropertyName = "id")]
public virtual int Id { get; set; }
[JsonIgnore]
public string Content { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
所以Entity只有Id和Content.继承类可能有其他属性,但我只对该Content部分感兴趣(复杂的JSON).
可以通过泛型访问各种不同的实体Repository<T>.我需要知道Type具体类,因为T通过数据提供程序映射到基础SQLite表,构建在SQLite-net ORM之上.
所以,例如,如果我有Schedule : Entity,那么我将Repository<Schedule>用于操纵名为的表Schedule.这部分工作正常.
// must be instantiated with concrete class/type inheriting
// from Entity in order to map to correct database table
public class Repository<T> where T : new()
{
public async virtual Task<IEnumerable<T>> GetAllAsync()
{
return await SQLiteDataProvider.Connection.Table<T>().ToListAsync();
}
// etc.
}
Run Code Online (Sandbox Code Playgroud)
主要问题是"命令"来自JavaScript客户端,所以我将收到JSON格式的请求.在这个JSON中,我有一个名为的属性CollectionName,它指定了所需的表(以及具体类型).
我需要/想要的是一个漂亮而干净的代码片段,可以从任何给定的表中获取实体.因此,下面的方法应该解决我所有的问题,但事实证明它没有......
public async Task<IEnumerable<Entity>> GetAllEntitiesFrom(CollectionArgs args)
{
// args.CollectionName is type of entity as string
// namespace + collection name is mapped as correct type
// e.g. MyNamespace.Schedule
Type entityType = Type.GetType(
string.Format("{0}{1}", EntityNamespacePrefix, args.CollectionName), true, true);
// get correct repository type using resolved entity type
// e.g. Repository<MyNamespace.Schedule>
Type repositoryType = typeof(Repository<>).MakeGenericType(entityType);
dynamic repository = Activator.CreateInstance(repositoryType);
// Below `GetAllAsync()` returns `Task<IEnumerable<T>>`.
// this blocking call works 100%
//var entities = repository.GetAllAsync().Result;
// this non-blocking call works when it feels like it
var entities = await repository.GetAllAsync();
return entities;
}
Run Code Online (Sandbox Code Playgroud)
因此,如果(上图)我使用阻止.Result一切工作谎言魅力.相反,如果我使用await,代码可能会或可能不会.它似乎真的取决于行星的位置和/或Flying Spaghetti Monster的情绪波动.
随机地,但往往是给定的线将投掷
无法转换类型为'System.Runtime.CompilerServices.TaskAwaiter'1 [System.Collections.Generic.IEnumerable'1 [MyNamespace.Schedule]]'的对象以键入'System.Runtime.CompilerServices.INotifyCompletion'.
我正在使用.NET 4.0 Extended Framework.
可以通过2次动态调用来实现:
public async Task<IEnumerable<Entity>> GetAllEntitiesFrom(CollectionArgs args)
{
var entityType = Type.GetType(
string.Format("{0}{1}", EntityNamespacePrefix, args.CollectionName), true, true);
var repositoryType = typeof(Repository<>).MakeGenericType(entityType);
var repository = Activator.CreateInstance(repositoryType);
var task = (Task)((dynamic)repository).GetAllAsync();
await task;
var entities = (IEnumerable<Entity>)((dynamic)task).Result;
return entities;
}
Run Code Online (Sandbox Code Playgroud)
编辑
虽然上面应该可行,但应该有更好的整体设计。不幸的是,MS 决定使用异步任务,并且由于Task<TResult>是类,我们无法从协方差中受益。然而,我们可以通过一些通用扩展的帮助来做到这一点,但代价是一些 GC 垃圾。但在我看来,它极大地简化了此类设计/实现。一探究竟:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
namespace Tests
{
// General async extensions
public interface IAwaitable<out TResult>
{
IAwaiter<TResult> GetAwaiter();
TResult Result { get; }
}
public interface IAwaiter<out TResult> : ICriticalNotifyCompletion, INotifyCompletion
{
bool IsCompleted { get; }
TResult GetResult();
}
public static class AsyncExtensions
{
public static IAwaitable<TResult> AsAwaitable<TResult>(this Task<TResult> task) { return new TaskAwaitable<TResult>(task); }
class TaskAwaitable<TResult> : IAwaitable<TResult>, IAwaiter<TResult>
{
TaskAwaiter<TResult> taskAwaiter;
public TaskAwaitable(Task<TResult> task) { taskAwaiter = task.GetAwaiter(); }
public IAwaiter<TResult> GetAwaiter() { return this; }
public bool IsCompleted { get { return taskAwaiter.IsCompleted; } }
public TResult Result { get { return taskAwaiter.GetResult(); } }
public TResult GetResult() { return taskAwaiter.GetResult(); }
public void OnCompleted(Action continuation) { taskAwaiter.OnCompleted(continuation); }
public void UnsafeOnCompleted(Action continuation) { taskAwaiter.UnsafeOnCompleted(continuation); }
}
}
// Your entity framework
public abstract class Entity
{
// ...
}
public interface IRepository<out T>
{
IAwaitable<IEnumerable<T>> GetAllAsync();
}
public class Repository<T> : IRepository<T> where T : Entity
{
public IAwaitable<IEnumerable<T>> GetAllAsync() { return GetAllAsyncCore().AsAwaitable(); }
protected async virtual Task<IEnumerable<T>> GetAllAsyncCore()
{
//return await SQLiteDataProvider.Connection.Table<T>().ToListAsync();
// Test
await Task.Delay(1000);
return await Task.FromResult(Enumerable.Empty<T>());
}
}
public static class Repository
{
public static IAwaitable<IEnumerable<Entity>> GetAllEntitiesFrom(string collectionName)
{
var entityType = Type.GetType(typeof(Entity).Namespace + "." + collectionName, true, true);
var repositoryType = typeof(Repository<>).MakeGenericType(entityType);
var repository = (IRepository<Entity>)Activator.CreateInstance(repositoryType);
return repository.GetAllAsync();
}
}
// Test
class EntityA : Entity { }
class EntityB : Entity { }
class Program
{
static void Main(string[] args)
{
var t = Test();
t.Wait();
}
static async Task Test()
{
var a = await Repository.GetAllEntitiesFrom(typeof(EntityA).Name);
var b = await Repository.GetAllEntitiesFrom(typeof(EntityB).Name);
}
}
}
Run Code Online (Sandbox Code Playgroud)