Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/entity-framework/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 你如何处理两个客户之间的竞争?_C#_Entity Framework_Concurrency - Fatal编程技术网

C# 你如何处理两个客户之间的竞争?

C# 你如何处理两个客户之间的竞争?,c#,entity-framework,concurrency,C#,Entity Framework,Concurrency,我正在编写一个简单的消息传递模块,这样一个进程可以发布消息,另一个进程可以订阅消息。我使用EF/SqlServer作为进程外通信机制。“服务器”只是发布者/订阅者对的共同名称(可以称为“通道”) 我有下面的方法,它向数据库中添加一行,表示一个命名的“服务器” 我遇到的问题是,当我同时启动两个客户机时,应该只有一个客户机添加一个新的服务器条目,但实际情况并非如此。我实际上得到了两个同名条目的非常错误的结果,并且意识到Any()保护不足以实现这一点 服务器的实体使用int PK,并且假定我的存储库将

我正在编写一个简单的消息传递模块,这样一个进程可以发布消息,另一个进程可以订阅消息。我使用EF/SqlServer作为进程外通信机制。“服务器”只是发布者/订阅者对的共同名称(可以称为“通道”)

我有下面的方法,它向数据库中添加一行,表示一个命名的“服务器”

我遇到的问题是,当我同时启动两个客户机时,应该只有一个客户机添加一个新的服务器条目,但实际情况并非如此。我实际上得到了两个同名条目的非常错误的结果,并且意识到Any()保护不足以实现这一点

服务器的实体使用int PK,并且假定我的存储库将强制名称字段的唯一性。但我开始觉得这是行不通的

public class Server
{
    public int Id { get; set; }
    public string Name { get; set; }
}
我认为我可以解决这一问题的两种方法似乎都不太理想:

  • 字符串主键
  • 忽略异常
  • 这就是并发性的问题,对吗

    在这种情况下,我希望两个客户端使用相同的名称调用存储库,但只得到数据库中一行使用该名称的结果,我该如何处理这种情况

    更新:这是存储库代码

    namespace MyBus.Data
    {
        public class Repository : IDisposable
        {
            private readonly Context context;
            private readonly bool autoSave;
    
            public delegate Chain Chain(Action<Repository> action);
            public static Chain Command(Action<Repository> action)
            {
                using (var repo = new Data.Repository(true))
                {
                    action(repo);
                }
                return new Chain(next => Command(next));
            }
    
            public Repository(bool autoSave)
            {
                this.autoSave = autoSave;
                context = new Context();
            }
    
            public void Dispose()
            {
                if (autoSave)
                    context.SaveChanges();
                context.Dispose();
            }
    
            public void AddServer(string name)
            {
                if (!context.Servers.Any(c => c.Name == name))
                {
                    context.Servers.Add(new Server { Name = name });
                }
            }
    
            public void AddClient(string name, bool isPublisher)
            {
                if (!context.Clients.Any(c => c.Name == name))
                {
                    context.Clients.Add(new Client
                    {
                        Name = name,
                        ClientType = isPublisher ? ClientType.Publisher : ClientType.Subscriber
                    });
                }
            }
    
            public void AddMessageType<T>()
            {
                var typeName = typeof(T).FullName;
                if (!context.MessageTypes.Any(c => c.Name == typeName))
                {
                    context.MessageTypes.Add(new MessageType { Name = typeName });
                }
            }
    
            public void AddRegistration<T>(string serverName, string clientName)
            {
                var server = context.Servers.Single(c => c.Name == serverName);
                var client = context.Clients.Single(c => c.Name == clientName);
                var messageType = context.MessageTypes.Single(c => c.Name == typeof(T).FullName);
                if (!context.Registrations.Any(c =>
                        c.ServerId == server.Id &&
                        c.ClientId == client.Id &&
                        c.MessageTypeId == messageType.Id))
                {
                    context.Registrations.Add(new Registration
                    {
                        Client = client,
                        Server = server,
                        MessageType = messageType
                    });
                }
            }
    
            public void AddMessage<T>(T item, out int messageId)
            {
                var messageType = context.MessageTypes.Single(c => c.Name == typeof(T).FullName);
                var serializer = new XmlSerializer(typeof(T));
                var sb = new StringBuilder();
                using (var sw = new StringWriter(sb))
                {
                    serializer.Serialize(sw, item);
                }
                var message = new Message
                {
                    MessageType = messageType,
                    Created = DateTime.UtcNow,
                    Data = sb.ToString()
                };
                context.Messages.Add(message);
                context.SaveChanges();
                messageId = message.Id;
            }
    
            public void CreateDeliveries<T>(int messageId, string serverName, string sendingClientName, T item)
            {
                var messageType = typeof(T).FullName;
    
                var query = from reg in context.Registrations
                            where reg.Server.Name == serverName &&
                                  reg.Client.ClientType == ClientType.Subscriber &&
                                  reg.MessageType.Name == messageType
                            select new
                            {
                                reg.ClientId
                            };
    
                var senderClientId = context.Clients.Single(c => c.Name == sendingClientName).Id;
    
                foreach (var reg in query)
                {
                    context.Deliveries.Add(new Delivery
                    {
                        SenderClientId = senderClientId,
                        ReceiverClientId = reg.ClientId,
                        MessageId = messageId,
                        Updated = DateTime.UtcNow,
                        DeliveryStatus = DeliveryStatus.Sent
                    });
                }
            }
    
            public List<T> GetDeliveries<T>(string serverName, string clientName, out List<int> messageIds)
            {
                messageIds = new List<int>();
                var messages = new List<T>();
                var clientId = context.Clients.Single(c => c.Name == clientName).Id;
                var query = from del in context.Deliveries
                            where del.ReceiverClientId == clientId &&
                                  del.DeliveryStatus == DeliveryStatus.Sent
                            select new
                            {
                                del.Id,
                                del.Message.Data
                            };
                foreach (var item in query)
                {
                    var serializer = new XmlSerializer(typeof(T));
                    using (var sr = new StringReader(item.Data))
                    {
                        var t = (T)serializer.Deserialize(sr);
                        messages.Add(t);
                        messageIds.Add(item.Id);
                    }
                }
                return messages;
            }
    
            public void ConfirmDelivery(int deliveryId)
            {
                using (var context = new Context())
                {
                    context.Deliveries.First(c => c.Id == deliveryId).DeliveryStatus = DeliveryStatus.Received;
                    context.SaveChanges();
                }
            }
        }
    }
    
    名称空间MyBus.Data
    {
    公共类存储库:IDisposable
    {
    私有只读上下文;
    私有只读bool autoSave;
    公共代表链(行动);
    公共静态链命令(操作)
    {
    使用(var repo=new Data.Repository(true))
    {
    行动(回购);
    }
    返回新链(next=>Command(next));
    }
    公共存储库(bool autoSave)
    {
    this.autoSave=autoSave;
    上下文=新上下文();
    }
    公共空间处置()
    {
    如果(自动保存)
    SaveChanges();
    context.Dispose();
    }
    public void AddServer(字符串名称)
    {
    如果(!context.Servers.Any(c=>c.Name==Name))
    {
    Add(新服务器{Name=Name});
    }
    }
    public void AddClient(字符串名,bool isPublisher)
    {
    如果(!context.Clients.Any(c=>c.Name==Name))
    {
    context.Clients.Add(新客户端
    {
    Name=Name,
    ClientType=isPublisher?ClientType.Publisher:ClientType.Subscriber
    });
    }
    }
    public void AddMessageType()
    {
    var typeName=typeof(T).FullName;
    if(!context.MessageTypes.Any(c=>c.Name==typeName))
    {
    context.MessageTypes.Add(新的MessageType{Name=typeName});
    }
    }
    public void AddRegistration(字符串serverName、字符串clientName)
    {
    var server=context.Servers.Single(c=>c.Name==serverName);
    var client=context.Clients.Single(c=>c.Name==clientName);
    var messageType=context.MessageTypes.Single(c=>c.Name==typeof(T.FullName);
    如果(!context.Registrations.Any)(c=>
    c、 ServerId==server.Id&&
    c、 ClientId==client.Id&&
    c、 MessageTypeId==messageType.Id))
    {
    context.Registrations.Add(新注册
    {
    客户=客户,
    服务器=服务器,
    MessageType=MessageType
    });
    }
    }
    public void AddMessage(T项,out int messageId)
    {
    var messageType=context.MessageTypes.Single(c=>c.Name==typeof(T.FullName);
    var serializer=newxmlserializer(typeof(T));
    var sb=新的StringBuilder();
    使用(var sw=新的StringWriter(sb))
    {
    序列化器。序列化(软件、项目);
    }
    var消息=新消息
    {
    MessageType=MessageType,
    Created=DateTime.UtcNow,
    Data=sb.ToString()
    };
    context.Messages.Add(消息);
    SaveChanges();
    messageId=message.Id;
    }
    public void CreateDeliveries(int messageId、string serverName、string sendingClientName、T item)
    {
    var messageType=typeof(T).FullName;
    var query=来自context.Registrations中的注册表
    其中reg.Server.Name==serverName&&
    reg.Client.ClientType==ClientType.Subscriber&&
    reg.MessageType.Name==MessageType
    选择新的
    {
    注册客户
    };
    var senderClientId=context.Clients.Single(c=>c.Name==sendingClientName).Id;
    foreach(查询中的var reg)
    {
    context.Deliveries.Add(新交付)
    {
    SenderClientId=SenderClientId,
    ReceiverClientId=reg.ClientId,
    MessageId=MessageId,
    更新=DateTime.UtcNow,
    DeliveryStatus=DeliveryStatus.Sent
    });
    }
    }
    public List GetDeliveries(字符串serverName、字符串clientName、out List messageid)
    {
    messageIds=新列表();
    var messages=新列表();
    var clientId=context.Clients.Single(c=>c.Name==clientName).Id;
    var query=来自context.Deliveries中的del
    其中del.ReceiverClientId==clientId&&
    del.DeliveryStatus==DeliveryStatus.Sent
    
    namespace MyBus.Data
    {
        public class Repository : IDisposable
        {
            private readonly Context context;
            private readonly bool autoSave;
    
            public delegate Chain Chain(Action<Repository> action);
            public static Chain Command(Action<Repository> action)
            {
                using (var repo = new Data.Repository(true))
                {
                    action(repo);
                }
                return new Chain(next => Command(next));
            }
    
            public Repository(bool autoSave)
            {
                this.autoSave = autoSave;
                context = new Context();
            }
    
            public void Dispose()
            {
                if (autoSave)
                    context.SaveChanges();
                context.Dispose();
            }
    
            public void AddServer(string name)
            {
                if (!context.Servers.Any(c => c.Name == name))
                {
                    context.Servers.Add(new Server { Name = name });
                }
            }
    
            public void AddClient(string name, bool isPublisher)
            {
                if (!context.Clients.Any(c => c.Name == name))
                {
                    context.Clients.Add(new Client
                    {
                        Name = name,
                        ClientType = isPublisher ? ClientType.Publisher : ClientType.Subscriber
                    });
                }
            }
    
            public void AddMessageType<T>()
            {
                var typeName = typeof(T).FullName;
                if (!context.MessageTypes.Any(c => c.Name == typeName))
                {
                    context.MessageTypes.Add(new MessageType { Name = typeName });
                }
            }
    
            public void AddRegistration<T>(string serverName, string clientName)
            {
                var server = context.Servers.Single(c => c.Name == serverName);
                var client = context.Clients.Single(c => c.Name == clientName);
                var messageType = context.MessageTypes.Single(c => c.Name == typeof(T).FullName);
                if (!context.Registrations.Any(c =>
                        c.ServerId == server.Id &&
                        c.ClientId == client.Id &&
                        c.MessageTypeId == messageType.Id))
                {
                    context.Registrations.Add(new Registration
                    {
                        Client = client,
                        Server = server,
                        MessageType = messageType
                    });
                }
            }
    
            public void AddMessage<T>(T item, out int messageId)
            {
                var messageType = context.MessageTypes.Single(c => c.Name == typeof(T).FullName);
                var serializer = new XmlSerializer(typeof(T));
                var sb = new StringBuilder();
                using (var sw = new StringWriter(sb))
                {
                    serializer.Serialize(sw, item);
                }
                var message = new Message
                {
                    MessageType = messageType,
                    Created = DateTime.UtcNow,
                    Data = sb.ToString()
                };
                context.Messages.Add(message);
                context.SaveChanges();
                messageId = message.Id;
            }
    
            public void CreateDeliveries<T>(int messageId, string serverName, string sendingClientName, T item)
            {
                var messageType = typeof(T).FullName;
    
                var query = from reg in context.Registrations
                            where reg.Server.Name == serverName &&
                                  reg.Client.ClientType == ClientType.Subscriber &&
                                  reg.MessageType.Name == messageType
                            select new
                            {
                                reg.ClientId
                            };
    
                var senderClientId = context.Clients.Single(c => c.Name == sendingClientName).Id;
    
                foreach (var reg in query)
                {
                    context.Deliveries.Add(new Delivery
                    {
                        SenderClientId = senderClientId,
                        ReceiverClientId = reg.ClientId,
                        MessageId = messageId,
                        Updated = DateTime.UtcNow,
                        DeliveryStatus = DeliveryStatus.Sent
                    });
                }
            }
    
            public List<T> GetDeliveries<T>(string serverName, string clientName, out List<int> messageIds)
            {
                messageIds = new List<int>();
                var messages = new List<T>();
                var clientId = context.Clients.Single(c => c.Name == clientName).Id;
                var query = from del in context.Deliveries
                            where del.ReceiverClientId == clientId &&
                                  del.DeliveryStatus == DeliveryStatus.Sent
                            select new
                            {
                                del.Id,
                                del.Message.Data
                            };
                foreach (var item in query)
                {
                    var serializer = new XmlSerializer(typeof(T));
                    using (var sr = new StringReader(item.Data))
                    {
                        var t = (T)serializer.Deserialize(sr);
                        messages.Add(t);
                        messageIds.Add(item.Id);
                    }
                }
                return messages;
            }
    
            public void ConfirmDelivery(int deliveryId)
            {
                using (var context = new Context())
                {
                    context.Deliveries.First(c => c.Id == deliveryId).DeliveryStatus = DeliveryStatus.Received;
                    context.SaveChanges();
                }
            }
        }
    }
    
        public void AddServer(string name)
        {
            if (!context.Servers.Any(c => c.Name == name))
            {
                context.Database.ExecuteSqlCommand(@"MERGE Servers WITH (HOLDLOCK) AS T
                                                     USING (SELECT {0} AS Name) AS S
                                                     ON T.Name = S.Name
                                                     WHEN NOT MATCHED THEN 
                                                     INSERT (Name) VALUES ({0});", name);
            }
        }
    
        public class Context : DbContext
        {
            public DbSet<MessageType> MessageTypes { get; set; }
            public DbSet<Message> Messages { get; set; }
            public DbSet<Delivery> Deliveries { get; set; }
            public DbSet<Client> Clients { get; set; }
            public DbSet<Server> Servers { get; set; }
            public DbSet<Registration> Registrations { get; set; }
    
            public class Initializer : IDatabaseInitializer<Context>
            {
                public void InitializeDatabase(Context context)
                {
                    if (context.Database.Exists() && !context.Database.CompatibleWithModel(false))
                        context.Database.Delete();
    
                    if (!context.Database.Exists())
                    {
                        context.Database.Create();
                        context.Database.ExecuteSqlCommand(
                           @"alter table Servers 
                             add constraint UniqueServerName unique (Name)");
                    }
                }
            }
        }
    
    readonly List<Func<Exception, bool>> ExceptionsIgnoredOnSave = 
        new List<Func<Exception, bool>>();
    
    static readonly Func<Exception, bool> UniqueConstraintViolation =
        e => e.AnyMessageContains("Violation of UNIQUE KEY constraint");
    
    public static class Ext
    {
        public static bool AnyMessageContains(this Exception ex, string text)
        {
            while (ex != null)
            {
                if(ex.Message.Contains(text))
                    return true;
                ex = ex.InnerException;
            }
            return false;
        }
    }
    
        public void Dispose()
        {
            if (autoSave)
            {
                try
                {
                    context.SaveChanges();
                }
                catch (Exception ex)
                {      
                    if(!ExceptionsIgnoredOnSave.Any(pass => pass(ex)))
                        throw;
                    Console.WriteLine("ignoring exception..."); // temp
                }
            }
            context.Dispose();
        }
    
        public void AddServer(string name)
        {
            ExceptionsIgnoredOnSave.Add(UniqueConstraintViolation);
    
            if (!context.Servers.Any(c => c.Name == name))
            {
                var server = context.Servers.Add(new Server { Name = name });
            }
        }