我有一个WCF
服务,该服务应从数据库返回对象,但每个实体仅应返回一次。我想避免出现这样的情况:许多客户端正在使用服务,并且他们可以获得相同的Request
实体。
public Request GetChangeRequest()
{
using (var context = new Data.Core.Context())
{
var request = context.Requests
.Where(r => r.IsAvaible)
.FirstOrDefault();
if (request != null)
{
request.IsAvaible = false;
context.SaveChanges();
}
return request;
}
}
我真的很想知道是否有理由给予额外的安全性,例如锁定数据库。为此,我已经管理了以下内容:
public Request GetChangeRequest()
{
using (var context = new Data.Core.Context())
{
context.OnLock<Request>(context.GetTableName<Request>(), () =>
{
var request = context.Requests
.Where(r => r.IsAvaible)
.FirstOrDefault();
if (request != null)
{
request.IsAvaible = false;
context.SaveChanges();
}
return request;
});
}
}
public static class DBContextExtensions
{
public static string GetTableName<T>(this DbContext context) where T : class
{
var type = typeof(T);
var entityName = (context as System.Data.Entity.Infrastructure.IObjectContextAdapter).ObjectContext.CreateObjectSet<T>().EntitySet.Name;
var tableAttribute = type.GetCustomAttributes(false).OfType<System.ComponentModel.DataAnnotations.Schema.TableAttribute>().FirstOrDefault();
return tableAttribute == null ? entityName : tableAttribute.Name;
}
public static T OnLock<T>(this DbContext context, string tableName, Func<T> action)
{
T res;
using (DbContextTransaction scope = context.Database.BeginTransaction())
{
context.Database.ExecuteSqlCommand($"SELECT TOP 1 Id FROM {tableName} WITH (TABLOCKX, HOLDLOCK)");
res = action.Invoke();
scope.Commit();
}
return res;
}
}
当两个请求实体返回给两个不同的客户端时,我无法重现Scenerio。这是否意味着WCF
服务按顺序执行请求?
答案 0 :(得分:3)
代替您自己实现锁定机制,一种可能的解决方案是将服务作为单例运行,并且不允许并行请求。
您可以通过将WCF服务属性 InstanceContextMode 和 ConcurrencyMode 设置为 Single 来实现。
有关会话,实例化和并发性的更多信息,请参见here。
答案 1 :(得分:0)
您应该能够使用并发检查来确保仅返回一个实体,而不会一次阻止WCF一次查询。
您在实体类中需要一个特殊的字段,该字段的属性为[Timestamp]
,然后在保存时为catch
DbUpdateConcurrencyException
,这将使您知道其他人已经返回了该记录,所以你应该再买一个。
public class Request
{
...
[Timestamp]
public byte[] RowVersion { get; set; }
}
public Request GetChangeRequest()
{
using (var context = new Data.Core.Context())
{
while (true)
{
try
{
var request = context.Requests
.Where(r => r.IsAvaible)
.FirstOrDefault();
request.IsAvaible = false;
context.SaveChanges();
return request;
}
catch (DbUpdateConcurrencyException)
{
}
}
}
}
有关更多详细信息,请参见here