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