Как убедиться, что служба WCF не будет взаимодействовать с базой данных параллельно - PullRequest
1 голос
/ 14 мая 2019

У меня есть сервис 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 сервис выполняет запросы последовательно?

Ответы [ 2 ]

3 голосов
/ 14 мая 2019

Вместо того, чтобы реализовывать механизм блокировки самостоятельно, одним из возможных решений будет запуск службы в виде одноэлементного режима и запрет на параллельные запросы.

Этого можно добиться, установив свойства службы WCF InstanceContextMode и ConcurrencyMode до Single .

Для получения дополнительной информации о сеансах, экземплярах и параллелизме см. здесь .

0 голосов
/ 14 мая 2019

Вы должны иметь возможность использовать проверку параллелизма, чтобы убедиться, что возвращается только одна сущность, не блокируя 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)
            {
            }
        }
    }
}

См. здесь для более подробной информации

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...