Backend API
        
    <div class="schedulers">
    @for(int i = 1; i <= 2; i++) {
        <div class="column-@i">
            @(Html.DevExtreme().Scheduler()
                .ID(string.Format("{0}{1}", "scheduler", i))
                .DataSource(d => d.Mvc()
                    .Controller("SchedulerSignalR")
                    .Key("AppointmentId")
                    .LoadAction("Get")
                    .UpdateAction("Put")
                    .InsertAction("Post")
                    .DeleteAction("Delete")
                )
                .TimeZone("America/Los_Angeles")
                .RemoteFiltering(true)
                .Views(new[] {
                    SchedulerViewType.Day,
                    SchedulerViewType.WorkWeek
                })
                .CurrentView(SchedulerViewType.Day)
                .CurrentDate(new DateTime(2021, 4, 27))
                .StartDayHour(9)
                .EndDayHour(19)
                .Height(600)
                .DateSerializationFormat("yyyy-MM-ddTHH:mm:ssZ")
                .TextExpr("Text")
                .DescriptionExpr("Description")
                .StartDateExpr("StartDate")
                .EndDateExpr("EndDate")
                .AllDayExpr("AllDay")
            )
        </div>
    }
</div>
<script src="~/signalr/signalr-session-id.js"></script>
<script src="~/signalr/signalr-client.js"></script>
<script>
var connection = new signalR.HubConnectionBuilder()
    .withUrl("@Url.Content("~/schedulerSignalRHub")", {
        skipNegotiation: true,
        transport: signalR.HttpTransportType.WebSockets
    })
    .configureLogging(signalR.LogLevel.Information)
    .build();
$(function () {
    var store1 = $("#scheduler1").dxScheduler("getDataSource").store();
    var store2 = $("#scheduler2").dxScheduler("getDataSource").store();
    connection.start()
        .then(function () {
            connection.on("update", function (key, data) {
                store1.push([{ type: "update", key: key, data: data }]);
                store2.push([{ type: "update", key: key, data: data }]);
            });
            connection.on("insert", function (data) {
                store1.push([{ type: "insert", data: data }]);
                store2.push([{ type: "insert", data: data }]);
            });
            connection.on("remove", function (key) {
                store1.push([{ type: "remove", key: key }]);
                store2.push([{ type: "remove", key: key }]);
            });
        });
});
</script>
        
        using Microsoft.AspNetCore.Mvc;
using DevExtreme.NETCore.Demos.Models.SampleData;
using DevExtreme.NETCore.Demos.ViewModels;
namespace DevExtreme.NETCore.Demos.Controllers {
    public class SchedulerController : Controller {
        public ActionResult SignalRService() {
            return View();
        }
    }
}
        
        using System;
using System.Linq;
using DevExtreme.AspNet.Data;
using DevExtreme.AspNet.Mvc;
using DevExpress.Utils.Serializing.Helpers;
using DevExtreme.NETCore.Demos.Hubs;
using DevExtreme.NETCore.Demos.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;
using Microsoft.Extensions.Caching.Memory;
using DevExpress.Data.Utils;
namespace DevExtreme.NETCore.Demos.Controllers.ApiControllers {
    [Route("api/[controller]")]
    public class SchedulerSignalRController : Controller {
        InMemoryAppointmentsDataContext _data;
        private static readonly NonCryptographicRandom random = NonCryptographicRandom.System;
        private IHubContext<SchedulerSignalRHub> hubContext;
        public SchedulerSignalRController(IHttpContextAccessor httpContextAccessor, IMemoryCache memoryCache, IHubContext<SchedulerSignalRHub> hubcontext) {
            _data = new InMemoryAppointmentsDataContext(httpContextAccessor, memoryCache);
            hubContext = hubcontext;
        }
        [HttpGet]
        public object Get(DataSourceLoadOptions loadOptions) {
            return DataSourceLoader.Load(_data.Appointments, loadOptions);
        }
        [HttpPost]
        public IActionResult Post(string values) {
            var newAppointment = new Appointment();
            JsonPopulateObjectExtensions.PopulateObject(values, newAppointment);
            if(!TryValidateModel(newAppointment))
                return BadRequest(ModelState.GetFullErrorMessage());
            _data.Appointments.Add(newAppointment);
            _data.SaveChanges();
            var groupName = GetGroupName();
            if(groupName != null) {
                hubContext.Clients.Group(GetGroupName()).SendAsync("insert", newAppointment);
            }
            return Ok();
        }
        [HttpPut]
        public IActionResult Put(int key, string values) {
            var appointment = _data.Appointments.First(a => a.AppointmentId == key);
            JsonPopulateObjectExtensions.PopulateObject(values, appointment);
            if(!TryValidateModel(appointment))
                return BadRequest(ModelState.GetFullErrorMessage());
            _data.SaveChanges();
            var groupName = GetGroupName();
            if(groupName != null) {
                hubContext.Clients.Group(GetGroupName()).SendAsync("update", key, appointment);
            }
            return Ok();
        }
        [HttpDelete]
        public void Delete(int key) {
            var appointment = _data.Appointments.First(a => a.AppointmentId == key);
            _data.Appointments.Remove(appointment);
            _data.SaveChanges();
            var groupName = GetGroupName();
            if(groupName != null) {
                hubContext.Clients.Group(GetGroupName()).SendAsync("remove", key);
            }
        }
        string GetGroupName() {
            HttpContext.Request.Cookies.TryGetValue(SchedulerSignalRHub.GroupIdKey, out var cookie);
            return cookie ?? "0";
        }
    }
}
        
        using System;
using System.Text.Json.Serialization;
namespace DevExtreme.NETCore.Demos.Models {
    public class Appointment {
        [JsonPropertyName("AppointmentId")]
        public int AppointmentId { get; set; }
        [JsonPropertyName("Text")]
        public string Text { get; set; }
        [JsonPropertyName("Description")]
        public string Description { get; set; }
        [JsonPropertyName("StartDate")]
        public string StartDate { get; set; }
        [JsonPropertyName("EndDate")]
        public string EndDate { get; set; }
        [JsonPropertyName("AllDay")]
        public bool AllDay { get; set; }
        [JsonPropertyName("RecurrenceRule")]
        public string RecurrenceRule { get; set; }
        [JsonPropertyName("RecurrenceException")]
        public string RecurrenceException { get; set; }
    }
    public class DisableDatesAppointment {
        [JsonPropertyName("AppointmentId")]
        public int AppointmentId { get; set; }
        [JsonPropertyName("Text")]
        public string Text { get; set; }
        [JsonPropertyName("Description")]
        public string Description { get; set; }
        [JsonPropertyName("StartDate")]
        public DateTime StartDate { get; set; }
        [JsonPropertyName("EndDate")]
        public DateTime EndDate { get; set; }
        [JsonPropertyName("AllDay")]
        public bool AllDay { get; set; }
        [JsonPropertyName("RecurrenceRule")]
        public string RecurrenceRule { get; set; }
        [JsonPropertyName("RecurrenceException")]
        public string RecurrenceException { get; set; }
    }
}
        
        using System;
using System.Collections.Generic;
using System.Linq;
namespace DevExtreme.NETCore.Demos.Models.SampleData {
    public partial class SampleData {
        public static readonly IEnumerable<Appointment> Appointments = new[] {
            new Appointment {
                AppointmentId = 1,
                Text = "Website Re-Design Plan",
                StartDate = "2021-04-26T16:30:00.000Z",
                EndDate = "2021-04-26T18:30:00.000Z"
            },
            new Appointment {
                AppointmentId = 2,
                Text = "Book Flights to San Fran for Sales Trip",
                StartDate = "2021-04-26T19:00:00.000Z",
                EndDate = "2021-04-26T20:00:00.000Z",
                AllDay = true
            },
            new Appointment {
                AppointmentId = 3,
                Text = "Install New Router in Dev Room",
                StartDate = "2021-04-26T21:30:00.000Z",
                EndDate = "2021-04-26T22:30:00.000Z"
            },
            new Appointment {
                AppointmentId = 4,
                Text = "Approve Personal Computer Upgrade Plan",
                StartDate = "2021-04-27T17:00:00.000Z",
                EndDate = "2021-04-27T18:00:00.000Z"
            },
            new Appointment {
                AppointmentId = 5,
                Text = "Final Budget Review",
                StartDate = "2021-04-27T19:00:00.000Z",
                EndDate = "2021-04-27T20:35:00.000Z"
            },
            new Appointment {
                AppointmentId = 6,
                Text = "New Brochures",
                StartDate = "2021-04-27T21:30:00.000Z",
                EndDate = "2021-04-27T22:45:00.000Z"
            },
            new Appointment {
                AppointmentId = 7,
                Text = "Install New Database",
                StartDate = "2021-04-28T16:45:00.000Z",
                EndDate = "2021-04-28T18:15:00.000Z"
            },
            new Appointment {
                AppointmentId = 8,
                Text = "Approve New Online Marketing Strategy",
                StartDate = "2021-04-28T19:00:00.000Z",
                EndDate = "2021-04-28T21:00:00.000Z"
            },
            new Appointment {
                AppointmentId = 9,
                Text = "Upgrade Personal Computers",
                StartDate = "2021-04-28T22:15:00.000Z",
                EndDate = "2021-04-28T23:30:00.000Z"
            },
            new Appointment {
                AppointmentId = 10,
                Text = "Customer Workshop",
                StartDate = "2021-04-29T18:00:00.000Z",
                EndDate = "2021-04-29T19:00:00.000Z",
                AllDay = true
            },
            new Appointment {
                AppointmentId = 11,
                Text = "Prepare 2021 Marketing Plan",
                StartDate = "2021-04-29T18:00:00.000Z",
                EndDate = "2021-04-29T20:30:00.000Z"
            },
            new Appointment {
                AppointmentId = 12,
                Text = "Brochure Design Review",
                StartDate = "2021-04-29T21:00:00.000Z",
                EndDate = "2021-04-29T22:30:00.000Z"
            },
            new Appointment {
                AppointmentId = 13,
                Text = "Create Icons for Website",
                StartDate = "2021-04-30T17:00:00.000Z",
                EndDate = "2021-04-30T18:30:00.000Z"
            },
            new Appointment {
                AppointmentId = 14,
                Text = "Upgrade Server Hardware",
                StartDate = "2021-04-30T21:30:00.000Z",
                EndDate = "2021-04-30T23:00:00.000Z"
            },
            new Appointment {
                AppointmentId = 15,
                Text = "Submit New Website Design",
                StartDate = "2021-04-30T23:30:00.000Z",
                EndDate = "2021-05-01T01:00:00.000Z"
            },
            new Appointment {
                AppointmentId = 16,
                Text = "Launch New Website",
                StartDate = "2021-04-30T19:20:00.000Z",
                EndDate = "2021-04-30T21:00:00.000Z"
            }
        };
    }
}
        
        using System;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
namespace DevExtreme.NETCore.Demos.Hubs {
    public class SchedulerSignalRHub : Hub {
        public static string GroupIdKey = "dx-SchedulerSignalRHub-groupId";
        public override Task OnConnectedAsync() {
            string groupId;
            Context.GetHttpContext().Request.Cookies.TryGetValue(GroupIdKey, out groupId);
            Groups.AddToGroupAsync(Context.ConnectionId, groupId ?? "0");
            return base.OnConnectedAsync();
        }
    }
}
        
        .schedulers {
    display: flex;
}
.column-1 {
    padding-right: 5px;
}
.column-2 {
    padding-left: 5px;
}
.dx-scheduler-small .dx-scheduler-view-switcher.dx-tabs {
    display: table;
}
        
                Follow the steps below to implement this functionality. Note again that this demo repeats all steps twice for the two Schedulers. Your project will have a single control and a single store.
- 
Configure a CustomStore. In this demo, we use the createStore method (part of the DevExtreme.AspNet.data extension). 
- 
Create the Scheduler and use its dataSource property to bind it to the store instance. 
- 
When a push notification is received, call the store's push(changes) method to update the store's data (see the connection.onevent handlers).
For server-side configuration, refer to the ASP.NET MVC version of this demo.
For more information about integration with push services, refer to the following help topic: Integration with Push Services.