I'd like to be able to simulate the compass sensor when running a Windows Phone 7.1 in the emulator.
At this stage I don't particularly care what data the compass returns. Just that I can run against something when using the emulator to test the code in question.
I'm aware that I could deploy to my dev unlocked phone to test compass functionality but I've found the connection via the Zune software to drop out frequently.
Update
I've looked into creating my own wrapper class that could simulate the compass when running a debug build and the compass isn't otherwise supported.
The Microsoft.Devices.Sensors.CompassReading struct has me a bit stumpted. Because it is a struct where the properties can only be set internally I can't inherit from it to provide my own values back. I looked at using reflection to brute force some values in but Silverlight doesn't appear to allow it.
as you already noticed I had a similar problem. when I mocked the compass sensor, I also had difficulties because you cannot inherite from the existing classes and write your own logic. Therefore I wrote my own compass interface which is the only compass functionality used by my application. Then there are two implementations, one wrapper to the WP7 compass functionalities and my mock compass.
I can show you some code, but not before weekend as I'm not at my delevopment machine atm.
Edit:
You already got it but for other people who have the same problem I'll add my code. As I already said, I wrote an interface and two implementations, one for the phone and a mock implementation.
Compass Interface
public interface ICompass
{
#region Methods
void Start();
void Stop();
#endregion
#region Properties
CompassData CurrentValue { get; }
bool IsDataValid { get; }
TimeSpan TimeBetweenUpdates { get; set; }
#endregion
#region Events
event EventHandler<CalibrationEventArgs> Calibrate;
event EventHandler<CompassDataChangedEventArgs> CurrentValueChanged;
#endregion
}
Used data classes and event args
public class CompassData
{
public CompassData(double headingAccurancy, double magneticHeading, Vector3 magnetometerReading, DateTimeOffset timestamp, double trueHeading)
{
HeadingAccuracy = headingAccurancy;
MagneticHeading = magneticHeading;
MagnetometerReading = magnetometerReading;
Timestamp = timestamp;
TrueHeading = trueHeading;
}
public CompassData(CompassReading compassReading)
{
HeadingAccuracy = compassReading.HeadingAccuracy;
MagneticHeading = compassReading.MagneticHeading;
MagnetometerReading = compassReading.MagnetometerReading;
Timestamp = compassReading.Timestamp;
TrueHeading = compassReading.TrueHeading;
}
#region Properties
public double HeadingAccuracy { get; private set; }
public double MagneticHeading { get; private set; }
public Vector3 MagnetometerReading { get; private set; }
public DateTimeOffset Timestamp { get; private set; }
public double TrueHeading { get; private set; }
#endregion
}
public class CompassDataChangedEventArgs : EventArgs
{
public CompassDataChangedEventArgs(CompassData compassData)
{
CompassData = compassData;
}
public CompassData CompassData { get; private set; }
}
WP7 implementation
public class DeviceCompass : ICompass
{
private Compass _compass;
#region Implementation of ICompass
public void Start()
{
if(_compass == null)
{
_compass = new Compass {TimeBetweenUpdates = TimeBetweenUpdates};
// get TimeBetweenUpdates because the device could have change it to another value
TimeBetweenUpdates = _compass.TimeBetweenUpdates;
// attach to events
_compass.CurrentValueChanged += CompassCurrentValueChanged;
_compass.Calibrate += CompassCalibrate;
}
_compass.Start();
}
public void Stop()
{
if(_compass != null)
{
_compass.Stop();
}
}
public CompassData CurrentValue
{
get { return _compass != null ? new CompassData(_compass.CurrentValue) : default(CompassData); }
}
public bool IsDataValid
{
get { return _compass != null ? _compass.IsDataValid : false; }
}
public TimeSpan TimeBetweenUpdates { get; set; }
public event EventHandler<CalibrationEventArgs> Calibrate;
public event EventHandler<CompassDataChangedEventArgs> CurrentValueChanged;
#endregion
#region Private methods
private void CompassCalibrate(object sender, CalibrationEventArgs e)
{
EventHandler<CalibrationEventArgs> calibrate = Calibrate;
if (calibrate != null)
{
calibrate(sender, e);
}
}
private void CompassCurrentValueChanged(object sender, SensorReadingEventArgs<CompassReading> e)
{
EventHandler<CompassDataChangedEventArgs> currentValueChanged = CurrentValueChanged;
if (currentValueChanged != null)
{
currentValueChanged(sender, new CompassDataChangedEventArgs(new CompassData(e.SensorReading)));
}
}
#endregion
}
Mock implementation
public class MockCompass : ICompass
{
private readonly Timer _timer;
private CompassData _currentValue;
private bool _isDataValid;
private TimeSpan _timeBetweenUpdates;
private bool _isStarted;
private readonly Random _random;
public MockCompass()
{
_random = new Random();
_timer = new Timer(TimerEllapsed, null, Timeout.Infinite, Timeout.Infinite);
_timeBetweenUpdates = new TimeSpan();
_currentValue = new CompassData(0, 0, new Vector3(), new DateTimeOffset(), 0);
}
#region Implementation of ICompass
public void Start()
{
_timer.Change(0, (int)TimeBetweenUpdates.TotalMilliseconds);
_isStarted = true;
}
public void Stop()
{
_isStarted = false;
_timer.Change(Timeout.Infinite, Timeout.Infinite);
_isDataValid = false;
}
public CompassData CurrentValue
{
get { return _currentValue; }
}
public bool IsDataValid
{
get { return _isDataValid; }
}
public TimeSpan TimeBetweenUpdates
{
get { return _timeBetweenUpdates; }
set
{
_timeBetweenUpdates = value;
if (_isStarted)
{
_timer.Change(0, (int) TimeBetweenUpdates.TotalMilliseconds);
}
}
}
public event EventHandler<CalibrationEventArgs> Calibrate;
public event EventHandler<CompassDataChangedEventArgs> CurrentValueChanged;
#endregion
#region Private methods
private void TimerEllapsed(object state)
{
_currentValue = new CompassData(_random.NextDouble()*5,
(_currentValue.MagneticHeading + 0.1)%360,
_currentValue.MagnetometerReading,
new DateTimeOffset(DateTime.UtcNow),
(_currentValue.TrueHeading + 0.1)%360);
_isDataValid = true;
EventHandler<CompassDataChangedEventArgs> currentValueChanged = CurrentValueChanged;
if(currentValueChanged != null)
{
currentValueChanged(this, new CompassDataChangedEventArgs(_currentValue));
}
}
#endregion
}
Related
After updating to ABP v3.0.0, I started getting DbContext disposed exception when running parallel requests to repository that create new UnitOfWork like this:
using (var uow = _unitOfWorkManager.Begin(TransactionScopeOption.RequiresNew))
I looked in the source code and found some code in AsyncLocalCurrentUnitOfWorkProvider that I don't understand. When setting current uow, it sets property in wrapper:
private static readonly AsyncLocal<LocalUowWrapper> AsyncLocalUow = new AsyncLocal<LocalUowWrapper>();
private static void SetCurrentUow(IUnitOfWork value)
{
lock (AsyncLocalUow)
{
if (value == null)
{
if (AsyncLocalUow.Value == null)
{
return;
}
if (AsyncLocalUow.Value.UnitOfWork?.Outer == null)
{
AsyncLocalUow.Value.UnitOfWork = null;
AsyncLocalUow.Value = null;
return;
}
AsyncLocalUow.Value.UnitOfWork = AsyncLocalUow.Value.UnitOfWork.Outer;
}
else
{
if (AsyncLocalUow.Value?.UnitOfWork == null)
{
if (AsyncLocalUow.Value != null)
{
AsyncLocalUow.Value.UnitOfWork = value;
}
AsyncLocalUow.Value = new LocalUowWrapper(value);
return;
}
value.Outer = AsyncLocalUow.Value.UnitOfWork;
AsyncLocalUow.Value.UnitOfWork = value;
}
}
}
private class LocalUowWrapper
{
public IUnitOfWork UnitOfWork { get; set; }
public LocalUowWrapper(IUnitOfWork unitOfWork)
{
UnitOfWork = unitOfWork;
}
}
That does not look thread-safe, as any thread can set new UnitOfWork and then dispose it.
Is it a bug? I can use my own implementation of ICurrentUnitOfWorkProvider, without wrapping, but I'm not sure if that is correct.
Update
I can't give an example with DbContext disposed exception, but here is one with null reference exception in repository.GetAll() method. I think it has the same reason.
namespace TestParallelEFRequest
{
public class Ent1 : Entity<int> { public string Name { get; set; } }
public class Ent2 : Entity<int> { public string Name { get; set; } }
public class Ent3 : Entity<int> { public string Name { get; set; } }
public class Ent4 : Entity<int> { public string Name { get; set; } }
public class DomainStartEvent : EventData {}
public class DBContext : AbpDbContext {
public DBContext(): base("Default") {}
public IDbSet<Ent1> Ent1 { get; set; }
public IDbSet<Ent2> Ent2 { get; set; }
public IDbSet<Ent3> Ent3 { get; set; }
public IDbSet<Ent4> Ent4 { get; set; }
}
public class TestService : DomainService, IEventHandler<DomainStartEvent>
{
private readonly IRepository<Ent1> _rep1;
private readonly IRepository<Ent2> _rep2;
private readonly IRepository<Ent3> _rep3;
private readonly IRepository<Ent4> _rep4;
public TestService(IRepository<Ent1> rep1, IRepository<Ent2> rep2, IRepository<Ent3> rep3, IRepository<Ent4> rep4) {
_rep1 = rep1;_rep2 = rep2;_rep3 = rep3;_rep4 = rep4;
}
Task HandleEntityes<T>(IRepository<T> rep, int i) where T : class, IEntity<int> {
return Task.Run(() => {
using (var uow = UnitOfWorkManager.Begin(TransactionScopeOption.RequiresNew)) {
Thread.Sleep(i); // Simulating work
rep.GetAll(); // <- Exception here
}
});
}
public void HandleEvent(DomainStartEvent eventData) {
using (var uow = UnitOfWorkManager.Begin(TransactionScopeOption.RequiresNew)) {
var t1 = HandleEntityes(_rep1, 10);
var t2 = HandleEntityes(_rep2, 10);
var t3 = HandleEntityes(_rep3, 10);
var t4 = HandleEntityes(_rep4, 1000);
Task.WaitAll(t1, t2, t3, t4);
}
}
}
[DependsOn(typeof(AbpEntityFrameworkModule))]
public class ProgrammModule : AbpModule
{
public override void PreInitialize() { Configuration.DefaultNameOrConnectionString = "Default"; }
public override void Initialize()
{
IocManager.RegisterAssemblyByConvention(Assembly.GetExecutingAssembly());
Database.SetInitializer<DBContext>(null);
}
}
class Program {
static void Main(string[] args) {
using (var bootstrapper = AbpBootstrapper.Create<ProgrammModule>()) {
bootstrapper.Initialize();
bootstrapper.IocManager.Resolve<IEventBus>().Trigger(new DomainStartEvent());
Console.ReadKey();
}
}
}
}
PRISM 6.2 / EntityFramework 6.3.1 / StockTrader UI / UnityContainer
I acutally having a project with PRISM 5.0.0 and want to update to PRISM 6.2. The project runs fine on 5.0.0, but when I'm updating to 6.2 I got the following problem with the InteractionRequest.
When I navigate to a view/viewmodel with Notifications for the first time, everything works and I can handle the InteractionRequests as usual. If I navigate back and navigate to the view again with a new object, the InteractionsRequest raised the notification twice. (...navigating back and go to again -> raised three times and so on).
In some reasons, the message "This Visual is not connected to a PresentationSource" will occur.
I figure out, that the _invocationCount and _invocationList on the InteractionRequest will not be set to "0"/"null" with PRISM 6.2. So, i think the InteractionRequest will call the notification more than one time. Attached, are screenshots from PRISM 5 and PRISM 6.2.
How can I handle this and solve the problem. In my opinion, it's not a big thing but I actually spent a lot of time to find a solution. Thanks...
PRISM 5.0.0 - working fine
PRISM 6.2 - issue
2017.02.22 Added Sourccode. Software is used to handle devices in datacenters. I deleted all unnecessary sourcecode, but with these files the problem still occur. Perhaps this is a try to find my issue....
Rackmodule.cs
-> Initialize Module Rack
public class RackModule : IModule
{
private readonly IRegionManager _regionManager;
private readonly IUnityContainer _container;
public RackModule(IRegionManager regionManager, IUnityContainer container)
{
_regionManager = regionManager;
_container = container;
}
public void Initialize()
{
_container.RegisterType<IRackViewModel, RackViewModel>(new ContainerControlledLifetimeManager());
_container.RegisterType<IRackToolbarViewModel, RackToolbarViewModel>(new ContainerControlledLifetimeManager());
_container.RegisterType<IRackStatusbarViewModel, RackStatusbarViewModel>(new ContainerControlledLifetimeManager());
_container.RegisterType<IRackSummaryViewModel, RackSummaryViewModel>(new ContainerControlledLifetimeManager());
_container.RegisterType<IGeneralDataViewModel, GeneralDataViewModel>(new ContainerControlledLifetimeManager());
_container.RegisterType<IPlanDataViewModel, PlanDataViewModel>(new ContainerControlledLifetimeManager());
_container.RegisterType<IRackDataViewModel, RackDataViewModel>(new ContainerControlledLifetimeManager());
_container.RegisterType<Object, GeneralDataView>(typeof(GeneralDataView).FullName);
IRegion region = this._regionManager.Regions["MainRegion"];
var rackView = _container.Resolve<RackView>();
region.Add(rackView, "RackView");
region.Activate(rackView);
IRegion toolbarregion = this._regionManager.Regions["RackToolbarRegion"];
var toolbarView = _container.Resolve<RackToolbarView>();
toolbarregion.Add(toolbarView, "RackToolbarView");
toolbarregion.Activate(toolbarView);
IRegion statusbarregion = this._regionManager.Regions["RackStatusbarRegion"];
var statusbarView = _container.Resolve<RackStatusbarView>();
statusbarregion.Add(statusbarView, "RackStatusbarView");
statusbarregion.Activate(statusbarView);
_container.RegisterType<Object, RackSummaryView>(typeof(RackSummaryView).FullName);
_regionManager.RequestNavigate(RegionNames.RackContentRegion, typeof(RackSummaryView).FullName);
}
}
RackSummaryViewModel.cs
-> Overview of racks. Go to RackDataView, when click on object
public class RackSummaryViewModel : BindableBase, IRackSummaryViewModel
{
private readonly IRegionManager _regionManager;
private readonly IEventAggregator _eventAggregator;
private readonly IUnityContainer _container;
public DelegateCommand<SearchEventArgs> OnSearch { get; private set; }
public DelegateCommand AdvancedRackSearchCommand { get; private set; }
public InteractionRequest<AdvancedRackSearchNotification> AdvancedSearchRequest { get; private set; }
private ObservableCollection<RackSummaryEntry> _racks;
public ObservableCollection<RackSummaryEntry> Racks
{
get { return _racks; }
private set {SetProperty(ref _racks, value);}
}
private RackSummaryEntry _currentRack;
public RackSummaryEntry CurrentRack
{
get { return _currentRack; }
set
{
if (SetProperty(ref _currentRack, value))
{
if (_currentRack != null)
{
var parameters = new NavigationParameters();
parameters.Add("RackID", _currentRack.PrimaryKey.ToString(GuidNumericFormatSpecifier));
_container.RegisterType<Object, RackDataView>(typeof(RackDataView).FullName);
_regionManager.RequestNavigate(RegionNames.RackContentRegion, new Uri(typeof(RackDataView).FullName + parameters, UriKind.Relative));
}
}
}
}
private const string GuidNumericFormatSpecifier = "N";
public RackSummaryViewModel(IEventAggregator eventAggregator, IRegionManager regionManager, IUnityContainer container)
{
_regionManager = regionManager;
_eventAggregator = eventAggregator;
_container = container;
ISessionFactory factory = new SessionFactory();
container.RegisterType<IRepository, Repository>(new InjectionConstructor(factory.CurrentUoW));
IUnitOfWork unitOfWork = factory.CurrentUoW;
IRepository localrepository = new Repository(unitOfWork);
var query = localrepository.GetList<DMS.Domain.Domain.Rack>();
Racks = new ObservableCollection<RackSummaryEntry>(query
.Select(x => new RackSummaryEntry
{
PrimaryKey = x.PrimaryKey,
Country = x.Location.Address.Country,
City = x.Location.Address.City,
Street = x.Location.Address.Street,
Building = x.Location.BuildingName,
RoomName = x.Location.RoomName,
RackName = x.RackName,
RackHeight = x.RackHeight
}).ToList());
}
}
RackDataViewModel.cs
-> Only Button "Save" und "Go Back"
public class RackDataViewModel : BindableBase, IRackDataViewModel, INavigationAware, IRegionMemberLifetime
{
private IRegionNavigationJournal _navigationJournal;
private readonly IRegionManager _regionManager;
private readonly IUnityContainer _container;
private readonly IEventAggregator _eventAggregator;
public DelegateCommand GoBackCommand { get; private set; }
public DelegateCommand SaveCommand { get; private set; }
public InteractionRequest<INotification> SaveNotificationRequest { get; private set; }
private const string RackIdKey = "RackID";
private const string EType = "EditType";
private const string GuidNumericFormatSpecifier = "N";
public DMS.Domain.Domain.Rack rack;
// [InjectionConstructor] check if necessary
public RackDataViewModel(IRegionManager regionManager, IRegionNavigationJournal navigationJournal, IUnityContainer container, IEventAggregator eventAggregator, ILoggerFactory logFactory)
{
_regionManager = regionManager;
_navigationJournal = navigationJournal;
_container = container;
_eventAggregator = eventAggregator;
GoBackCommand = new DelegateCommand(OnGoBackExecute);
SaveCommand = new DelegateCommand(OnSaveExecute);
SaveNotificationRequest = new InteractionRequest<INotification>();
}
private void OnGoBackExecute()
{
if (_navigationJournal != null)
{
while (_navigationJournal.CanGoBack)
_navigationJournal.GoBack();
_regionManager.Regions.Remove(RegionNames.RackGeneralDataRegion);
}
}
private void OnSaveExecute()
{
SaveNotificationRequest.Raise(new Notification { Content = "Save changes submitted", Title = "Save changes" });
}
public bool KeepAlive
{
get { return false; }
}
private Guid? GetRequestedRackId(NavigationContext navigationContext)
{
var rack = navigationContext.Parameters[RackIdKey];
Guid rackId;
if (rack != null)
{
if (rack is Guid)
rackId = (Guid)rack;
else
rackId = Guid.Parse(rack.ToString());
return rackId;
}
return null;
}
bool INavigationAware.IsNavigationTarget(NavigationContext navigationContext)
{
var type = navigationContext.Parameters[EType];
if (rack == null || ((string)type) == "New")
return true;
var requestedRackId = GetRequestedRackId(navigationContext);
return requestedRackId.HasValue && requestedRackId.Value == rack.PrimaryKey;
}
void INavigationAware.OnNavigatedFrom(NavigationContext navigationContext)
{
}
void INavigationAware.OnNavigatedTo(NavigationContext navigationContext)
{
Guid? rackId;
NavigationParameters parameters = new NavigationParameters();
string key = navigationContext.Parameters[RackIdKey].ToString();
rackId = GetRequestedRackId(navigationContext);
parameters = navigationContext.Parameters;
_regionManager.RequestNavigate(RegionNames.RackGeneralDataRegion, new Uri(typeof(GeneralDataView).FullName + parameters, UriKind.Relative));
_navigationJournal = navigationContext.NavigationService.Journal;
}
}
GeneralDataViewModel.cs
-> Is in region of RackDataView with the data of the racks
public class GeneralDataViewModel : BindableBase, IGeneralDataViewModel, INavigationAware
{
private IRegionNavigationJournal _navigationJournal;
private readonly IRegionManager _regionManager;
private readonly IRepository _repository;
private const string RackIdKey = "RackID";
public DMS.Domain.Domain.Rack Rack { get; set; }
public List<Location> Locations { get; set; }
public GeneralDataViewModel(IRegionManager regionManager, IRepository repository)
{
_regionManager = regionManager;
_repository = repository;
}
private Guid? GetRequestedRackId(NavigationContext navigationContext)
{
var rack = navigationContext.Parameters[RackIdKey];
Guid rackId;
if (rack != null)
{
if (rack is Guid)
rackId = (Guid)rack;
else
rackId = Guid.Parse(rack.ToString());
return rackId;
}
return null;
}
bool INavigationAware.IsNavigationTarget(NavigationContext navigationContext)
{
if (Rack == null)
return true;
var requestedRackId = GetRequestedRackId(navigationContext);
return requestedRackId.HasValue && requestedRackId.Value == Rack.PrimaryKey;
}
void INavigationAware.OnNavigatedFrom(NavigationContext navigationContext)
{
// Intentionally not implemented.
}
void INavigationAware.OnNavigatedTo(NavigationContext navigationContext)
{
var rackId = GetRequestedRackId(navigationContext);
Rack = _repository.GetEntity<DMS.Domain.Domain.Rack>(rackId);
_navigationJournal = navigationContext.NavigationService.Journal;
}
}
I created a local db with helper app project. and deployed it from isolate storage to installation folder,i added to project directory with content build action by add existing item. my problem is that i want to insert data, but i don't know how to move the db file to isolate storage to insert and data must add to my .sdf file that is locate in my project directory also.
Souphia,
While learning to use WP, I wrote a simple application that tracked tasks.
One version of that app stored all task data in Sql on the phone.
You can read the post and download all the code for the app here:
http://www.ritzcovan.com/2012/02/building-a-simple-windows-phone-app-part-3/
But, here is some of the code from that project:
First we have the model class decorated with the appropriate attributes:
[Table]
public class Task : INotifyPropertyChanged, INotifyPropertyChanging
{
[Column(IsDbGenerated = false, IsPrimaryKey = true, CanBeNull = false)]
public string Id
{
get { return _id; }
set
{
NotifyPropertyChanging("Id");
_id = value;
NotifyPropertyChanging("Id");
}
}
[Column]
public string Name
{
get { return _name; }
set
{
NotifyPropertyChanging("Name");
_name = value;
NotifyPropertyChanged("Name");
}
}
[Column]
public string Category
{
get { return _category; }
set
{
NotifyPropertyChanging("Category");
_category = value;
NotifyPropertyChanged("Category");
}
}
[Column]
public DateTime? DueDate
{
get { return _dueDate; }
set
{
NotifyPropertyChanging("DueDate");
_dueDate = value;
NotifyPropertyChanged("DueDate");
}
}
[Column]
public DateTime? CreateDate
{
get { return _createDate; }
set
{
NotifyPropertyChanging("CreateDate");
_createDate = value;
NotifyPropertyChanged("CreateDate");
}
}
[Column]
public bool IsComplete
{
get { return _isComplete; }
set
{
NotifyPropertyChanging("IsComplete");
_isComplete = value;
NotifyPropertyChanged("IsComplete");
}
}
[Column(IsVersion = true)] private Binary _version;
private string _id;
private bool _isComplete;
private DateTime? _createDate;
private DateTime? _dueDate;
private string _name;
private string _category;
public event PropertyChangedEventHandler PropertyChanged;
public event PropertyChangingEventHandler PropertyChanging;
public void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
public void NotifyPropertyChanging(string property)
{
if (PropertyChanging != null)
PropertyChanging(this, new PropertyChangingEventArgs(property));
}
}
In the constructor in app.xaml.cs, I have the following:
TaskMasterDataContext = new TaskMasterDataContext();
if (!TaskMasterDataContext.DatabaseExists())
{
TaskMasterDataContext.CreateDatabase();
DatabaseHelper.SetupDatabase(TaskMasterDataContext);
}
and here is the TaskMasterDataContext.cs code
public class TaskMasterDataContext : DataContext
{
public TaskMasterDataContext() : base("Data Source=isostore:/TaskMasterData.sdf")
{
}
public Table<Task> Tasks;
}
public static class DatabaseHelper
{
public static void SetupDatabase(TaskMasterDataContext dataContext)
{
string category = string.Empty;
var tasks = new List<Task>();
for (int i = 0; i < 20; i++)
{
tasks.Add(new Task()
{
Id = System.Guid.NewGuid().ToString(),
Category = GetCategoryString(i),
CreateDate = DateTime.Now,
DueDate = DateTime.Now.AddDays(new Random().Next(1, 30)),
IsComplete = false,
Name = String.Format("{0} Task # {1}", GetCategoryString(i), i)
});
}
dataContext.Tasks.InsertAllOnSubmit(tasks);
dataContext.SubmitChanges();
}
private static string GetCategoryString(int i)
{
if (i%2 == 0)
return "home";
if (i%3 == 0)
return "personal";
return "work";
}
}
The DatabaseHelper class is just there to populate the DB with some test data after its created.
I hope this helps.
I need to create a scheduled task using Orchard CMS.
I have a service method (let's say it loads some data from external sources), and I need to execute it every day at 8:00 AM.
I figured out I have to use IScheduledTaskHandler and IScheduledTaskManager... Does anyone know how to solve this problem? Some sample code will be appreciated.
In your IScheduledTaskHandler, you have to implement Process to provide your task implementation (I Advise you to put your implementation in another service class), and you have to register your task in the task manager. Once in the Handler constructor to register the first task, and then in the process implementation, to ensure that once a task was executed, the next one is scheduled.
Here is a sample:
public class MyTaskHandler : IScheduledTaskHandler
{
private const string TaskType = "MyTaskUniqueID";
private readonly IScheduledTaskManager _taskManager;
public ILogger Logger { get; set; }
public MyTaskHandler(IScheduledTaskManager taskManager)
{
_taskManager = taskManager;
Logger = NullLogger.Instance;
try
{
DateTime firstDate = //Set your first task date (utc).
ScheduleNextTask(firstDate);
}
catch(Exception e)
{
this.Logger.Error(e,e.Message);
}
}
public void Process(ScheduledTaskContext context)
{
if (context.Task.TaskType == TaskType)
{
try
{
//Do work (calling an IService for instance)
}
catch (Exception e)
{
this.Logger.Error(e, e.Message);
}
finally
{
DateTime nextTaskDate = //Your next date (utc).
this.ScheduleNextTask(nextTaskDate);
}
}
}
private void ScheduleNextTask(DateTime date)
{
if (date > DateTime.UtcNow )
{
var tasks = this._taskManager.GetTasks(TaskType);
if (tasks == null || tasks.Count() == 0)
this._taskManager.CreateTask(TaskType, date, null);
}
}
}
You should do the first scheduling with an implementation of IOrchardShellEvents rather than in the task constructor to avoid adding multiple tasks.
Here is an abstract DailyTaskHandler class you can implement :
Usage
public class MyTaskHandler : DailyTaskHandler {
private readonly IMyService _myService;
public MyTaskHandler(IMyService myService,
IDailyTasksScheduler dailyTasksScheduler) : base(dailyTasksScheduler) {
_myService = myService;
}
public override int Hour => base.Hour; // you can override default hour
public override void Process() => _myService.DoStuff();
}
Abstract daily task handler and scheduler
public abstract class DailyTaskHandler : IDailyTaskHandler, IScheduledTaskHandler {
private readonly IDailyTasksScheduler _dailyTasksScheduler;
protected DailyTaskHandler(IDailyTasksScheduler dailyTasksScheduler) {
_dailyTasksScheduler = dailyTasksScheduler;
Logger = NullLogger.Instance;
TaskType = GetType().FullName;
}
public ILogger Logger { get; set; }
public virtual int Hour { get; } = 1; // default scheduled hour of the day
public string TaskType { get; }
public void Process(ScheduledTaskContext context) {
if (context.Task.TaskType == TaskType) {
Logger.Information($"Process task: {TaskType}");
try {
Process();
}
catch (Exception e) {
Logger.Error(e, e.Message);
}
finally {
_dailyTasksScheduler.Schedule(this);
}
}
}
public abstract void Process();
}
public class DailyTasksStarter : IOrchardShellEvents {
private readonly IEnumerable<IDailyTaskHandler> _dailyTaskHandlers;
private readonly IDailyTasksScheduler _dailyTasksScheduler;
public DailyTasksStarter(
IEnumerable<IDailyTaskHandler> dailyTaskHandlers,
IDailyTasksScheduler dailyTasksScheduler) {
_dailyTaskHandlers = dailyTaskHandlers;
_dailyTasksScheduler = dailyTasksScheduler;
}
public void Activated() => _dailyTasksScheduler.Schedule(_dailyTaskHandlers);
public void Terminating() { }
}
public class DailyTasksScheduler : IDailyTasksScheduler {
private readonly IScheduledTaskManager _scheduledTaskManager;
public DailyTasksScheduler(IScheduledTaskManager scheduledTaskManager) {
_scheduledTaskManager = scheduledTaskManager;
}
public void Schedule(IDailyTaskHandler dailyTaskHandler) => Schedule(new IDailyTaskHandler[] { dailyTaskHandler });
public void Schedule(IEnumerable<IDailyTaskHandler> dailyTaskHandlers) {
DateTime nextDay = DateTime.UtcNow.AddDays(1);
foreach (var dailyTaskHandler in dailyTaskHandlers) {
DateTime nextTaskDate = new DateTime(nextDay.Year, nextDay.Month, nextDay.Day, dailyTaskHandler.Hour, 0, 0, DateTimeKind.Utc);
if (nextTaskDate > DateTime.UtcNow && _scheduledTaskManager.GetTasks(dailyTaskHandler.TaskType)?.Any() != true) {
_scheduledTaskManager.CreateTask(dailyTaskHandler.TaskType, nextTaskDate, null);
}
}
}
}
public interface IDailyTaskHandler : IDependency {
int Hour { get; }
string TaskType { get; }
}
public interface IDailyTasksScheduler : IDependency {
void Schedule(IDailyTaskHandler dailyTaskHandler);
void Schedule(IEnumerable<IDailyTaskHandler> dailyTaskHandlers);
}
You can avoid using a task scheduler class by having the schedule method and the IOrchardShellEvents implementation on your task handler class.
I've recently started to learn Fluent NH, and I'm having some trouble with this test method. It takes forever to run (it's been running for over ten minutes now, and no sign of progress...).
[TestMethod]
public void Entry_IsCorrectlyMapped()
{
Action<PersistenceSpecification<Entry>> testAction = pspec => pspec
.CheckProperty(e => e.Id, "1")
.VerifyTheMappings();
TestMapping<Entry>(testAction);
}
with this helper method (slightly simplified - i have a couple of try/catch blocks too, to provide nicer error messages):
public void TestMapping<T>(Action<PersistenceSpecification<T>> testAction) where T : IEntity
{
using (var session = DependencyFactory.CreateSessionFactory(true).OpenSession())
{
testAction(new PersistenceSpecification<T>(session));
}
}
The DependencyFactory.CreateSessionFactory() method looks like this:
public static ISessionFactory CreateSessionFactory(bool buildSchema)
{
var cfg = Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory())
.Mappings(m => m.FluentMappings.AddFromAssembly(typeof(Entry).Assembly));
if (buildSchema)
{
cfg = cfg.ExposeConfiguration(config => new SchemaExport(config).Create(false, true));
}
return cfg.BuildSessionFactory();
}
I've tried debugging, but I can't figure out where the bottleneck is. Why is this taking so long?
I would think it has to do with the way your trying to use the session together with the persistence spec. Make a base test class like the one below that provides you a session; if whole test takes longer than about 3 - 4 seconds max something is wrong.
Cheers,
Berryl
[TestFixture]
public class UserAutoMappingTests : InMemoryDbTestFixture
{
private const string _nickName = "berryl";
private readonly Name _name = new Name("Berryl", "Hesh");
private const string _email = "bhesh#cox.net";
protected override PersistenceModel _GetPersistenceModel() { return new UserDomainAutoMapModel().Generate(); }
[Test]
public void Persistence_CanSaveAndLoad_User()
{
new PersistenceSpecification<User>(_Session)
.CheckProperty(x => x.NickName, _nickName)
.CheckProperty(x => x.Email, _email)
.CheckProperty(x => x.Name, _name)
.VerifyTheMappings();
}
}
public abstract class InMemoryDbTestFixture
{
protected ISession _Session { get; set; }
protected SessionSource _SessionSource { get; set; }
protected Configuration _Cfg { get; set; }
protected abstract PersistenceModel _GetPersistenceModel();
protected PersistenceModel _persistenceModel;
[TestFixtureSetUp]
public void SetUpPersistenceModel()
{
_persistenceModel = _GetPersistenceModel();
}
[SetUp]
public void SetUpSession()
{
NHibInMemoryDbSession.Init(_persistenceModel); // your own session factory
_Session = NHibInMemoryDbSession.Session;
_SessionSource = NHibInMemoryDbSession.SessionSource;
_Cfg = NHibInMemoryDbSession.Cfg;
}
[TearDown]
public void TearDownSession()
{
NHibInMemoryDbSession.TerminateInMemoryDbSession();
_Session = null;
_SessionSource = null;
_Cfg = null;
}
}
public static class NHibInMemoryDbSession
{
public static ISession Session { get; private set; }
public static Configuration Cfg { get; private set; }
public static SessionSource SessionSource { get; set; }
public static void Init(PersistenceModel persistenceModel)
{
Check.RequireNotNull<PersistenceModel>(persistenceModel);
var SQLiteCfg = SQLiteConfiguration.Standard.InMemory().ShowSql();
SQLiteCfg.ProxyFactoryFactory(typeof(ProxyFactoryFactory).AssemblyQualifiedName);
var fluentCfg = Fluently.Configure().Database(SQLiteCfg).ExposeConfiguration(cfg => { Cfg = cfg; });
SessionSource = new SessionSource(fluentCfg.BuildConfiguration().Properties, persistenceModel);
Session = SessionSource.CreateSession();
SessionSource.BuildSchema(Session, true);
}
public static void TerminateInMemoryDbSession()
{
Session.Close();
Session.Dispose();
Session = null;
SessionSource = null;
Cfg = null;
Check.Ensure(Session == null);
Check.Ensure(SessionSource == null);
Check.Ensure(Cfg == null);
}
}