Форум программистов, компьютерный форум, киберфорум
Наши страницы
C#: Базы данных, ADO.NET
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.73/15: Рейтинг темы: голосов - 15, средняя оценка - 4.73
Casper-SC
Эксперт .NET
3618 / 1827 / 355
Регистрация: 27.03.2010
Сообщений: 5,136
Записей в блоге: 1
1

Entity Framework. Исключение при обращении к контексту сразу из двух потоков

26.01.2015, 15:56. Просмотров 2766. Ответов 1
Метки нет (Все метки)

Исключение типа "System.Data.Entity.Core.EntityCommandExecutionException" возникло в EntityFramework.SqlServer.dll, но не было обработано в коде пользователя

Дополнительные сведения: An error occurred while executing the command definition. See the inner exception for details.
Открыл Inner Exception:
{"ExecuteReader требует, чтобы команда имела транзакцию, если подключение, назначенное команде, находится в отложенной локальной транзакции Свойство Transaction для команды не инициализировано."}
В чём суть проблемы. Есть клиент серверное приложение, использующее Entity Framework 6.0.0.0.

Сервер поднимает 2 сервиса:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    class Program
    {
        static void Main(string[] args)
        {
            ServiceHost host1 = new ServiceHost(typeof(AuthService));
            var authBinding = new NetTcpBinding(SecurityMode.None);
            host1.AddServiceEndpoint(typeof(IAuthService), authBinding, Consts.AuthAddress);
            host1.Open();
 
            ServiceHost host2 = new ServiceHost(typeof(ClientService));
            var clientBinding = new NetTcpBinding(SecurityMode.None);
            host2.AddServiceEndpoint(typeof(IClientService), clientBinding, Consts.ClientsAddress);
            host2.Open();
 
            Console.WriteLine("Сервисы запущены");
 
            Console.ReadKey();
        }
    }
AuthResult
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using System.Runtime.Serialization;
using XXX.API.Data;
 
namespace XXX.API.Communication.Auth
{
    /// <summary>
    /// Результат авторизации, содержит в себе пользователя и маркер сессии
    /// </summary>
    [DataContract]
    public class AuthResult
    {
        public AuthResult()
        {
        }
 
        public AuthResult(User user, string token)
        {
            User = user;
            Token = token;
        }
 
        [DataMember]
        public User User { get; set; }
 
        [DataMember]
        public string Token { get; set; }
    }
}


IAuthService, IClientService
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
    [ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IAuthServiceCallback))]
    public interface IAuthService
    {
        [OperationContract(IsInitiating = true, IsTerminating = false)]
        AuthResult Login(AuthData authData);
 
        [OperationContract(IsInitiating = false, IsTerminating = true)]
        void Logout();
 
        /// <summary>
        /// Проверить соединение
        /// </summary>
        /// <returns></returns>
        [OperationContract]
        bool CheckConnection();
 
        [OperationContract]
        bool RegisterUser(string login, string password);
 
        /// <summary>
        /// Установить уровень доступа для пользователя. Изменить права может только администратор, в противном случае права изменены не будут.
        /// </summary>
        /// <param name="login">Логин/имя пользователя</param>
        /// <param name="permission">Новый уровень доступа</param>
        /// <returns></returns>
        [OperationContract]
        bool SetPermissionLevel(string login, Permission permission);
    }
 
    [ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IClientServiceCallback))]
    public interface IClientService
    {
        [OperationContract]
        bool Add(Client client);
 
        [OperationContract]
        Client[] GetAll();
 
        [OperationContract]
        Client GetById(int id);
 
        [OperationContract]
        bool Update(Client client);
 
        [OperationContract]
        bool Connect(int userId, string token);
    }


Далее я на клиенте (программа) добавляю в БД 2 клиента (компании-заказчики):
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
using ConsApp_Client.Communication.Callbacks;
using System;
using System.ServiceModel;
using System.Threading;
using System.Threading.Tasks;
using Utils.Security;
using XXX.API.Communication.Auth;
using XXX.API.Communication.Services;
using XXX.API.Data;
 
namespace ConsApp_Client
{
    class Program
    {
        static SynchronizationContext _syncContext = new SynchronizationContext();
        private IAuthService _authService;
        private IClientService _clientService;
 
        static void Main(string[] args)
        {
            Instance = new Program();
            Instance.Run();
        }
 
        public static Program Instance { get; private set; }
 
        /// <summary>Запустить работу программы</summary>
        private void Run()
        {
            _authService = InitAuthService();
            _clientService = InitClientService();
 
            Console.Write("Проверка соединения: ");
            Console.WriteLine(_authService.CheckConnection());
 
            AuthData auth = new AuthData() { Login = "CasperSC", Password = HashHelper.ComputeMd5Hash("123456") };
            //bool addSuccess = _authService.RegisterUser(auth.Login, auth.Password);
            //Console.WriteLine(addSuccess ? auth.Login + " успешно добавлен" : "Добавление не удалось");
 
            Console.WriteLine("Авторизация...");
            AuthResult result = _authService.Login(auth);
            Console.Write("Результат авторизации: ");
            if (result != null)
                Console.WriteLine("Id: {0}, Имя: {1}, Права доступа: {2}.", result.User.Id, result.User.Name, result.User.Permission);
            else
                Console.WriteLine("не удалась");
 
            const string userName = "Defiler";
            bool permsSetResult = _authService.SetPermissionLevel(userName, Permission.User);
            Console.WriteLine("Смена прав для пользователя \"{0}\" {1}.", userName,
                permsSetResult ? "успешно завершена" : "не удалась");
 
            if (result != null)
            {
                Console.WriteLine();
                Console.Write("Подключение нового сервиса: ");
 
                //IClientServiceCallback clientCallback = 
 
                bool clientConnectResult = _clientService.Connect(result.User.Id, result.Token);
                if (clientConnectResult)
                {
                    _clientService.Add(new Client("Лукойл", "Не указан"));
                    _clientService.Add(new Client("Роял Фэктори", "Не указан"));
                }
 
                Console.WriteLine("{0}", clientConnectResult ? "Успешное подключение" : "Не удалось");
            }
 
            Console.ReadKey();
 
            _authService.Logout();
        }
 
        private static IAuthService InitAuthService()
        {
            EndpointAddress endpoint = new EndpointAddress(new Uri(Consts.AuthAddress));
            NetTcpBinding binding = new NetTcpBinding(SecurityMode.None);
            IAuthService service = DuplexChannelFactory<IAuthService>.CreateChannel(
                new AuthServiceCallback(Program.Instance, _syncContext), binding, endpoint);
#if DEBUG
            binding.ReceiveTimeout = TimeSpan.FromMinutes(5);
            binding.SendTimeout = TimeSpan.FromMinutes(5);
#endif
            return service;
        }
 
        private static IClientService InitClientService()
        {
            EndpointAddress endpoint = new EndpointAddress(new Uri(Consts.ClientsAddress));
            NetTcpBinding binding = new NetTcpBinding(SecurityMode.None);
            IClientService service = DuplexChannelFactory<IClientService>.CreateChannel(
                new ClientServiceCallback(Program.Instance, _syncContext), binding, endpoint);
#if DEBUG
            binding.ReceiveTimeout = TimeSpan.FromMinutes(5);
            binding.SendTimeout = TimeSpan.FromMinutes(5);
#endif
            return service;
        }
 
        public void AddClient(int id)
        {
            Task<Client>.Factory.StartNew(() => _clientService.GetById(id))
            .ContinueWith(task =>
            {
                Client client = task.Result;
                Console.WriteLine("Добавлен клиент: Id = {0}, Name = {1}, Address = {2}", client.Id, client.Name, client.Address);
            });
        }
    }
}

В итоге при добавлении клиента срабатывает колбек и вызывает метод: AddClient в Program.cs
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System.ServiceModel;
using System.Threading;
using XXX.API.Communication.ServiceCallbacks;
 
namespace ConsApp_Client.Communication.Callbacks
{
    [ServiceContract(SessionMode = SessionMode.Required)]
    internal class ClientServiceCallback : IClientServiceCallback
    {
        private readonly Program _program;
        private readonly SynchronizationContext _context;
 
        public ClientServiceCallback(Program program, SynchronizationContext context)
        {
            _program = program;
            _context = context;
        }
 
        public void ClientAdded(int id)
        {
            //Task.Factory.StartNew(() =>
            //{
            _context.Post((unused) =>
            {
                _program.AddClient(id);
            }, null);
                
            //});
        }
    }
}
Суть в том, что доходя до получения клиента из БД вылетает исключение в методе GetClientById:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
using System.Linq;
using Db = Server.Models.Repository;
 
namespace Server.Models.Repository
{
    internal partial class Database
    {
        public bool AddClient(Db.Client dbClient, out int clientId)
        {
            var client = _dbContainer.ClientSet.Add(dbClient);
            bool success = _dbContainer.SaveChanges() > 0;
            clientId = client.Id;
            return success;
        }
 
        public Db.Client[] GetAllClients()
        {
            return _dbContainer.ClientSet.ToArray();
        }
 
        public Db.Client GetClientById(int id)
        {
            Db.Client client = _dbContainer.ClientSet.FirstOrDefault(c => c.Id == id);
            return client;
        }
 
        public bool UpdateClient(Db.Client dbClient)
        {
            Db.Client client = _dbContainer.ClientSet.FirstOrDefault(c => c.Id == dbClient.Id);
            if (client != null)
            {
                client = dbClient;
                return _dbContainer.SaveChanges() > 0;
            }
 
            return false;
        }
    }
}
Как посоветуете решать данную проблему?
0
Миниатюры
Entity Framework. Исключение при обращении к контексту сразу из двух потоков  
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
26.01.2015, 15:56
Ответы с готовыми решениями:

Исключение при попытке добавить элемент в БД (entity framework)
Пытаюсь сохранить объект в БД: public void Add(EntityModels entity) ...

Entity Framework 6.0.1. Модель Code First. Не обновляются данные, вылетает исключение
using System; using System.Collections.Generic; using System.Linq; using...

В чем разница между Entity Framework и Entity Framework Core?
В чем разница (если она есть) между entity framework и entity framework core?

Разграничение потоков при обращении к одному файлу
Здравствуйте! Помогите с такой задачей: необходимо создать программу, в которой...

Entity Framework увеличение id при добавлении
Всем привет. Использую подход Model first, создал базу. Как сделать так,...

1
Casper-SC
Эксперт .NET
3618 / 1827 / 355
Регистрация: 27.03.2010
Сообщений: 5,136
Записей в блоге: 1
26.01.2015, 16:41  [ТС] 2
Как это работает. Запускается клиент. На сервере поднимается сессия для этого клиента. То есть у каждого клиента на сервере свой объект ClientService, в нём в свою очередь свой объект Database, у которого в свою очередь свой объект
DbModelContainer
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
//------------------------------------------------------------------------------
// <auto-generated>
//     Этот код создан по шаблону.
//
//     Изменения, вносимые в этот файл вручную, могут привести к непредвиденной работе приложения.
//     Изменения, вносимые в этот файл вручную, будут перезаписаны при повторном создании кода.
// </auto-generated>
//------------------------------------------------------------------------------
 
namespace Server.Models.Repository
{
    using System;
    using System.Data.Entity;
    using System.Data.Entity.Infrastructure;
    
    public partial class DbModelContainer : DbContext
    {
        public DbModelContainer()
            : base("name=DbModelContainer")
        {
        }
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            throw new UnintentionalCodeFirstException();
        }
    
        public virtual DbSet<User> UserSet { get; set; }
        public virtual DbSet<Message> MessageSet { get; set; }
        public virtual DbSet<Client> ClientSet { get; set; }
    }
}


Я тут нагуглил, что нужно для каждого метода нужно открывать и закрывать контекст. Не слишком ли это жирная операция? Или так и нужно?

Добавлено через 1 минуту
Совет, который я нагуглил
0
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
26.01.2015, 16:41

Entity Framework. Удаление entity без удаления связей
Вечер добрый. Есть модель Coder First. Каскадное удаление запрещено. ...

Ошибка при изменении данных через Entity Framework
Столкнулся с проблемой обновления данных, сам еще новичок в Entity. Добавляет в...

Зачем закрывать подключение при работе с Entity Framework?
Зачем закрывать подключение при entity framework?На что это...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
2
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru