在您描述的情况下,正在操作的资源不是 Foo,而是一个事务(基于您的评论)。您针对特定操作类型 (ComeForth) 的 T (Foo) 类型的实体对长期运行的事务进行建模。
控制器接受事务 POST 请求进行处理并返回事务的表示,其中包括分配给事务的唯一标识符,该标识符可用于跟踪其进度。
客户端执行 GET 操作以使用在接受处理事务时收到的唯一标识符来检索长时间运行的事务的状态。
我选择使用 XML 序列化来进行演示,但您可以将参与事务的实体序列化为字节数组或任何有意义的东西。
示例 Web API:
/事务/{id}
- POST:创建一个新事务进行处理
- GET:检索具有指定 id 的事务以验证它是否已完成
Web API 服务模型:
[DataContract()]
public class Transaction
{
public Transaction()
{
this.Id = Guid.Empty;
}
/// <summary>
/// Gets or sets the unique identifier for this transaction.
/// </summary>
/// <value>
/// A <see cref="Guid"/> that represents the unique identifier for this transaction.
/// </value>
[DataMember()]
public Guid Id
{
get;
set;
}
/// <summary>
/// Gets or sets a value indicating if this transaction has been completed.
/// </summary>
/// <value>
/// <see langword="true"/> if this transaction has been completed; otherwise, <see langword="false"/>.
/// </value>
[DataMember()]
public bool IsComplete
{
get;
set;
}
/// <summary>
/// Gets or sets the action being performed.
/// </summary>
/// <value>The action being performed.</value>
[DataMember()]
public string Action
{
get;
set;
}
/// <summary>
/// Gets or sets the serialized representation of the entity participating in the transaction.
/// </summary>
/// <value>The serialized representation of the entity participating in the transaction.</value>
[DataMember()]
public string Entity
{
get;
set;
}
/// <summary>
/// Gets or sets the assembly qualified name of the entity participating in the transaction.
/// </summary>
/// <value>
/// The <see cref="Type.AssemblyQualifiedName"/> of the <see cref="Entity"/>.
/// </value>
[DataMember()]
public string EntityType
{
get;
set;
}
/// <summary>
/// Returns the <see cref="Entity"/> as a type of <typeparamref name="T"/>.
/// </summary>
/// <typeparam name="T">The type to project the <see cref="Entity"/> as.</typeparam>
/// <returns>
/// An object of type <typeparamref name="T"/> that represents the <see cref="Entity"/>.
/// </returns>
public T As<T>() where T : class
{
T result = default(T);
var serializer = new XmlSerializer(typeof(T));
using (var reader = XmlReader.Create(new MemoryStream(Encoding.UTF8.GetBytes(this.Entity))))
{
result = serializer.Deserialize(reader) as T;
}
return result;
}
/// <summary>
/// Serializes the specified <paramref name="entity"/>.
/// </summary>
/// <typeparam name="T">The type of entity being serialized.</typeparam>
/// <param name="entity">The entity to serialize.</param>
public static Transaction From<T>(T entity, string action = null) where T : class
{
var transaction = new Transaction();
transaction.EntityType = typeof(T).AssemblyQualifiedName;
transaction.Action = action;
var serializer = new XmlSerializer(typeof(T));
byte[] data = null;
using (var stream = new MemoryStream())
{
serializer.Serialize(stream, entity);
stream.Flush();
data = stream.ToArray();
}
transaction.Entity = Encoding.UTF8.GetString(data);
return transaction;
}
}
[DataContract()]
public class Foo
{
public Foo()
{
}
[DataMember()]
public string PropertyA
{
get;
set;
}
[DataMember()]
public int PropertyB
{
get;
set;
}
[DataMember()]
public Foo PropertyC
{
get;
set;
}
}
交易控制器:
public class TransactionsController : ApiController
{
public TransactionsController() : base()
{
}
private static ConcurrentDictionary<Guid, Transaction> _transactions = new ConcurrentDictionary<Guid, Transaction>();
/// <summary>
/// Using to initiate the processing of a transaction
/// </summary>
/// <param name="transaction"></param>
/// <returns></returns>
[HttpPost()]
public HttpResponseMessage Post(Transaction transaction)
{
if(transaction == null)
{
return this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, new HttpError("Unable to model bind request."));
}
transaction.Id = Guid.NewGuid();
// Execute asynchronous long running transaction here using the model.
_transactions.TryAdd(transaction.Id, transaction);
// Return response indicating request has been accepted fro processing
return this.Request.CreateResponse<Transaction>(HttpStatusCode.Accepted, transaction);
}
/// <summary>
/// Used to retrieve status of a pending transaction.
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpGet()]
public HttpResponseMessage Get(Guid id)
{
Transaction transaction = null;
if(!_transactions.TryGetValue(id, out transaction))
{
return this.Request.CreateErrorResponse(HttpStatusCode.NotFound, new HttpError("Transaction does not exist"));
}
return this.Request.CreateResponse<Transaction>(HttpStatusCode.OK, transaction);
}
}
对事务控制器的示例客户端调用:
var foo = new Foo()
{
PropertyA = "ABC",
PropertyB = 123,
PropertyC = new Foo()
{
PropertyA = "DEF",
PropertyB = 456
}
};
var transaction = Transaction.From<Foo>(foo, "ComeForth");
Guid pendingTransactionId = Guid.Empty;
// Initiate a transaction
using(var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:12775/api/", UriKind.Absolute);
using (var response = client.PostAsJsonAsync<Transaction>("transactions", transaction).Result)
{
response.EnsureSuccessStatusCode();
pendingTransactionId = response.Content.ReadAsAsync<Transaction>().Result.Id;
}
}
// Retrieve status of transaction
Transaction pendingTransaction = null;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:12775/api/", UriKind.Absolute);
var requestUri = String.Format(null, "transactions\\{0}", pendingTransactionId.ToString());
using (var response = client.GetAsync(requestUri).Result)
{
response.EnsureSuccessStatusCode();
pendingTransaction = response.Content.ReadAsAsync<Transaction>().Result;
}
}
// Check if transaction has completed
if(pendingTransaction.IsComplete)
{
}
因此,您仍然可以使用 REST 和 ASP.NET Web API 来对长时间运行的进程的启动进行建模,您只需要将要执行的操作表示为它自己的单独资源。希望这对您的开发工作有所帮助。