I have a problem with Sterling Database for Windows Phone. I implemented the database step by step in my wp7app, but it doesn't serialize my data when new entities are saved. For example: I serialize credentials using sterling database:
var userCredentials = new UserCredentials(userName, password);
App.Database.Save(userCredentials);
App.Database.Flush();
But when the application is reactivated (or re-launched) Sterling doesn't return any values from isolated storage:
var firstOrDefault = App.Database.Query<UserCredentials, string>()
.ToList()
.FirstOrDefault();
My ActivateEngine method looks are standard and TableDefinition is:
CreateTableDefinition< UserCredentials, string >(t => t.UserName),
Why is sterling database doesn't serialize my data? Everything seems to be implemented fine. Please help.
Are you activating and registering the database on startup and diposing on completion as described in the Quickstart?
My personal preference is to use an application service similar to the following:
namespace MyApp.Data
{
using System.Windows;
using Wintellect.Sterling;
using Wintellect.Sterling.IsolatedStorage;
///
/// Defines a an application service that supports the Sterling database.
///
public class SterlingDatabaseService : IApplicationService, IApplicationLifetimeAware
{
public static SterlingDatabaseService Current { get; private set; }
public ISterlingDatabaseInstance Database { get; private set; }
private SterlingEngine _engine;
///
/// Called by an application in order to initialize the application extension service.
///
/// Provides information about the application state.
public void StartService(ApplicationServiceContext context)
{
Current = this;
_engine = new SterlingEngine();
}
///
/// Called by an application in order to stop the application extension service.
///
public void StopService()
{
_engine = null;
}
///
/// Called by an application immediately before the event occurs.
///
public void Starting()
{
_engine.Activate();
Database = _engine
.SterlingDatabase
.RegisterDatabase(new IsolatedStorageDriver());
}
///
/// Called by an application immediately after the event occurs.
///
public void Started()
{
return;
}
///
/// Called by an application immediately before the event occurs.
///
public void Exiting()
{
_engine.Dispose();
}
///
/// Called by an application immediately after the event occurs.
///
public void Exited()
{
return;
}
}
}
If you use this approach, don't forget to add an instance in App.xaml:
<Application.ApplicationLifetimeObjects>
<!-- Required object that handles lifetime events for the application. -->
<shell:PhoneApplicationService Activated="Application_Activated"
Closing="Application_Closing"
Deactivated="Application_Deactivated"
Launching="Application_Launching" />
<data:SterlingDatabaseService />
</Application.ApplicationLifetimeObjects>
Related
I am having trouble with local notification on Android using Xamarin. The local notification shows every time the app is in the foreground or open. If I reboot the notification does not show. There is a RECEIVE_BOOT_COMPLETED BroadcastReceiver but I do not know if it is firing on reboot.
I cannot figure out what is wrong. I have tripled checked everything and feel like something small is missing.
Edit: I think I have it worked out for notifications on the app being closed thanks to this thread: Broadcast receiver not working when app is closed. Essentially, if debugging, the app needs to be reopened after closing it so that the receiver is called.
I used this doc to setup local notifications: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/local-notifications
AndroidManifest.cs
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="5" android:versionName="1.0.2.1" package="com.business.myapp" android:installLocation="preferExternal">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="31" />
<application android:label="MyApp" android:theme="#style/MainTheme" android:supportsRtl="true" android:icon="#mipmap/icon"></application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
</manifest>
AlarmHandler.cs
using Android.Content;
using MyApp.Business.Services;
using MyApp.Global;
using MyApp.Utils;
using Serilog;
using System;
namespace MyApp.Droid
{
/// <summary>
/// Handle notification receiving.
///
/// https://github.com/xamarin/xamarin-forms-samples/blob/main/LocalNotifications/LocalNotifications/LocalNotifications.Android/AlarmHandler.cs
/// </summary>
[BroadcastReceiver(
Enabled = true,
Exported = true,
Label = "Local Notifications Broadcast Receiver")]
public class AlarmHandler : BroadcastReceiver
{
/// <summary>
/// Called when a notification is shown.
/// </summary>
/// <param name="context"></param>
/// <param name="intent"></param>
public override void OnReceive(Context context, Intent intent)
{
// if (intent?.Extras != null)
// {
if (!Settings.DailyNotificationEnabled) return;
// do not show notification if it is not time based on the settings time value.
// this handles cases where the user changes the notification time multiple times.
/* Disabled on 3/6/2022 to see if this helps notifications.
var timeSpan = DateTime.Now - NotificationDateTime.GetDailyNotificationDateTime();
var absTotalSeconds = Math.Abs(timeSpan.TotalSeconds);
if (absTotalSeconds > 59 && absTotalSeconds < 86399) return;
*/
string title = intent.GetStringExtra(AndroidNotificationManager.TitleKey);
string message = intent.GetStringExtra(AndroidNotificationManager.MessageKey);
title = Constants.NotificationOffllineTitle;
message = Constants.NotificationOffllineMessage;
try
{
var votdHttpRequest = new VotdHttpRequest();
var votdApiDto = votdHttpRequest.MakeApiCall(DateTime.Now, Settings.TwoLetterISOLanguageName);
title = $"{DateTime.Now.ToShortDateString()} {votdApiDto.#ref} ({votdApiDto.ver.ToUpper()})";
var scripture = Settings.DisplayKjvVersion ? votdApiDto.en_scrip_kjv : votdApiDto.en_scrip;
message = Remove.RecursiveHtmlDecode(scripture);
}
catch (Exception ex)
{
Log.Error(ex, "Failed recieving notification.");
}
AndroidNotificationManager manager = AndroidNotificationManager.Instance ?? new AndroidNotificationManager();
manager.Show(title, message);
// }
}
}
}
AndroidNotificationManager.cs
using System;
using Android.App;
using Android.Content;
using Android.Graphics;
using Android.OS;
using AndroidX.Core.App;
using MyApp.Notifications;
using Xamarin.Forms;
using AndroidApp = Android.App.Application;
[assembly: Dependency(typeof(MyApp.Droid.AndroidNotificationManager))]
namespace MyApp.Droid
{
/// <summary>
/// Daily notification manager.
/// </summary>
public class AndroidNotificationManager : INotificationManager
{
#region Private Fields
const string channelId = "default";
const string channelName = "Default";
const string channelDescription = "The default channel for notifications.";
bool channelInitialized = false;
int messageId = 0;
int pendingIntentId = 0;
NotificationManager manager;
#endregion
#region Public Properties
public const string TitleKey = "title";
public const string MessageKey = "message";
public event EventHandler NotificationReceived;
public static AndroidNotificationManager Instance { get; private set; }
public AndroidNotificationManager() => Initialize();
#endregion
#region Constructor
#endregion
#region Private Methods
/// <summary>
/// Create the notification channel.
/// </summary>
void CreateNotificationChannel()
{
manager = (NotificationManager)AndroidApp.Context.GetSystemService(AndroidApp.NotificationService);
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
var channelNameJava = new Java.Lang.String(channelName);
var channel = new NotificationChannel(channelId, channelNameJava, NotificationImportance.Default)
{
Description = channelDescription
};
manager.CreateNotificationChannel(channel);
}
channelInitialized = true;
}
/// <summary>
/// Get the time of notification in UTC ticks.
/// </summary>
/// <param name="notifyTime"></param>
/// <returns></returns>
long GetNotifyTime(DateTime notifyTime)
{
DateTime utcTime = TimeZoneInfo.ConvertTimeToUtc(notifyTime);
double epochDiff = (new DateTime(1970, 1, 1) - DateTime.MinValue).TotalSeconds;
long utcAlarmTime = utcTime.AddSeconds(-epochDiff).Ticks / 10000;
return utcAlarmTime; // milliseconds
}
#endregion
#region Public Methods
/// <summary>
/// Initialize the notification channel
/// </summary>
public void Initialize()
{
if (Instance == null)
{
CreateNotificationChannel();
Instance = this;
}
}
/// <summary>
/// Schedule a notification.
/// </summary>
/// <param name="title"></param>
/// <param name="message"></param>
/// <param name="notifyTime">Time of notification.</param>
public void SendNotification(string title, string message, int id, DateTime? notifyTime = null)
{
if (!channelInitialized)
{
CreateNotificationChannel();
}
if (notifyTime != null)
{
var intent = CreateIntent(id);
intent.PutExtra(TitleKey, title);
intent.PutExtra(MessageKey, message);
intent.SetIdentifier(id.ToString());
PendingIntent pendingIntent = PendingIntent.GetBroadcast(
AndroidApp.Context,
pendingIntentId++,
intent,
PendingIntentFlags.Immutable);
long triggerTime = GetNotifyTime(notifyTime.Value);
long millisecondsRepeating = AlarmManager.IntervalDay;
AlarmManager alarmManager = AndroidApp.Context.GetSystemService(Context.AlarmService) as AlarmManager;
alarmManager.SetRepeating(AlarmType.RtcWakeup, triggerTime, millisecondsRepeating, pendingIntent);
}
else
{
Show(title, message);
}
}
/// <summary>
/// Called when a notifiation is received.
/// </summary>
/// <param name="title"></param>
/// <param name="message"></param>
public void ReceiveNotification(string title, string message)
{
var args = new NotificationEventArgs()
{
Title = title,
Message = message,
};
NotificationReceived?.Invoke(null, args);
}
/// <summary>
/// Display the notification in the notification area.
/// </summary>
/// <param name="title"></param>
/// <param name="message"></param>
public void Show(string title, string message)
{
Intent intent = new Intent(AndroidApp.Context, typeof(MainActivity));
intent.PutExtra(TitleKey, title);
intent.PutExtra(MessageKey, message);
PendingIntent pendingIntent = PendingIntent.GetActivity(
AndroidApp.Context,
pendingIntentId++,
intent,
PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable);
var textStyle = new NotificationCompat.BigTextStyle();
textStyle.BigText(message);
textStyle.SetSummaryText(title);
NotificationCompat.Builder builder = new NotificationCompat.Builder(AndroidApp.Context, channelId)
.SetContentIntent(pendingIntent)
// .SetContentTitle(title) // not required with big text style
// .SetContentText(message) // not required with big text style
.SetLargeIcon(BitmapFactory.DecodeResource(AndroidApp.Context.Resources, Resource.Drawable.icon_votd))
.SetSmallIcon(Resource.Drawable.icon_votd)
.SetDefaults((int)NotificationDefaults.Sound | (int)NotificationDefaults.Vibrate)
.SetStyle(textStyle);
if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
builder.SetVisibility((int)NotificationVisibility.Public);
Notification notification = builder.Build();
notification.Flags = NotificationFlags.AutoCancel;
manager.Notify(messageId++, notification);
}
/// <summary>
/// Delete a notification.
/// </summary>
/// <param name="id"></param>
public void DeleteNotification(int id)
{
Intent intent = new Intent(AndroidApp.Context, typeof(AlarmHandler));
PendingIntent pendingIntent = PendingIntent.GetBroadcast(
AndroidApp.Context,
id, intent,
PendingIntentFlags.CancelCurrent | PendingIntentFlags.Immutable);
AlarmManager alarmManager = AndroidApp.Context.GetSystemService(Context.AlarmService) as AlarmManager;
alarmManager.Cancel(pendingIntent);
}
private Intent CreateIntent(int id)
{
return new Intent(Android.App.Application.Context, typeof(AlarmHandler)).SetAction("LocalNotifierIntent" + id);
}
public void CancelAlarm(int id)
{
var intent = CreateIntent(id);
var pendingIntent = PendingIntent.GetBroadcast(
Android.App.Application.Context,
0,
intent,
PendingIntentFlags.UpdateCurrent | PendingIntentFlags.Immutable);
var alarmManager = GetAlarmManager();
alarmManager.Cancel(pendingIntent);
var notificationManager = NotificationManagerCompat.From(Android.App.Application.Context);
notificationManager.CancelAll();
notificationManager.Cancel(id);
}
private AlarmManager GetAlarmManager()
{
var alarmManager = Android.App.Application.Context.GetSystemService(Context.AlarmService) as AlarmManager;
return alarmManager;
}
#endregion
}
}
BootReciever.cs
using Android.App;
using Android.Content;
using MyApp.Notifications;
using MyApp.Utils;
using System;
using Xamarin.Forms;
namespace MyApp.Droid.Notifications
{
/// <summary>
/// Used to reschedule notifications on boot. Android notifications are deleted when the OS is rebooted.
/// </summary>
[BroadcastReceiver(
Enabled = true,
Label = "Reboot complete receiver",
Exported = true)]
[IntentFilter(new[] { Android.Content.Intent.ActionBootCompleted })]
public class BootReceiver : BroadcastReceiver
{
/// <summary>
/// Called on boot.
/// </summary>
/// <param name="context"></param>
/// <param name="intent"></param>
public override void OnReceive(Context context, Intent intent)
{
//if (intent.Action == "android.intent.action.BOOT_COMPLETED")
//{
// ScheduleDailyNotification();
//}
ScheduleDailyNotification();
}
/// <summary>
/// Schedule the daily notification.
/// </summary>
private void ScheduleDailyNotification()
{
if (Settings.DailyNotificationEnabled)
{
var notificationManager = DependencyService.Get<INotificationManager>();
var notificationDateTime = NotificationDateTime.GetDailyNotificationDateTime();
notificationManager.SendNotification(
Global.Constants.NotificationOffllineTitle,
Global.Constants.NotificationOffllineMessage,
Global.Constants.DailyNotificationId,
//DateTime.Now.AddSeconds(60));
notificationDateTime);
}
}
}
}
I hate to delete a question from the internet.
The issue on boot received was using dependency injection in the boot receiver. Exception: You must call Xamarin.Forms.Forms.Init(); prior to using this property. I just instantiated the class instead of using DI for this instance.
Here is what worked for me on the issue with the notification not showing when the app was closed: https://stackoverflow.com/a/60197247/814891
After registering my BroadcastReceiver (BR) statically in the
manifest, applying the proper intent filters, using JobIntentService
(and registering it in the manifest) to handle the work that was
called from my BR, I was still getting inconsistent results.
Once all of what I listed above has been done you should be able to
send an ADB command to activate your broadcast and process the work in
your service even if the app is closed. This was working for me some
of the time, but not all of the time.
This article describes limitation to BRs. "As of Android 3.1 the
Android system excludes all receiver from receiving intents by default
if the corresponding application has never been started by the user or
if the user explicitly stopped the application via the Android menu"
(AKA a user executes Force Stop)
When I start the app by debugging it, then swipe it closed on my
device, my ADB command never activates my BR. However, after my
debugging session is over, when I open up the app on my device and
swipe it closed, I can activate my BR through ADB commands.
This occurs because when you debug an application, then manually swipe
it closed on the device, Android considers this a Force Stop hence why
my BR cannot be activated until I re-open the app on the device
without debugging.
Scoured the internet for hours, and wasn't able to find my solution,
so I thought I'd post it here just in case some poor unfortunate soul
is encountering the same weird functionality I was.
Happy coding :)
I am developing an MVC 5 application that uses Ninject to handle dependency injection. The application defines a SecurityService that provided various information about current logged user. I am using Windows Authentication.
Ok, let's dive into the code.
NinjectWebCommon.cs
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
private static KernelBase kernel;
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
kernel = new StandardKernel();
try
{
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
return kernel;
}
catch
{
kernel.Dispose();
throw;
}
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ISecurityService>().To<SecurityService>().InRequestScope();
// custom bindings are defined here
}
public static void PerformInjectionOn(object instance)
{
kernel.Inject(instance);
}
Please, notice kernel.Bind<ISecurityService>().To<SecurityService>().InRequestScope(); for security binding definition.
SecurityService.cs
private AppUser _CurrentUser = null;
/// <summary>
/// gets logged user data, based on current identity username (Sam account name)
/// </summary>
/// <returns>AppUser object if windows identity maps to an existing active user. Otherwise null</returns>
public AppUser GetLoggedUserData()
{
lock(lockObj)
{
String currUsername = WindowsIdentity.GetCurrent().Name;
// comparison between current user name and actually authenticated user is needed since some requests end with different values!
if (_CurrentUser == null || !_CurrentUser.Username.Equals(currUsername))
{
_CurrentUser = _ScopedDataAccess.AppUserRepository.AllNoTracking
// some includes
.SingleOrDefault(u => u.IsEnabled && u.Username.Equals(currUsername));
if (_CurrentUser == null)
{
logger.LogEx(LogLevel.Info, "GetLoggedUserData - user {0} authentication failed", currUsername);
return null;
}
}
return _CurrentUser;
}
}
My problem is that, even if SecurityService is instantiated per request, sometimes I receive an instance where _CurrentUser.Username is different from currUsername (i.e. both are valid A/D users with which I perform the test).
Current workaround is to have !_CurrentUser.Username.Equals(currUsername) to invalidate cached user instance, if request authenticated user is different from cached one, but I would like to know what is happening.
Just out of curiosity, I have checked InThreadScope and had the same problem, but I think this can be explained by the fact that the thread pool used by IIS may provide the same thread for another request.
Does anyone know why InRequestScope behaves like this?
Thanks.
[edit]
Call stack when current user is different from cached one:
ProjectName.Models.dll!ProjectName.Models.SecurityService.GetLoggedUserData() Line 54 C#
ProjectName.Models.dll!ProjectName.Models.SecurityService.GetAndCheckUserData() Line 76 C#
ProjectName.Models.dll!ProjectName.Models.SecurityService.IsAdmin.get() Line 98 C#
ProjectName.Models.dll!ProjectName.Models.EntitiesCache.ProjectStatuses.get() Line 51 C#
ProjectName.Services.dll!ProjectName.Services.ProjectService.CreateSelectorsDomain() Line 253 C#
ProjectName.Services.dll!ProjectName.Services.ProjectService.ProjectService(ProjectName.Models.ISecurityService securityService, ProjectName.Models.IEntitiesCache entitiesCache, ProjectName.Models.IScopedDataAccess dataAccess, ProjectName.Services.IProjectTemplateService projectTemplateService) Line 33 C#
[External Code]
ProjectName.Web.dll!ProjectName.Web.NinjectWebCommon.PerformInjectionOn(object instance) Line 93 C#
ProjectName.Web.dll!ProjectName.Web.BaseController.BaseController() Line 21 C#
[External Code]
The logic in all steps in synchronous (no async, await, no Tasks)
I have a set of integration tests executed by MsTest around OWIN, using the OWIN startup method for self host.
The tests are quite simple and use the following pattern:
WebApp.Start<Startup>(url: appAddress);
HttpClient client = new HttpClient();
client.DefaultRequestHeaders
.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = client.GetAsync(uri).Result;
When I run the test on our local DEV machines, they are green.
When we run the test from Visual Studio inside our Build Machine, they are green.
If they run from the queue, few days ago, we started to get this annoying error from OWIN:
*** OWIN STARTED ***
{"Message":"An error has occurred.",
"ExceptionMessage":"Multiple types were found that match the controller named 'xxx'.
This can happen if the route that services this request ('odata/v1/{*odataPath}') found multiple controllers defined with the same name but differing namespaces, which is not supported.
The request for 'xxx' has found the following matching controllers:
namespace.V1.Controllers.xxxController
namespace.V1.Controllers.xxxController",
"ExceptionType":"System.InvalidOperationException","StackTrace":" at System.Web.Http.Dispatcher.DefaultHttpControllerSelector.SelectController(HttpRequestMessage request)
at System.Web.Http.Dispatcher.HttpControllerDispatcher.<SendAsync>d__1.MoveNext()"}
And this is strange because we use Windsor to resolve the controllers and we also print out the registrations in Windsor and of course there is only one controller with that name. We have this issue on all the controllers only if the code is executed within MsBuild ... Inside Visual Studio of the Build Machine or in production, it works.
Could be that the error is something else but swallowed by OWIN?
I was getting the same error and In my case it was a combination of factors:
I'm using Nunit and Owin for selfhosting.
In the setup of the tests I start web server:
_server = WebApp.Start<Startup>(new StartOptions(baseAddress));
In the teardown of the tests I kill it:
_server.Dispose();
One of my tests was using TestCaseSource which is executed before the setup:
[TestCaseSource(typeof(TestData), "TestDataSource")]
public void Test_With_Source(TestData testcase)
Down the line, inside TestData.TestDataSource I was loading the assembly that contains the controllers to get some route information:
loadedAssembly = Assembly.LoadFile(dll); //First load of the assembly
At the point where the the web server was started (on the setup) the assembly was loaded once already (by the TestCaseSource) so I end up with the same assembly twice and web api complaining about a duplication of the same controller.
My fix was to remove the Assembly.LoadFile(dll) by:
typeof(BaseController).Assembly
That made the assembly containing the BaseController not been loaded twice.
We solved the problem in our tests by hosting the OWIN project within IIS express
public class IisExpressRunner : IDisposable
{
/// <summary>
/// Stores whether this instance has been disposed.
/// </summary>
private Boolean _isDisposed;
/// <summary>
/// Stores the IIS Express process.
/// </summary>
private Process _process;
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Starts IIS Express using the specified directory path and port.
/// </summary>
/// <param name="configPath">
/// The directory path.
/// </param>
/// <param name="siteName">
/// The port.
/// </param>
public void Start(String configPath, string siteName)
{
String iisExpressPath = DetermineIisExpressPath();
String arguments = String.Format(
CultureInfo.InvariantCulture, "/config:\"{0}\" /site:{1}", configPath, siteName);
ProcessStartInfo info = new ProcessStartInfo(iisExpressPath)
{
WindowStyle = ProcessWindowStyle.Hidden,
ErrorDialog = true,
LoadUserProfile = true,
CreateNoWindow = false,
UseShellExecute = false,
Arguments = arguments
};
Thread startThread = new Thread(() => StartIisExpress(info))
{
IsBackground = true
};
startThread.Start();
}
/// <summary>
/// Releases unmanaged and - optionally - managed resources.
/// </summary>
/// <param name="disposing">
/// <c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.
/// </param>
protected virtual void Dispose(Boolean disposing)
{
if (_isDisposed)
{
return;
}
if (disposing)
{
// Free managed resources
if (_process.HasExited == false)
{
_process.CloseMainWindow();
}
_process.Kill();
_process = null;
}
// Free native resources if there are any
_isDisposed = true;
}
/// <summary>
/// Determines the IIS express path.
/// </summary>
/// <returns>
/// A <see cref="String"/> instance.
/// </returns>
private static String DetermineIisExpressPath()
{
String iisExpressPath;
if (Environment.Is64BitOperatingSystem)
{
iisExpressPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
}
else
{
iisExpressPath = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
}
iisExpressPath = Path.Combine(iisExpressPath, #"IIS Express\iisexpress.exe");
return iisExpressPath;
}
/// <summary>
/// Starts the IIS express.
/// </summary>
/// <param name="info">
/// The info.
/// </param>
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
Justification = "Required here to ensure that the instance is disposed.")]
private void StartIisExpress(ProcessStartInfo info)
{
try
{
_process = Process.Start(info);
_process.WaitForExit();
}
catch (Exception)
{
Dispose();
}
}
}
I'm trying to build a UserControl for a Windows Phone app using Caliburn Micro inside the control. The bootstrapper is usually setup in the App resources
<Application.Resources>
<local:AppBootstrapper x:Key="bootstrapper" />
</Application.Resources>
I try to do this in the Xaml of the user control.
<UserControl.Resources>
<local:AppBootstrapper x:Key="bootstrapper" />
</UserControl.Resources>
But this throws an exception during initialization when the component is loaded. The LoadComponent call throws the exception: "A first chance exception of type 'System.InvalidOperationException' occurred in Microsoft.Phone.ni.dll"
Where and when should the bootstrapper be initialised?
It's not intended to be used in a UserControl's resources, so I can't guarantee any good or bad behavior. The bootstrapper should be used in the application resources or you can instantiate it directly in code. Try creating it in the user control's ctor, just after the InitializeComponent call.
Since you are placing a bootstrapper on a UserControl it seems that the PhoneApplicationService is probably already instantiated - have you tried putting the boostrapper in the app resources section?
The CM source shows that CM creates a new instance of PhoneApplicationService when the bootstrapper initialises and that looks like the issue, the root of your application must have already created an instance.
Is there a reason you can't use the boostrapper in the standard way (in app resources)? Does your App.xaml contain an initialiser for PhoneApplicationService?
Edit:
CM source where you are getting the error is in PrepareApplication. e.g.
protected override void PrepareApplication() {
base.PrepareApplication();
phoneService = new PhoneApplicationService();
phoneService.Activated += OnActivate;
phoneService.Deactivated += OnDeactivate;
phoneService.Launching += OnLaunch;
phoneService.Closing += OnClose;
Application.ApplicationLifetimeObjects.Add(phoneService);
if (phoneApplicationInitialized) {
return;
}
RootFrame = CreatePhoneApplicationFrame();
RootFrame.Navigated += OnNavigated;
phoneApplicationInitialized = true;
}
You could probably just subclass this and get away with not creating a new PhoneApplicationService but reusing the existing one:
/// <summary>
/// A custom bootstrapper designed to setup phone applications.
/// </summary>
public class CustomPhoneBootstrapper : Bootstrapper {
bool phoneApplicationInitialized;
PhoneApplicationService phoneService;
/// <summary>
/// The root frame used for navigation.
/// </summary>
public PhoneApplicationFrame RootFrame { get; private set; }
/// <summary>
/// Provides an opportunity to hook into the application object.
/// </summary>
protected override void PrepareApplication(PhoneApplicationService phoneAppService, PhoneApplicationFrame rootFrame) {
base.PrepareApplication();
phoneService = phoneAppService;
phoneService.Activated += OnActivate;
phoneService.Deactivated += OnDeactivate;
phoneService.Launching += OnLaunch;
phoneService.Closing += OnClose;
Application.ApplicationLifetimeObjects.Add(phoneService);
if (phoneApplicationInitialized) {
return;
}
RootFrame = rootFrame;
RootFrame.Navigated += OnNavigated;
phoneApplicationInitialized = true;
}
void OnNavigated(object sender, NavigationEventArgs e) {
if (Application.RootVisual != RootFrame) {
Application.RootVisual = RootFrame;
}
}
/// <summary>
/// Occurs when a fresh instance of the application is launching.
/// </summary>
protected virtual void OnLaunch(object sender, LaunchingEventArgs e) { }
/// <summary>
/// Occurs when a previously tombstoned or paused application is resurrected/resumed.
/// </summary>
protected virtual void OnActivate(object sender, ActivatedEventArgs e) { }
/// <summary>
/// Occurs when the application is being tombstoned or paused.
/// </summary>
protected virtual void OnDeactivate(object sender, DeactivatedEventArgs e) { }
/// <summary>
/// Occurs when the application is closing.
/// </summary>
protected virtual void OnClose(object sender, ClosingEventArgs e) { }
}
Disclaimer: Not sure if any of this will work since I've never devved for Windows Phone but as far as I can see that should do what the original bootstrapper did but skip the creation of the application and the root frame. You just need to supply the app and the root frame (or maybe just the app if you can get the root frame from the application object - like I said, no idea what's possible)
I am learning ASP.NE4 MVC3. I have created a NinjectDependencyResolver class, but I want to know how I would go about implementing the ServiceLocator class. Currently I get this error "The type SportsStore.WebUI.Infrastructure.NinjectDependencyResolver does not appear to implement Microsoft.Practices.ServiceLocation.IServiceLocator.
Parameter name: commonServiceLocator".
Global.asax
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
RegisterDependencyResolver();
//ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
}
private void RegisterDependencyResolver()
{
var kernel = new StandardKernel();
DependencyResolver.SetResolver(new NinjectDependencyResolver(kernel));
}
NinjectDepencyResolver cs
public class NinjectDependencyResolver
{
private readonly IKernel _kernel;
public NinjectDependencyResolver(IKernel kernel)
{
_kernel = kernel;
}
public object GetService(Type serviceType)
{
return _kernel.TryGet(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
try
{
return _kernel.GetAll(serviceType);
}
catch (Exception)
{
return new List<object>();
}
}
Your NinjectDependencyResolver must inherit from IDependencyResolver so your code should look like this:
public class NinjectDependencyResolver : IDependencyResolver
I would not do it like that. For one thing, Mark Seemann's book "Dependency Injection in .NET" clearly shows that Service Locator is actually an anti-pattern.
At any rate try not to bloat your global.asax file
If you instead used Nuget and got the latest version of NinjectMVC3 , you should end up with a clean Application_Start method
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
However, if you want to you can add in this line into the end of that method as I believe this is what Adam and Steve do in the Sportstore application in the Apress MVC3 book.
ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());
Since that book was released, Ninject released newer versions that make it much easier, in fact I would guarantee that the Apress MVC4 book that ends up coming out will show the simpler way. The simple way is use nuget and get NinjectMVC3 , then it will have an App_Start folder which will run the files in them at start of the application.
Here is an example of it with some bindings
using Products.Data.Abstract;
using Products.Data.Concrete;
using Products.Data.Infrastructure;
[assembly: WebActivator.PreApplicationStartMethod(typeof(ProductsWeb.App_Start.NinjectMVC3), "Start")]
[assembly: WebActivator.ApplicationShutdownMethodAttribute(typeof(ProductsWeb.App_Start.NinjectMVC3), "Stop")]
namespace ProductsWeb.App_Start
{
using System.Reflection;
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
using Ninject;
using Ninject.Web.Mvc;
public static class NinjectMVC3
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestModule));
DynamicModuleUtility.RegisterModule(typeof(HttpApplicationInitializationModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
RegisterServices(kernel);
return kernel;
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<IProductsRepository>().To<FakeProductsRepository>();
kernel.Bind<MovieRepository>().To<MovieRepository>();
}
}
}
Why not just use the official MVC Integration extension for Ninject, and the Common Service Locator implementation that comes in the official main distribution of Ninject (the dll is included in the build downloads)?