あなたが説明している場合、操作対象のリソースは 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 を引き続き使用して、実行時間の長いプロセスの開始をモデル化できます。必要なのは、実行する操作を独自の個別のリソースとして表すことだけです。これが開発作業に役立つことを願っています。