diff --git a/src/Umbraco.Core/CoreBootManager.cs b/src/Umbraco.Core/CoreBootManager.cs
index 4aeee2881d..294ad69a87 100644
--- a/src/Umbraco.Core/CoreBootManager.cs
+++ b/src/Umbraco.Core/CoreBootManager.cs
@@ -320,7 +320,7 @@ namespace Umbraco.Core
//supplying a username/password, this will automatically disable distributed calls
// .. we'll override this in the WebBootManager
ServerMessengerResolver.Current = new ServerMessengerResolver(
- new DefaultServerMessenger());
+ new WebServiceServerMessenger());
MappingResolver.Current = new MappingResolver(
ServiceProvider, LoggerResolver.Current.Logger,
diff --git a/src/Umbraco.Core/Logging/AppDomainTokenFormatter.cs b/src/Umbraco.Core/Logging/AppDomainTokenFormatter.cs
new file mode 100644
index 0000000000..0abddc63e3
--- /dev/null
+++ b/src/Umbraco.Core/Logging/AppDomainTokenFormatter.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Web;
+
+namespace Umbraco.Core.Logging
+{
+ ///
+ /// Allows for outputting a normalized appdomainappid token in a log format
+ ///
+ public sealed class AppDomainTokenConverter : log4net.Util.PatternConverter
+ {
+ protected override void Convert(TextWriter writer, object state)
+ {
+ writer.Write(HttpRuntime.AppDomainAppId.ReplaceNonAlphanumericChars(string.Empty));
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Models/Rdbms/CacheInstructionDto.cs b/src/Umbraco.Core/Models/Rdbms/CacheInstructionDto.cs
new file mode 100644
index 0000000000..c24004b7dc
--- /dev/null
+++ b/src/Umbraco.Core/Models/Rdbms/CacheInstructionDto.cs
@@ -0,0 +1,31 @@
+using System;
+using Umbraco.Core.Persistence;
+using Umbraco.Core.Persistence.DatabaseAnnotations;
+
+namespace Umbraco.Core.Models.Rdbms
+{
+ [TableName("umbracoCacheInstruction")]
+ [PrimaryKey("id")]
+ [ExplicitColumns]
+ internal class CacheInstructionDto
+ {
+ [Column("id")]
+ [NullSetting(NullSetting = NullSettings.NotNull)]
+ [PrimaryKeyColumn(AutoIncrement = true, Name = "PK_umbracoCacheInstruction")]
+ public int Id { get; set; }
+
+ [Column("utcStamp")]
+ [NullSetting(NullSetting = NullSettings.NotNull)]
+ public DateTime UtcStamp { get; set; }
+
+ [Column("jsonInstruction")]
+ [SpecialDbType(SpecialDbTypes.NTEXT)]
+ [NullSetting(NullSetting = NullSettings.NotNull)]
+ public string Instructions { get; set; }
+
+ [Column("originated")]
+ [NullSetting(NullSetting = NullSettings.NotNull)]
+ [Length(500)]
+ public string OriginIdentity { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Models/Rdbms/ServerRegistrationDto.cs b/src/Umbraco.Core/Models/Rdbms/ServerRegistrationDto.cs
index 9833d9ab74..b7bdf265ce 100644
--- a/src/Umbraco.Core/Models/Rdbms/ServerRegistrationDto.cs
+++ b/src/Umbraco.Core/Models/Rdbms/ServerRegistrationDto.cs
@@ -15,22 +15,19 @@ namespace Umbraco.Core.Models.Rdbms
[Column("address")]
[Length(500)]
- public string Address { get; set; }
+ public string ServerAddress { get; set; }
- ///
- /// A unique column in the database, a computer name must always be unique!
- ///
[Column("computerName")]
[Length(255)]
- [Index(IndexTypes.UniqueNonClustered, Name = "IX_computerName")]
- public string ComputerName { get; set; }
+ [Index(IndexTypes.UniqueNonClustered, Name = "IX_computerName")] // server identity is unique
+ public string ServerIdentity { get; set; }
[Column("registeredDate")]
[Constraint(Default = "getdate()")]
public DateTime DateRegistered { get; set; }
[Column("lastNotifiedDate")]
- public DateTime LastNotified { get; set; }
+ public DateTime DateAccessed { get; set; }
[Column("isActive")]
[Index(IndexTypes.NonClustered)]
diff --git a/src/Umbraco.Core/Models/ServerRegistration.cs b/src/Umbraco.Core/Models/ServerRegistration.cs
index 7f43f5dfd2..900d6deb94 100644
--- a/src/Umbraco.Core/Models/ServerRegistration.cs
+++ b/src/Umbraco.Core/Models/ServerRegistration.cs
@@ -2,61 +2,67 @@
using System.Globalization;
using System.Reflection;
using Umbraco.Core.Models.EntityBase;
-using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.Sync;
namespace Umbraco.Core.Models
{
- internal class ServerRegistration : Entity, IServerAddress, IAggregateRoot
+ ///
+ /// Represents a registered server in a multiple-servers environment.
+ ///
+ public class ServerRegistration : Entity, IServerAddress, IAggregateRoot
{
private string _serverAddress;
- private string _computerName;
+ private string _serverIdentity;
private bool _isActive;
private static readonly PropertyInfo ServerAddressSelector = ExpressionHelper.GetPropertyInfo(x => x.ServerAddress);
- private static readonly PropertyInfo ComputerNameSelector = ExpressionHelper.GetPropertyInfo(x => x.ComputerName);
+ private static readonly PropertyInfo ServerIdentitySelector = ExpressionHelper.GetPropertyInfo(x => x.ServerIdentity);
private static readonly PropertyInfo IsActiveSelector = ExpressionHelper.GetPropertyInfo(x => x.IsActive);
+ ///
+ /// Initialiazes a new instance of the class.
+ ///
public ServerRegistration()
- {
-
- }
+ { }
///
- /// Creates an item with pre-filled properties
+ /// Initialiazes a new instance of the class.
///
- ///
- ///
- ///
- ///
- ///
- ///
- public ServerRegistration(int id, string serverAddress, string computerName, DateTime createDate, DateTime updateDate, bool isActive)
+ /// The unique id of the server registration.
+ /// The server url.
+ /// The unique server identity.
+ /// The date and time the registration was created.
+ /// The date and time the registration was last accessed.
+ /// A value indicating whether the registration is active.
+ public ServerRegistration(int id, string serverAddress, string serverIdentity, DateTime registered, DateTime accessed, bool isActive)
{
- UpdateDate = updateDate;
- CreateDate = createDate;
- Key = Id.ToString(CultureInfo.InvariantCulture).EncodeAsGuid();
+ UpdateDate = accessed;
+ CreateDate = registered;
+ Key = id.ToString(CultureInfo.InvariantCulture).EncodeAsGuid();
Id = id;
ServerAddress = serverAddress;
- ComputerName = computerName;
+ ServerIdentity = serverIdentity;
IsActive = isActive;
}
///
- /// Creates a new instance for persisting a new item
+ /// Initialiazes a new instance of the class.
///
- ///
- ///
- ///
- public ServerRegistration(string serverAddress, string computerName, DateTime createDate)
+ /// The server url.
+ /// The unique server identity.
+ /// The date and time the registration was created.
+ public ServerRegistration(string serverAddress, string serverIdentity, DateTime registered)
{
- CreateDate = createDate;
- UpdateDate = createDate;
+ CreateDate = registered;
+ UpdateDate = registered;
Key = 0.ToString(CultureInfo.InvariantCulture).EncodeAsGuid();
ServerAddress = serverAddress;
- ComputerName = computerName;
+ ServerIdentity = serverIdentity;
}
+ ///
+ /// Gets or sets the server url.
+ ///
public string ServerAddress
{
get { return _serverAddress; }
@@ -70,19 +76,25 @@ namespace Umbraco.Core.Models
}
}
- public string ComputerName
+ ///
+ /// Gets or sets the server unique identity.
+ ///
+ public string ServerIdentity
{
- get { return _computerName; }
+ get { return _serverIdentity; }
set
{
SetPropertyValueAndDetectChanges(o =>
{
- _computerName = value;
- return _computerName;
- }, _computerName, ComputerNameSelector);
+ _serverIdentity = value;
+ return _serverIdentity;
+ }, _serverIdentity, ServerIdentitySelector);
}
}
+ ///
+ /// Gets or sets a value indicating whether the server is active.
+ ///
public bool IsActive
{
get { return _isActive; }
@@ -96,9 +108,23 @@ namespace Umbraco.Core.Models
}
}
+ ///
+ /// Gets the date and time the registration was created.
+ ///
+ public DateTime Registered { get { return CreateDate; } set { CreateDate = value; }}
+
+ ///
+ /// Gets the date and time the registration was last accessed.
+ ///
+ public DateTime Accessed { get { return UpdateDate; } set { UpdateDate = value; }}
+
+ ///
+ /// Converts the value of this instance to its equivalent string representation.
+ ///
+ ///
public override string ToString()
{
- return "(" + ServerAddress + ", " + ComputerName + ", IsActive = " + IsActive + ")";
+ return string.Format("{{\"{0}\", \"{1}\", {2}active}}", ServerAddress, ServerIdentity, IsActive ? "" : "!");
}
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Persistence/Factories/ServerRegistrationFactory.cs b/src/Umbraco.Core/Persistence/Factories/ServerRegistrationFactory.cs
index e13b24e1e1..aa0ed25ccd 100644
--- a/src/Umbraco.Core/Persistence/Factories/ServerRegistrationFactory.cs
+++ b/src/Umbraco.Core/Persistence/Factories/ServerRegistrationFactory.cs
@@ -1,5 +1,4 @@
-using System.Globalization;
-using Umbraco.Core.Models;
+using Umbraco.Core.Models;
using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Factories
@@ -10,7 +9,7 @@ namespace Umbraco.Core.Persistence.Factories
public ServerRegistration BuildEntity(ServerRegistrationDto dto)
{
- var model = new ServerRegistration(dto.Id, dto.Address, dto.ComputerName, dto.DateRegistered, dto.LastNotified, dto.IsActive);
+ var model = new ServerRegistration(dto.Id, dto.ServerAddress, dto.ServerIdentity, dto.DateRegistered, dto.DateAccessed, dto.IsActive);
//on initial construction we don't want to have dirty properties tracked
// http://issues.umbraco.org/issue/U4-1946
model.ResetDirtyProperties(false);
@@ -19,16 +18,17 @@ namespace Umbraco.Core.Persistence.Factories
public ServerRegistrationDto BuildDto(ServerRegistration entity)
{
- var dto = new ServerRegistrationDto()
+ var dto = new ServerRegistrationDto
{
- Address = entity.ServerAddress,
+ ServerAddress = entity.ServerAddress,
DateRegistered = entity.CreateDate,
IsActive = entity.IsActive,
- LastNotified = entity.UpdateDate,
- ComputerName = entity.ComputerName
+ DateAccessed = entity.UpdateDate,
+ ServerIdentity = entity.ServerIdentity
};
+
if (entity.HasIdentity)
- dto.Id = int.Parse(entity.Id.ToString(CultureInfo.InvariantCulture));
+ dto.Id = entity.Id;
return dto;
}
diff --git a/src/Umbraco.Core/Persistence/Mappers/ServerRegistrationMapper.cs b/src/Umbraco.Core/Persistence/Mappers/ServerRegistrationMapper.cs
index 24b0632f8a..40a18adc59 100644
--- a/src/Umbraco.Core/Persistence/Mappers/ServerRegistrationMapper.cs
+++ b/src/Umbraco.Core/Persistence/Mappers/ServerRegistrationMapper.cs
@@ -7,7 +7,7 @@ using Umbraco.Core.Models.Rdbms;
namespace Umbraco.Core.Persistence.Mappers
{
[MapperFor(typeof(ServerRegistration))]
- public sealed class ServerRegistrationMapper : BaseMapper
+ internal sealed class ServerRegistrationMapper : BaseMapper
{
private static readonly ConcurrentDictionary PropertyInfoCacheInstance = new ConcurrentDictionary();
@@ -29,10 +29,10 @@ namespace Umbraco.Core.Persistence.Mappers
{
CacheMap(src => src.Id, dto => dto.Id);
CacheMap(src => src.IsActive, dto => dto.IsActive);
- CacheMap(src => src.ServerAddress, dto => dto.Address);
+ CacheMap(src => src.ServerAddress, dto => dto.ServerAddress);
CacheMap(src => src.CreateDate, dto => dto.DateRegistered);
- CacheMap(src => src.UpdateDate, dto => dto.LastNotified);
- CacheMap(src => src.ComputerName, dto => dto.ComputerName);
+ CacheMap(src => src.UpdateDate, dto => dto.DateAccessed);
+ CacheMap(src => src.ServerIdentity, dto => dto.ServerIdentity);
}
#endregion
diff --git a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs
index b77e0843ea..76689948c5 100644
--- a/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs
+++ b/src/Umbraco.Core/Persistence/Migrations/Initial/DatabaseSchemaCreation.cs
@@ -79,9 +79,9 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
{38, typeof (User2NodeNotifyDto)},
{39, typeof (User2NodePermissionDto)},
{40, typeof (ServerRegistrationDto)},
-
{41, typeof (AccessDto)},
- {42, typeof (AccessRuleDto)}
+ {42, typeof (AccessRuleDto)},
+ {43, typeof(CacheInstructionDto)}
};
#endregion
diff --git a/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CreateCacheInstructionTable.cs b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CreateCacheInstructionTable.cs
new file mode 100644
index 0000000000..66391f9fe5
--- /dev/null
+++ b/src/Umbraco.Core/Persistence/Migrations/Upgrades/TargetVersionSevenThreeZero/CreateCacheInstructionTable.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Umbraco.Core.Configuration;
+using Umbraco.Core.Persistence.DatabaseAnnotations;
+using Umbraco.Core.Persistence.SqlSyntax;
+
+namespace Umbraco.Core.Persistence.Migrations.Upgrades.TargetVersionSevenThreeZero
+{
+ [Migration("7.3.0", 1, GlobalSettings.UmbracoMigrationName)]
+ public class CreateCacheInstructionTable : MigrationBase
+ {
+ public override void Up()
+ {
+ var textType = SqlSyntaxContext.SqlSyntaxProvider.GetSpecialDbType(SpecialDbTypes.NTEXT);
+
+ Create.Table("umbracoCacheInstruction")
+ .WithColumn("id").AsInt32().Identity().NotNullable()
+ .WithColumn("utcStamp").AsDateTime().NotNullable()
+ .WithColumn("jsonInstruction").AsCustom(textType).NotNullable();
+
+ Create.PrimaryKey("PK_umbracoCacheInstruction")
+ .OnTable("umbracoCacheInstruction")
+ .Column("id");
+ }
+
+ public override void Down()
+ {
+ Delete.PrimaryKey("PK_umbracoCacheInstruction").FromTable("cmsContentType2ContentType");
+ Delete.Table("cmsContentType2ContentType");
+ }
+ }
+}
diff --git a/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs b/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs
index 020ae2e9cf..1ef87357ea 100644
--- a/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs
+++ b/src/Umbraco.Core/Persistence/Repositories/ServerRegistrationRepository.cs
@@ -125,5 +125,12 @@ namespace Umbraco.Core.Persistence.Repositories
entity.ResetDirtyProperties();
}
+ public void DeactiveStaleServers(TimeSpan staleTimeout)
+ {
+ var timeoutDate = DateTime.UtcNow.Subtract(staleTimeout);
+
+ Database.Update("SET isActive=0 WHERE lastNotifiedDate < @timeoutDate", new {timeoutDate = timeoutDate});
+ }
+
}
}
\ No newline at end of file
diff --git a/src/Umbraco.Core/Services/ServerRegistrationService.cs b/src/Umbraco.Core/Services/ServerRegistrationService.cs
index f52c05768f..157e7b795d 100644
--- a/src/Umbraco.Core/Services/ServerRegistrationService.cs
+++ b/src/Umbraco.Core/Services/ServerRegistrationService.cs
@@ -11,63 +11,66 @@ namespace Umbraco.Core.Services
{
///
- /// Service to manage server registrations in the database
+ /// Manages server registrations in the database.
///
- internal class ServerRegistrationService : RepositoryService
+ public sealed class ServerRegistrationService : RepositoryService
{
-
- public ServerRegistrationService(IDatabaseUnitOfWorkProvider provider, RepositoryFactory repositoryFactory, ILogger logger)
- : base(provider, repositoryFactory, logger)
- {
- }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// A UnitOfWork provider.
+ /// A repository factory.
+ /// A logger.
+ public ServerRegistrationService(IDatabaseUnitOfWorkProvider uowProvider, RepositoryFactory repositoryFactory, ILogger logger)
+ : base(uowProvider, repositoryFactory, logger)
+ { }
///
- /// Called to 'call home' to ensure the current server has an active record
+ /// Touches a server to mark it as active; deactivate stale servers.
///
- ///
- public void EnsureActive(string address)
+ /// The server url.
+ /// The server unique identity.
+ /// The time after which a server is considered stale.
+ public void TouchServer(string serverAddress, string serverIdentity, TimeSpan staleTimeout)
{
-
var uow = UowProvider.GetUnitOfWork();
using (var repo = RepositoryFactory.CreateServerRegistrationRepository(uow))
{
- //NOTE: we cannot use Environment.MachineName as this does not work in medium trust
- // found this out in CDF a while back: http://clientdependency.codeplex.com/workitem/13191
-
- var computerName = System.Net.Dns.GetHostName();
- var query = Query.Builder.Where(x => x.ComputerName.ToUpper() == computerName.ToUpper());
- var found = repo.GetByQuery(query).ToArray();
- ServerRegistration server;
- if (found.Any())
+ var query = Query.Builder.Where(x => x.ServerIdentity.ToUpper() == serverIdentity.ToUpper());
+ var server = repo.GetByQuery(query).FirstOrDefault();
+ if (server == null)
{
- server = found.First();
- server.ServerAddress = address; //This should not really change but it might!
- server.UpdateDate = DateTime.UtcNow; //Stick with Utc dates since these might be globally distributed
- server.IsActive = true;
+ server = new ServerRegistration(serverAddress, serverIdentity, DateTime.UtcNow)
+ {
+ IsActive = true
+ };
}
else
{
- server = new ServerRegistration(address, computerName, DateTime.UtcNow);
+ server.ServerAddress = serverAddress; // should not really change but it might!
+ server.UpdateDate = DateTime.UtcNow; // stick with Utc dates since these might be globally distributed
+ server.IsActive = true;
}
repo.AddOrUpdate(server);
uow.Commit();
+
+ repo.DeactiveStaleServers(staleTimeout);
}
}
///
- /// Deactivates a server by name
+ /// Deactivates a server.
///
- ///
- public void DeactiveServer(string computerName)
+ /// The server unique identity.
+ public void DeactiveServer(string serverIdentity)
{
var uow = UowProvider.GetUnitOfWork();
using (var repo = RepositoryFactory.CreateServerRegistrationRepository(uow))
{
- var query = Query.Builder.Where(x => x.ComputerName.ToUpper() == computerName.ToUpper());
- var found = repo.GetByQuery(query).ToArray();
- if (found.Any())
+ var query = Query.Builder.Where(x => x.ServerIdentity.ToUpper() == serverIdentity.ToUpper());
+ var server = repo.GetByQuery(query).FirstOrDefault();
+ if (server != null)
{
- var server = found.First();
server.IsActive = false;
repo.AddOrUpdate(server);
uow.Commit();
@@ -76,7 +79,20 @@ namespace Umbraco.Core.Services
}
///
- /// Return all active servers
+ /// Deactivates stale servers.
+ ///
+ /// The time after which a server is considered stale.
+ public void DeactiveStaleServers(TimeSpan staleTimeout)
+ {
+ var uow = UowProvider.GetUnitOfWork();
+ using (var repo = RepositoryFactory.CreateServerRegistrationRepository(uow))
+ {
+ repo.DeactiveStaleServers(staleTimeout);
+ }
+ }
+
+ ///
+ /// Return all active servers.
///
///
public IEnumerable GetActiveServers()
diff --git a/src/Umbraco.Core/Services/ServiceContext.cs b/src/Umbraco.Core/Services/ServiceContext.cs
index f1eb873db7..255f6e457f 100644
--- a/src/Umbraco.Core/Services/ServiceContext.cs
+++ b/src/Umbraco.Core/Services/ServiceContext.cs
@@ -274,7 +274,7 @@ namespace Umbraco.Core.Services
///
/// Gets the
///
- internal ServerRegistrationService ServerRegistrationService
+ public ServerRegistrationService ServerRegistrationService
{
get { return _serverRegistrationService.Value; }
}
diff --git a/src/Umbraco.Core/StringExtensions.cs b/src/Umbraco.Core/StringExtensions.cs
index 86a2f9c36e..eb7c0f4975 100644
--- a/src/Umbraco.Core/StringExtensions.cs
+++ b/src/Umbraco.Core/StringExtensions.cs
@@ -114,13 +114,11 @@ namespace Umbraco.Core
internal static string ReplaceNonAlphanumericChars(this string input, char replacement)
{
- //any character that is not alphanumeric, convert to a hyphen
- var mName = input;
- foreach (var c in mName.ToCharArray().Where(c => !char.IsLetterOrDigit(c)))
- {
- mName = mName.Replace(c, replacement);
- }
- return mName;
+ var inputArray = input.ToCharArray();
+ var outputArray = new char[input.Length];
+ for (var i = 0; i < inputArray.Length; i++)
+ outputArray[i] = char.IsLetterOrDigit(inputArray[i]) ? inputArray[i] : replacement;
+ return new string(outputArray);
}
///
diff --git a/src/Umbraco.Core/Sync/BatchedDatabaseServerMessenger.cs b/src/Umbraco.Core/Sync/BatchedDatabaseServerMessenger.cs
new file mode 100644
index 0000000000..5c439377f3
--- /dev/null
+++ b/src/Umbraco.Core/Sync/BatchedDatabaseServerMessenger.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Newtonsoft.Json;
+using Umbraco.Core.Models.Rdbms;
+using umbraco.interfaces;
+
+namespace Umbraco.Core.Sync
+{
+ // abstract because it needs to be inherited by a class that will
+ // - trigger FlushBatch() when appropriate
+ // - trigger Boot() when appropriate
+ // - trigger Sync() when appropriate
+ //
+ public abstract class BatchedDatabaseServerMessenger : DatabaseServerMessenger
+ {
+ private readonly Func> _getBatch;
+
+ protected BatchedDatabaseServerMessenger(ApplicationContext appContext, bool enableDistCalls, DatabaseServerMessengerOptions options,
+ Func> getBatch)
+ : base(appContext, enableDistCalls, options)
+ {
+ if (getBatch == null)
+ throw new ArgumentNullException("getBatch");
+
+ _getBatch = getBatch;
+ }
+
+ public void FlushBatch()
+ {
+ var batch = _getBatch(false);
+ if (batch == null) return;
+
+ var instructions = batch.SelectMany(x => x.Instructions).ToArray();
+ batch.Clear();
+ if (instructions.Length == 0) return;
+
+ var dto = new CacheInstructionDto
+ {
+ UtcStamp = DateTime.UtcNow,
+ Instructions = JsonConvert.SerializeObject(instructions, Formatting.None),
+ OriginIdentity = GetLocalIdentity()
+ };
+
+ ApplicationContext.DatabaseContext.Database.Insert(dto);
+ }
+
+ protected override void DeliverRemote(IEnumerable servers, ICacheRefresher refresher, MessageType messageType, IEnumerable