简体   繁体   English

如何确保WCF服务不会与数据库并行通信

[英]How to ensure that the WCF service will not communicate to the database in parallel

I have a WCF service, which should return objects from database, but each entity should be returned only once. 我有一个WCF服务,该服务应该从数据库返回对象,但是每个实体只能返回一次。 I would like to avoid scenario, where many clients are using service, and they can get same Request entity. 我想避免出现这样的情况:许多客户端正在使用服务,并且他们可以获得相同的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;
    }
}

I am really wondering if there is a reason to give additional security like locking database. 我真的很想知道是否有理由给予额外的安全性,例如锁定数据库。 To do this I have managed something like this: 为此,我已经管理了以下内容:

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;
    }
}

I couldn't reproduce scenerio, when two request entity are returned to two different clients. 当两个请求实体返回给两个不同的客户端时,我无法重现Scenerio。 Does that mean, that WCF service performs requests sequentially? 这是否意味着WCF服务按顺序执行请求?

Instead of implementing a locking mechanism by yourself, one possible solution would be running the service as a singleton and not allowing parallel requests. 除了可能自己实现锁定机制之外,一种可能的解决方案是将服务作为单例运行,并且不允许并行请求。

You can achieve this by setting your WCF Service properties InstanceContextMode and ConcurrencyMode to Single . 您可以通过将WCF服务属性InstanceContextModeConcurrencyMode设置Single来实现

For more information about Sessions, Instancing, and Concurrency see here . 有关会话,实例化和并发的更多信息,请参见此处

You should be able to use concurrency checking to make sure that only one entity is returned, without blocking the WCF to one query at a time. 您应该能够使用并发检查来确保仅返回一个实体,而不会一次阻止WCF一次查询。

You need a special field in you entity class with an attribute [Timestamp] and then catch the DbUpdateConcurrencyException when saving, which will let you know that someone else has already returned that record, so you should get another one. 您在实体类中需要一个具有[Timestamp]属性的特殊字段,然后在DbUpdateConcurrencyExceptioncatch 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)
            {
            }
        }
    }
}

See here for more details 详情请看这里

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM