I want running this method UpdateStatus when he close app
It is my coding method UpdateStatus in android:
String id = "";
var id = Application.Current.Properties["Id"].ToString();
User user = new User(id);
user.Id = id;
user.Datetime = time;
var responseStatus = await api.UpdateStatus(new UpdateStatusQuery(user));
Could you help me ?
In the Android, When you close your applcation, based on my research, above code can not be run, because all of threads or services will be killed when applcation is killed.
If you want to run above code when application in the background, you can can use background service to achieve that, due to background execution limits in Android 8.0 or later, if you code need some time to execute, and you want code running stably, Foreground Services is a good choice.
In xamarin forms, you can use dependenceService in the OnSleep method of App.xaml.cs
OnSleep - called each time the application goes to the background.
You can create a interface.
IService.cs
public interface IService
{
void Start();
}
Then achieved DependentService to start a Foreground Service.
[assembly: Xamarin.Forms.Dependency(typeof(DependentService))]
namespace TabGuesture.Droid
{
[Service]
public class DependentService : Service, IService
{
public void Start()
{
var intent = new Intent(Android.App.Application.Context,
typeof(DependentService));
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
{
Android.App.Application.Context.StartForegroundService(intent);
}
else
{
Android.App.Application.Context.StartService(intent);
}
}
public override IBinder OnBind(Intent intent)
{
return null;
}
public const int SERVICE_RUNNING_NOTIFICATION_ID = 10000;
public override StartCommandResult OnStartCommand(Intent intent,
StartCommandFlags flags, int startId)
{
// From shared code or in your PCL
CreateNotificationChannel();
string messageBody = "service starting";
var notification = new Notification.Builder(this, "10111")
.SetContentTitle(Resources.GetString(Resource.String.app_name))
.SetContentText(messageBody)
.SetSmallIcon(Resource.Drawable.main)
.SetOngoing(true)
.Build();
StartForeground(SERVICE_RUNNING_NOTIFICATION_ID, notification);
//==============================do you work=====================
String id = "";
var id = Application.Current.Properties["Id"].ToString();
User user = new User(id);
user.Id = id;
user.Datetime = time;
var responseStatus = await api.UpdateStatus(new UpdateStatusQuery(user));
return StartCommandResult.Sticky;
}
void CreateNotificationChannel()
{
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
{
// Notification channels are new in API 26 (and not a part of the
// support library). There is no need to create a notification
// channel on older versions of Android.
return;
}
var channelName = Resources.GetString(Resource.String.channel_name);
var channelDescription = GetString(Resource.String.channel_description);
var channel = new NotificationChannel("10111", channelName, NotificationImportance.Default)
{
Description = channelDescription
};
var notificationManager = (NotificationManager)GetSystemService(NotificationService);
notificationManager.CreateNotificationChannel(channel);
}
}
}
Here is similar thread:
How to create service doing work at period time in Xamarin.Forms?
Related
We are developing android widget for Xamarin.Forms application. The widget updates and gets data from the Web Service when the app is in Background, but stops working when the app is killed/closed. I have followed this article for developing this widget -
Xamarin: Android Widget with timer, stops when app killed
I want to Update the widget when the user clicks on Refresh button. If I add hardcoded data for textboxes and click Refresh it updates the time but doesn’t work if I assign web service result data for the textboxes. I have added internet permission in AndroidManifest.xml. Is there a way I can get the data from web service even when the app is closed? Or Probably I am missing some permission?
AppWidget.cs -
public static class WidgetConsts
{
public const string DebugTag = "com.myapp.WIDGET";
public const string ActionWakeup = "com.myapp.WIDGET_WAKEUP";
public const string ActionWidgetUpdate = "android.appwidget.action.APPWIDGET_UPDATE";
public const string ActionWidgetDisabled = "android.appwidget.action.APPWIDGET_DISABLED";
}
[BroadcastReceiver]
[IntentFilter(new string[] { WidgetConsts.ActionWakeup })]
public class AlarmReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
if (intent.Action.Equals(WidgetConsts.ActionWakeup))
{
Log.Debug(WidgetConsts.DebugTag, "Wakeup alarm called");
if (AppWidget.widgetTimer == null)
{
Log.Debug(WidgetConsts.DebugTag, "Widget updating does not run, enforcing update...");
AppWidget.UpdateAppWidget(context);
}
else
{
Log.Debug(WidgetConsts.DebugTag, "Widget updating runs, no action needed");
}
}
}
}
[BroadcastReceiver]
[IntentFilter(new string[] { WidgetConsts.ActionWidgetUpdate})]
[MetaData("android.appwidget.provider", Resource = "#xml/appwidget_provider")]
public class AppWidget : AppWidgetProvider
{
public static System.Timers.Timer widgetTimer = null;
public override void OnUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
RemoteViews views = BuildRemoteViews(context, appWidgetIds);
(AppWidgetManager.GetInstance(Android.App.Application.Context)).UpdateAppWidget(new ComponentName(Android.App.Application.Context, Java.Lang.Class.FromType(typeof(AppWidget))), views);
// appWidgetManager.UpdateAppWidget(appWidgetIds[0], views);
// set timer for updating the widget views each 5 sec
if (widgetTimer == null)
{
widgetTimer = new System.Timers.Timer();
widgetTimer.Interval = 5000;
widgetTimer.Elapsed += OnTimedEvent;
}
widgetTimer.Enabled = true;
// set alarm to wake up the app when killed, each 60 sec
// needs a fresh BroadcastReceiver because AppWidgetProvider.OnReceive is
// not virtual and overriden method in this class would not be called
AlarmManager am = (AlarmManager)context.GetSystemService(Context.AlarmService);
Intent ai = new Intent(context, typeof(AlarmReceiver));
ai.SetAction(WidgetConsts.ActionWakeup);
PendingIntent pi = PendingIntent.GetBroadcast(context, 0, ai, PendingIntentFlags.UpdateCurrent);
am.SetRepeating(AlarmType.ElapsedRealtime, 100, 1000 * 60, pi);
}
public override void OnEnabled(Context context)
{
AlarmManager am = (AlarmManager)context.GetSystemService(Context.AlarmService);
Intent ai = new Intent(context, typeof(AlarmReceiver));
ai.SetAction(WidgetConsts.ActionWakeup);
PendingIntent pi = PendingIntent.GetBroadcast(context, 0, ai, PendingIntentFlags.UpdateCurrent);
am.SetRepeating(AlarmType.ElapsedRealtime, 100, 1000 * 60, pi);
base.OnEnabled(context);
}
public override void OnDisabled(Context context)
{
Log.Debug(WidgetConsts.DebugTag, "Disabling the widget");
if (widgetTimer != null)
{
Log.Debug(WidgetConsts.DebugTag, "Stopping timer");
widgetTimer.Enabled = false;
}
else
Log.Debug(WidgetConsts.DebugTag, "Timer is null");
base.OnDisabled(context);
}
private void OnTimedEvent(object sender, ElapsedEventArgs e)
{
Log.Debug(WidgetConsts.DebugTag, "Updating status...");
new Handler(Looper.MainLooper).Post(() =>
{
//Run my code to periodically update the widget
RemoteViews views = new RemoteViews(Android.App.Application.Context.PackageName, Resource.Layout.SnapVertWidget);
AppWidgetManager manager = AppWidgetManager.GetInstance(Android.App.Application.Context);
ComponentName thisWidget = new ComponentName(Android.App.Application.Context, Java.Lang.Class.FromType(typeof(AppWidget)));
int[] appWidgetIds = manager.GetAppWidgetIds(thisWidget);
(AppWidgetManager.GetInstance(Android.App.Application.Context)).UpdateAppWidget(new ComponentName(Android.App.Application.Context, Java.Lang.Class.FromType(typeof(AppWidget))), views);
// manager.UpdateAppWidget(appWidgetIds[0], views);
});
}
static public void UpdateAppWidget(Context context)
{
Intent intent = new Intent(context, typeof(AppWidget));
intent.SetAction(WidgetConsts.ActionWidgetUpdate);
int[] ids = AppWidgetManager.GetInstance(context).GetAppWidgetIds(new ComponentName(context, Java.Lang.Class.FromType(typeof(AppWidget))));
intent.PutExtra(AppWidgetManager.ExtraAppwidgetIds, ids);
context.SendBroadcast(intent);
}
public RemoteViews BuildRemoteViews(Context context, int[] appWidgetIds)
{
xxx.Droid.Services.MyWidget myWidget = new xxx.Droid.Services.MyWidget();
var entry = myWidget.GetData();
// Build an update that holds the updated widget contents
var updateViews = new RemoteViews(context.PackageName, Resource.Layout.SnapVertWidget);
updateViews.SetTextViewText(Resource.Id.txtvwUpdate, Convert.ToString(DateTime.Now));
updateViews.SetTextViewText(Resource.Id.txtvwCityName, entry.Result.CityName);
updateViews.SetTextViewText(Resource.Id.txtvwTemp, entry.Result.TempValue);
//SetTextViewText(widgetView);
RegisterClicks(context, appWidgetIds, updateViews);
return updateViews;
}
private void RegisterClicks(Context context, int[] appWidgetIds, RemoteViews widgetView)
{
Intent intentUpdate = new Intent(context, typeof(AppWidget));
intentUpdate.SetAction(AppWidgetManager.ActionAppwidgetUpdate);
//Update the current widget instance only, by creating an array that contains the widget’s unique ID//
int[] idArray = new int[] { appWidgetIds[0] };
intentUpdate.PutExtra(AppWidgetManager.ExtraAppwidgetIds, idArray);
PendingIntent pendingUpdate = PendingIntent.GetBroadcast(
context, appWidgetIds[0], intentUpdate,
PendingIntentFlags.UpdateCurrent);
widgetView.SetOnClickPendingIntent(Resource.Id.btnRefresh, pendingUpdate);
Intent launchAppIntent = new Intent(context, typeof(MainActivity));
PendingIntent launchAppPendingIntent = PendingIntent.GetActivity(context, 0, launchAppIntent, PendingIntentFlags.UpdateCurrent);
widgetView.SetOnClickPendingIntent(Resource.Id.pnlWeather, launchAppPendingIntent);
}
}
I need to get sitecore information that collected from anonymous users to give him availability to export it or opt out - [GDPR]
any idea about contact ID for anonymous !
The way of doing it is dependent on the sitecore version.
Sitcore 9 you can use right to be forgotten
Sitecore 8+ you have to implement the feature from scratch.
Regarding Anonymous user - If the user is really anonymous, then you done need to worry about the GDPR (my view). But sometimes we map user email and sensitive personal info to anonymous user by using forms or WFFM. You can use email address of that user to query xDB (Contact Identifiers) to get the contact and contactID. Then reset informations.
Also: please note that based on WFFFM save action config, anonymous user will store in Core DB and Contact List.
To forget a user, you can use the following code. It will execute the ExecuteRightToBeForgotten function on the contact and scrub their data.
Forget User
public bool ForgetUser()
{
var id = _contactIdentificationRepository.GetContactId();
if (id == null)
{
return false;
}
var contactReference = new IdentifiedContactReference(id.Source, id.Identifier);
using (var client = _contactIdentificationRepository.CreateContext())
{
var contact = client.Get(contactReference, new ContactExpandOptions());
if (contact != null)
{
client.ExecuteRightToBeForgotten(contact);
client.Submit();
}
}
return false;
}
Fake up some data
public void FakeUserInfo()
{
var contactReference = _contactIdentificationRepository.GetContactReference();
using (var client = SitecoreXConnectClientConfiguration.GetClient())
{
// we can have 1 to many facets
// PersonalInformation.DefaultFacetKey
// EmailAddressList.DefaultFacetKey
// Avatar.DefaultFacetKey
// PhoneNumberList.DefaultFacetKey
// AddressList.DefaultFacetKey
// plus custom ones
var facets = new List<string> { PersonalInformation.DefaultFacetKey };
// get the contact
var contact = client.Get(contactReference, new ContactExpandOptions(facets.ToArray()));
// pull the facet from the contact (if it exists)
var facet = contact.GetFacet<PersonalInformation>(PersonalInformation.DefaultFacetKey);
// if it exists, change it, else make a new one
if (facet != null)
{
facet.FirstName = $"Myrtle-{DateTime.Now.Date.ToString(CultureInfo.InvariantCulture)}";
facet.LastName = $"McSitecore-{DateTime.Now.Date.ToString(CultureInfo.InvariantCulture)}";
// set the facet on the client connection
client.SetFacet(contact, PersonalInformation.DefaultFacetKey, facet);
}
else
{
// make a new one
var personalInfoFacet = new PersonalInformation()
{
FirstName = "Myrtle",
LastName = "McSitecore"
};
// set the facet on the client connection
client.SetFacet(contact, PersonalInformation.DefaultFacetKey, personalInfoFacet);
}
if (contact != null)
{
// submit the changes to xConnect
client.Submit();
// reset the contact
_contactIdentificationRepository.Manager.RemoveFromSession(Analytics.Tracker.Current.Contact.ContactId);
Analytics.Tracker.Current.Session.Contact = _contactIdentificationRepository.Manager.LoadContact(Analytics.Tracker.Current.Contact.ContactId);
}
}
}
ContactIdentificationRepository
using System.Linq;
using Sitecore.Analytics;
using Sitecore.Analytics.Model;
using Sitecore.Analytics.Tracking;
using Sitecore.Configuration;
using Sitecore.XConnect;
using Sitecore.XConnect.Client.Configuration;
namespace Sitecore.Foundation.Accounts.Repositories
{
public class ContactIdentificationRepository
{
private readonly ContactManager contactManager;
public ContactManager Manager => contactManager;
public ContactIdentificationRepository()
{
contactManager = Factory.CreateObject("tracking/contactManager", true) as ContactManager;
}
public IdentifiedContactReference GetContactReference()
{
// get the contact id from the current contact
var id = GetContactId();
// if the contact is new or has no identifiers
var anon = Tracker.Current.Contact.IsNew || Tracker.Current.Contact.Identifiers.Count == 0;
// if the user is anon, get the xD.Tracker identifier, else get the one we found
return anon
? new IdentifiedContactReference(Sitecore.Analytics.XConnect.DataAccess.Constants.IdentifierSource, Tracker.Current.Contact.ContactId.ToString("N"))
: new IdentifiedContactReference(id.Source, id.Identifier);
}
public Analytics.Model.Entities.ContactIdentifier GetContactId()
{
if (Tracker.Current?.Contact == null)
{
return null;
}
if (Tracker.Current.Contact.IsNew)
{
// write the contact to xConnect so we can work with it
this.SaveContact();
}
return Tracker.Current.Contact.Identifiers.FirstOrDefault();
}
public void SaveContact()
{
// we need the contract to be saved to xConnect. It is only in session now
Tracker.Current.Contact.ContactSaveMode = ContactSaveMode.AlwaysSave;
this.contactManager.SaveContactToCollectionDb(Tracker.Current.Contact);
}
public IXdbContext CreateContext()
{
return SitecoreXConnectClientConfiguration.GetClient();
}
}
}
I'm using the free version of Xamarin Studio to do some test. In aprticular I'm trying to connect to web Service but I receive the ConnectFailure (Network unreachable) when I debug the application.
This is my code
public class MainActivity : Activity
{
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
// Set our view from the "main" layout resource
SetContentView (Resource.Layout.Main);
// Our code will go here
// Get our UI controls from the loaded layout
EditText phoneNumberText = FindViewById<EditText>(Resource.Id.editText1);
Button translateButton = FindViewById<Button>(Resource.Id.button1);
Button callButton = FindViewById<Button>(Resource.Id.button2);
// Disable the "Call" button
callButton.Enabled = false;
// Add code to translate number
string translatedNumber = string.Empty;
var rxcui = "198440";
var request = HttpWebRequest.Create(string.Format(#"http://rxnav.nlm.nih.gov/REST/RxTerms/rxcui/{0}/allinfo", rxcui));
request.ContentType = "application/json";
request.Method = "GET";
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
if (response.StatusCode != HttpStatusCode.OK)
Console.Out.WriteLine("Error fetching data. Server returned status code: {0}", response.StatusCode);
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
var content = reader.ReadToEnd();
if(string.IsNullOrWhiteSpace(content)) {
Console.Out.WriteLine("Response contained empty body...");
}
else {
Console.Out.WriteLine("Response Body: \r\n {0}", content);
}
//Assert.NotNull(content);
}
}
}
}
}
I don't understand if this is an error caused by the free version or it's me.
Please help me.
Thanks
I did the following and got the below error msg:
The error message :
An exception of type 'System.AggregateException' occurred in mscorlib.dll but was not handled in user code
Additional information: One or more errors occurred.
If there is a handler for this exception, the program may be safely continued.
Question :
a) What seems to be the problems in above code as I just wanted to retrieve a record.
b) Must use Async Methods in WinRT or Windows store app?
c) Will below code able to retrieve record from Navision?
-----1------- Windows store App to access Nav Web Services
1.1 Added the service reference in WinRT App
1.2 Added a class1.cs in WinRT App
private async void btnImportCustomer_Click(object sender, RoutedEventArgs e)
{
Task _asyncCustomer = Class1.Customer.Listing.GetAsyncRecords("Y007");
### encounterd error here: ####
string g_strmsg = _asyncCustomer.Result.No + " “ +_asyncCustomer.Result.Name;
}
-----2---------- Class1.cs use inside WinRT App Project:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MobileNAVSalesSystem
{
class Class1
{
public static string _webserviceurlpage = "http ://{0}:{1}/{2}/WS/{3}/Page/{4}";
public static string _webserviceurlcodeunit = "http://{0}:{1}/{2}/WS/{3}/Codeunit/{4}";
public static Uri _webserviceuripage = null;
public static Uri _webserviceuricodeunit = null;
#region Customer
public class Customer
{
public class Card
{
//Do something for Card Type
}
public class Listing
{
public static wsCustomerList.Customer_List_PortClient GetService()
{
_webserviceuripage = new Uri(string.Format(_webserviceurlpage, "msxxx", "7047", "DynamicsNAV_xxx", Uri.EscapeDataString("Global xxx Pte. Ltd."), "Customer List"));
System.ServiceModel.BasicHttpBinding _wSBinding = new System.ServiceModel.BasicHttpBinding();
_wSBinding.Security.Mode = System.ServiceModel.BasicHttpSecurityMode.TransportCredentialOnly;
_wSBinding.Security.Transport.ClientCredentialType = System.ServiceModel.HttpClientCredentialType.Windows;
_wSBinding.MaxBufferSize = Int32.MaxValue;
_wSBinding.MaxReceivedMessageSize = Int32.MaxValue;
//_wSBinding.UseDefaultWebProxy = false;
wsCustomerList.Customer_List_PortClient _ws = new wsCustomerList.Customer_List_PortClient(_wSBinding, new System.ServiceModel.EndpointAddress(_webserviceuripage));
_ws.ClientCredentials.Windows.AllowedImpersonationLevel = System.Security.Principal.TokenImpersonationLevel.Delegation;
_ws.ClientCredentials.Windows.ClientCredential = new System.Net.NetworkCredential("xxx","xxxx", "companyName");
return _ws;
}
//-------------------------- Using Async Methods
public static async Task GetAsyncRecords(string _No)
{
wsCustomerList.Customer_List_PortClient _ws = GetService();
wsCustomerList.Customer_List _List = (await _ws.ReadAsync(_No)).Customer_List;
if (_ws.State == System.ServiceModel.CommunicationState.Opened)
await _ws.CloseAsync();
return _List;
}
public static async Task GetAsyncRecords(wsCustomerList.Customer_List_Filter[] _filters)
{
wsCustomerList.Customer_List_PortClient _ws = GetService();
wsCustomerList.Customer_List[] _List;
List _filterArray = new List();
_filterArray.AddRange(_filters);
_List = (await _ws.ReadMultipleAsync(_filterArray.ToArray(), null, 0)).ReadMultiple_Result1;
if (_ws.State == System.ServiceModel.CommunicationState.Opened)
await _ws.CloseAsync();
return _List;
}
public static async Task GetAsyncRecords(wsCustomerList.Customer_List_Filter[] _filters, string _bookmarkkey)
{
wsCustomerList.Customer_List_PortClient _ws = GetService();
wsCustomerList.Customer_List[] _List;
List _filterArray = new List();
_filterArray.AddRange(_filters);
_List = (await _ws.ReadMultipleAsync(_filterArray.ToArray(), _bookmarkkey, 0)).ReadMultiple_Result1;
if (_ws.State == System.ServiceModel.CommunicationState.Opened)
await _ws.CloseAsync();
return _List;
}
public static async Task GetAsyncRecords(wsCustomerList.Customer_List_Filter[] _filters, string _bookmarkkey, int _setsize)
{
wsCustomerList.Customer_List_PortClient _ws = GetService();
wsCustomerList.Customer_List[] _List;
List _filterArray = new List();
_filterArray.AddRange(_filters);
_List = (await _ws.ReadMultipleAsync(_filterArray.ToArray(), _bookmarkkey, _setsize)).ReadMultiple_Result1;
if (_ws.State == System.ServiceModel.CommunicationState.Opened)
await _ws.CloseAsync();
return _List;
}
}
}
#endregion
}
//--- end namespace
}
i know it is some time ago this question was posted, but others might stumble across it, so here goes:
a) What seems to be the problems in above code as I just wanted to retrieve a record.
it seems like your return type is incorrect.
b) Must use Async Methods in WinRT or Windows store app?
Yes, when using windows mobile platforms(windows store apps and windows phone apps), you have to use asynchronous calls.
c) Will below code able to retrieve record from Navision?
Hard to tell, but to me it seems like your data you try to retrieve is in a incorrect format. Ill give you a simple example from one of my current projects where I retrieve a login:
private async void Button_Click(object sender, RoutedEventArgs e)
{
await call();
}
private async Task call()
{
BasicHttpBinding binding = new BasicHttpBinding();
NetworkCredential cred = new NetworkCredential("username", "password", "domain");
WS_PortClient ws = new WS_PortClient(binding, new EndpointAddress("Webservice-URL"));
binding.Security.Mode = BasicHttpSecurityMode.TransportCredentialOnly;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Ntlm;
ws.ClientCredentials.Windows.ClientCredential = cred;
CheckLogin_Result s = await ws.CheckLoginAsync("parameter");
string k = s.return_value.ToString();
MessageDialog d = new MessageDialog(k, "message");
await d.ShowAsync();
}
Hope it helps!
I don't have much experience with .NET Web Api, but i've been working with it a while now, following John Papa's SPA application tutorial on Pluralsight. The application works fine, but the thing i'm struggling with now, is unit testing POST-controllers.
I have followed this incredible guide on how to unit test web api controllers. The only problem for me is when it comes to test the POST method.
My controller looks like this:
[ActionName("course")]
public HttpResponseMessage Post(Course course)
{
if (course == null)
throw new HttpResponseException(HttpStatusCode.NotAcceptable);
try
{
Uow.Courses.Add(course);
Uow.commit();
}
catch (Exception)
{
throw new HttpResponseException(HttpStatusCode.InternalServerError);
}
var response = Request.CreateResponse(HttpStatusCode.Created, course);
string uri = Url.Link(routeName: "ControllerActionAndId",
routeValues: new { id = course.Id });
response.Headers.Location = new Uri(uri);
return response;
}
And my unit test looks like this:
[Test]
public void PostShouldReturnHttpResponse()
{
var populatedPostController = new CoursesController(new TestUOW());
SetupPostControllerForTest(populatedPostController);
var course = new Course
{
Id = 12,
Author = new UserProfile()
{
Firstname = "John",
Lastname = "Johnson",
},
Description = "Testcourse",
Title = "Test Title"
};
var responses = populatedPostController.Post(course);
ObjectContent content = responses.Content as ObjectContent;
Course result = (Course)content.Value;
Assert.AreSame(result, course);
}
With the help function:
public static void SetupPostControllerForTest(ApiController controller)
{
var config = new HttpConfiguration();
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/courses/course");
var route = config.Routes.MapHttpRoute(
name: "ControllerActionAndId",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: null,
constraints: new { id = #"^\d+$" }
);
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "courses" }, { "action", "course" } });
controller.ControllerContext = new HttpControllerContext(config, routeData, request);
controller.Request = request;
controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
}
When i debug the unit test, it seems to fail at:
string uri = Url.Link(routeName: "ControllerActionAndId",
routeValues: new { id = course.Id });
response.Headers.Location = new Uri(uri); //Exception because uri = null
It seems like the Url.Link can't find the route.
I tried this guide aswell, but i really want the example i have above to work.
Am i missing something really basic here?
Yes, you are missing the one line in the configuration as Nemesv mentioned.
controller.Request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData
As you can see, configuring a controller just for using the UrlHelper is extremely complex. I tend to avoid the use of UrlHelper in the controller classes for that reason. I usually introduce an external dependency to make testing easier like an IUrlHelper, which allows me to mock the behavior in an unit test.
public interface IUrlHelper
{
string Link(string routeName, object routeValues);
string Route(string routeName, object routeValues);
}
public class UrlHelperWrapper : IUrlHelper
{
UrlHelper helper;
public UrlHelperWrapper(UrlHelper helper)
{
this.helper = helper;
}
public string Link(string routeName, object routeValues)
{
return this.helper.Link(routeName, routeValues);
}
public string Route(string routeName, object routeValues)
{
return this.helper.Route(routeName, routeValues);
}
}
I inject this UrlHelperWraper in the real Web API, and a mock of the IUrlHelper interface in the tests. By doing that, you don't need all that complex configuration with the routes.
Regards,
Pablo.