How do I get started with Redis on Servicestack on Windows? - asp.net-mvc

I've just got started with ServiceStack and have created my first service in MVC4. Now I want to persist my objects using Redis. I can't figure out how to get it running on Windows or if the ServiceStack distribution already has this included. I'm also thinking about using one of the Redis cloud implementations, but I'd like to get it running locally first.
Thanks

You need something like Redis on Windows (here and here, for blog posts about that). You can use the repo stored on github. Once you have that you can build redis in Visual Studio and run it.
Service Stack also has a support page here, including a link to a project that runs Redis as a Windows Service.
Edit. And I've found the project and blog post I played with a month or so ago as well (which coincidentally is written by Jason from stackexchange).
LATE UPDATE Ok, so no sooner had I commented
do more than "download" and "execute installer to get robust service" like you do with Nuget packages
than I find this Redis Nuget that allows you to run Redis from the command line, released by MSOpenTech that you can use with the ServiceStack.Redis package
Edit and this is how you use it:
Create a console application in Visual Studio
Run "manage NuGet packages" in the project console menu on solution explorer
Search for, and install "redis-64" and "ServiceStack.Redis" (you may want to do redis-64 by running install-package redis-64 from the Package Manager Console)
Start redis from packages\Redis-64.\tools\redis-server.exe by cmd prompt or double click
(If asked about windows firewall, just cancel to keep comms on the local machine)
run the following code:
public class Message {
public long Id { get; set; }
public string Payload { get; set; }
}
static void Main(string[] args) {
List<string> messages = new List<string> {
"Hi there",
"Hello world",
"Many name is",
"Uh, my name is"
};
var client = new RedisClient("localhost");
var msgClient = client.As<Message>();
for (int i = 0; i < messages.Count; i++) {
Message newItem = new Message {
Id = msgClient.GetNextSequence(),
Payload = messages[i] };
msgClient.Store(newItem);
}
foreach (var item in msgClient.GetAll()) {
Console.WriteLine("{0} {1}", item.Id, item.Payload);
msgClient.DeleteById(item.Id);
}
Console.WriteLine("(All done, press enter to exit)");
Console.ReadLine();
}
Output:
1 Hi there
2 Hello world
3 Many name is
4 Uh, my name is
(All done, press enter to exit)

Related

Controller gives directory browsing rather than returning text

I have the following action method:
public ActionResult Index(string filename)
{
string detailFolder = SSOSettingsFileManager.SSOSettingsFileReader.ReadString("bts.go.core", "SupportNotificationDetailPath");
string pathFile = System.IO.Path.Combine(detailFolder, filename);
XDocument xdoc = XDocument.Load(pathFile);
XNamespace ns = "http://BTS.GO.Core.Schemas.SupportNotificationDetail";
var notificationMsg = from x in xdoc.Descendants("NotificationMessage") select x.Value;
var content = HttpUtility.HtmlDecode(notificationMsg.FirstOrDefault());
IHtmlString htmlContent = new HtmlString(content);
SupportNotificationDetailViewModel vm = new SupportNotificationDetailViewModel();
vm.Content = htmlContent;
return View(vm);
}
This works fine when deployed into my dev machine.The view calls the action and is returned the text content of the "NotificationMessage"element, which it renders on screen, as shown below:
My problem is, with the same site deployed to my test server. When I click the link that executes the action, rather than being presented with text, I get a directory listing of the content of the folder contained in the variable detailFolder, as shown below:
I guess this must be down to a difference in the IIS config. I tried using msdn link to figure it out but no luck. The command for msdeploy comes back with "Cannot create a file when that file already exists.". I find this strange because I used web deploy to deploy the website to both servers. Both machines are running server 2012r2
The bad server has the following Windows Features installed (should be all that's needed?):
Any ideas?

How to fetch Build Warning from TFS MS Build or TFS API

I am trying to fetch Build Warning from MS Build,(in Build which contain or having number of solutions)
Is it possible to fetch using TFS API, or any TFS DB using QUERY ?
You could use this TFS REST API to get logs of a TFS builds. To get those logs out, you need to fetch those warnings by yourself. There's no API to only get warnings.
Http method: GET
http:/servername"8080/tfs/DefaultCollection/teamproject/_apis/build/builds/391/logs?api-version=2.0
You could also install a TFS ExtendedClient Nuget package to use TFS object model API.
Here is the code snippet:
Like the comment said above, the VNext build definition information couldn't be reached using the old version API. Install this TFS ExtendedClient Nuget package for your project, using the method below to get all build definitions.
using Microsoft.VisualStudio.Services.WebApi;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.TeamFoundation.Build.WebApi;
using Microsoft.TeamFoundation.Core.WebApi;
using Microsoft.VisualStudio.Services.Operations;
private static void GetBuildWarnings()
{
var u = new Uri("http://v-tinmo-12r2:8080/tfs/MyCollection/");
VssCredentials c = new VssCredentials(new Microsoft.VisualStudio.Services.Common.WindowsCredential(new NetworkCredential("username", "password", "domain")));
var connection = new VssConnection(u, c);
BuildHttpClient buildServer = connection.GetClient<BuildHttpClient>();
List<BuildLog> logs = buildServer.GetBuildLogsAsync("teamprojectname",buildId).Result;
foreach (BuildLog log in logs)
{
var list = buildServer.GetBuildLogLinesAsync("A92FB795-A956-45B5-A017-7A7DFB96A040",buildId,log.Id).Result; //A92FB795-A956-45B5-A017-7A7DFB96A040 is the team project Guid
foreach (var line in list)
{
if (l.Contains("[Warning]"))
{
Console.WriteLine(line);
}
}
}
Console.ReadLine();
}

MachineKeyDataProtector - Invalid link when confirmation email sent through background job

I've been pulling my hair out over this. Anytime a user registration email is sent out via my windows service (background task), I get an "Invalid link".
My setup
I'm using Hangfire as a windows service on our development server. This is where the problematic GenerateEmailConfirmationToken call is happening. It's in a completely different context, outside of the ASP.NET pipeline. So I have setup machineKey values to correspond with that in the web.config of the MVC application:
In the app.config of the Windows Service Console project, which transforms to MyApp.exe.config, I have a machineKey element
In the MVC 5 project - I have a machineKey element that matches the MyApp.exe.config machineKey element.
I've verified that BOTH of these have the same machine key element data.
The Problem
When I generate a user using the ASP.NET MVC context and pipeline (IE without going through the Hangfire Background job processing), the link works fine.
When I use the background job processor, I always get invalid link. I'm all out of ideas here.
Why is this happening? Is it because the token is being generated in a different thread? How do I get around this?
Relevant code for the various projects
IoC Bootstrapping
Gets called by both applications (Windows Service and MVC Web App)
container.Register<IUserTokenProvider<AppUser, int>>(() => DataProtector.TokenProvider, defaultAppLifeStyle);
DataProtector.cs
public class DataProtector
{
public static IDataProtectionProvider DataProtectionProvider { get; set; }
public static DataProtectorTokenProvider<AppUser, int> TokenProvider { get; set; }
static DataProtector()
{
DataProtectionProvider = new MachineKeyProtectionProvider();
TokenProvider = new DataProtectorTokenProvider<AppUser, int>(DataProtectionProvider.Create("Confirmation", "ResetPassword"));
}
}
Things I've Tried
Using a DpapiDataProtectionProvider
Custom MachineKeyProtectionProvider from Generating reset password token does not work in Azure Website
The MachineKeyProtectionProvider.cs code is exactly as the linked post above.
I've also tried other purposes like "YourMom" and "AllYourTokensAreBelongToMe" to no avail. Single purposes, multiple purposes - it doesn't matter - none work.
I'm also calling HttpUtility.UrlEncode(code) on the code that gets generated in both places (Controller and Background Job).
Solution
igor got it right, except it was not a code issue. It was because of a rogue service picking up the job, which had a different machine key. I had been staring at the problem so long that I did not see a second service running.
As I understand your problem there are 2 possible places where failure could occur.
1. MachineKey
It could be that the MachineKey itself is not producing a consistent value between your 2 applications. This can happen if your machineKey in the .config file is not the same in both applications (I did read that you checked it but a simple type-o, added space, added to the wrong parent element, etc. could lead to this behavior.). This can be easily tested to rule it out as a point of failure. Also the behavior might be different depending on the referenced .net framework, MachineKey.Protect
The configuration settings that are required for the MachineKeyCompatibilityMode.Framework45 option are required for this method even if the MachineKeySection.CompatibilityMode property is not set to the Framework45 option.
I created a random key pair for testing and using this key I generated a test value I assigned to variable validValue below in the code. If you copy/paste the following section into your web.config and app.config the Unprotect of that keyvalue will work.
web.config / app.config
<system.web>
<httpRuntime targetFramework="4.6.1"/>
<machineKey decryption="AES" decryptionKey="9ADCFD68D2089D79A941F9B8D06170E4F6C96E9CE996449C931F7976EF3DD209" validation="HMACSHA256" validationKey="98D92CC1E5688DB544A1A5EF98474F3758C6819A93CC97E8684FFC7ED163C445852628E36465DB4E93BB1F8E12D69D0A99ED55639938B259D0216BD2DF4F9E73" />
</system.web>
Service Application Test
class Program
{
static void Main(string[] args)
{
// should evaluate to SomeTestString
const string validValue = "03AD03E75A76CF13FDDA57425E9D362BA0FF852C4A052FD94F641B73CEBD3AC8B2F253BB45550379E44A4938371264BFA590F9E68E59DB57A9A4EB5B8B1CCC59";
var unprotected2 = MachineWrapper.Unprotect(validValue);
}
}
Mvc Controller (or Web Api controller) Test
public class WebTestController : Controller
{
// GET: WebTest
public ActionResult Index()
{
// should evaluate to SomeTestString
const string validValue = "03AD03E75A76CF13FDDA57425E9D362BA0FF852C4A052FD94F641B73CEBD3AC8B2F253BB45550379E44A4938371264BFA590F9E68E59DB57A9A4EB5B8B1CCC59";
var unprotected2 = MachineWrapper.Unprotect(validValue);
return View(unprotected2);
}
}
Common Code
using System;
using System.Linq;
using System.Text;
using System.Web.Security;
namespace Common
{
public class MachineWrapper
{
public static string Protect()
{
var testData = "SomeTestString";
return BytesToString(MachineKey.Protect(System.Text.Encoding.UTF8.GetBytes(testData), "PasswordSafe"));
}
public static string Unprotect(string data)
{
var bytes = StringToBytes(data);
var result = MachineKey.Unprotect(bytes, "PasswordSafe");
return System.Text.Encoding.UTF8.GetString(result);
}
public static byte[] StringToBytes(string hex)
{
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
public static string BytesToString(byte[] bytes)
{
var hex = new StringBuilder(bytes.Length * 2);
foreach (byte b in bytes)
hex.AppendFormat("{0:x2}", b);
return hex.ToString().ToUpper();
}
}
}
If this passes both Console and the Web Application will get the same value and not throw a CryptographicException message Error occurred during a cryptographic operation. If you want to test with your own keys just run Protect from the common MachineWrapper class and record the value and re-execute for both apps.
2. UserManager uses Wrong Type
I would start with the previous section BUT the other failure point is that your custom machine key provider is not being used by the Microsoft.AspNet.Identity.UserManager. So here are some questions/action items that can help you figure out why this is happening:
Is container.Register the Unity IoC framework or are you using another framework?
Are you sure that your Di framework is also injecting that instance in the Microsoft.AspNet.Identity.UserManager in both the Service application as well as the Web application?
Have put a break point in public byte[] Protect of your MachineKeyDataProtector class to see if this is called in both the Service application as well as the Web application?
From examples I have seen so far (including the one you posted with the custom MachineKey solution) you need to manually bootstrap the type during application startup but then again I have not ever tried to hook into the Identity framework to replace this component using DI.
If you look at the default Visual Studio template code that is provided when you create a new MVC application the code file App_Start\IdentityConfig.cs would be the place to add this new provider.
Method:
public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context)
Replace
var dataProtectionProvider = options.DataProtectionProvider;
if (dataProtectionProvider != null)
{
manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
}
With this
var provider = new MachineKeyProtectionProvider();
manager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(provider.Create("ResetPasswordPurpose"));
And this has to be configured for both applications if you are not using a common library where this is configured.

Azure RoleEnvironment.Changing event not being called in ASP.NET MVC 5

I am trying to use the Azure Runtime Reconfiguration Pattern to allow me to change a appSetting in the normal Web.config file via PowerShell (later by Microsoft Azure Web Sites Management Library).
My problem is that the RoleEnvironment.Changing event is not being called in my MVC app, so the web app is being restarted. I have placed event set up code in the MVC Application_Start as described in the Azure article, i.e.
protected void Application_Start()
{
RoleEnvironment.Changing += RoleEnvironment_Changing;
RoleEnvironment.Changed += RoleEnvironment_Changed;
//normal MVC code etc...
AreaRegistration.RegisterAllAreas();
}
The event handlers are a straight copy of the handled from the Azure article and look like this:
private const string CustomSettingName = "TestConfig";
public static string TestConfigValue;
private static void RoleEnvironment_Changing(object sender,
RoleEnvironmentChangingEventArgs e)
{
RoleLogs.Add("RoleEnvironment_Changing: started");
var changedSettings = e.Changes.OfType<RoleEnvironmentConfigurationSettingChange>()
.Select(c => c.ConfigurationSettingName).ToList();
Trace.TraceInformation("Changing notification. Settings being changed: "
+ string.Join(", ", changedSettings));
if (changedSettings
.Any(settingName => !string.Equals(settingName, CustomSettingName,
StringComparison.Ordinal)))
{
Console.WriteLine("Cancelling dynamic configuration change (restarting).");
RoleLogs.Add("RoleEnvironment_Changing: restarting!");
// Setting this to true will restart the role gracefully. If Cancel is not
// set to true, and the change is not handled by the application, the
// application will not use the new value until it is restarted (either
// manually or for some other reason).
e.Cancel = true;
}
else
{
RoleLogs.Add("RoleEnvironment_Changing: change is OK. Not restarting");
Console.WriteLine("Handling configuration change without restarting. ");
}
}
private static void RoleEnvironment_Changed(object sender,
RoleEnvironmentChangedEventArgs e)
{
RoleLogs.Add("RoleEnvironment_ChangED: Starting");
Console.WriteLine("Updating instance with new configuration settings.");
foreach (var settingChange in
e.Changes.OfType<RoleEnvironmentConfigurationSettingChange>())
{
if (string.Equals(settingChange.ConfigurationSettingName,
CustomSettingName,
StringComparison.Ordinal))
{
// Execute a function to update the configuration of the component.
RoleLogs.Add("RoleEnvironment_ChangED: TestConfig has changed");
Console.WriteLine("TestConfig has changed.");
TestConfigValue = RoleEnvironment.GetConfigurationSettingValue(CustomSettingName);
}
}
}
I have added logs which prove that my RoleEnvironment_Changing and RoleEnvironment_Changed are not being called in the MVC WebApp which means the WebApp is restarted when I change an appSetting via PowerShell. This also means the RoleEnvironment.Changing event never gets to the WebJob.
I am using Azure SDK 2.7.0
Any ideas?
UPDATE
#richag gave me an answer, which made me realise that my problem is because I am using a App Service rather than a Cloud Service. This SO answer and plus this video (see at 5:00mins) talks about the difference (Note: the video is old so the name of the web app is different, but the concept is the same).
I don't really want to change this late in the development, and I have worked round the problem another way. Maybe on the next project and will look at Cloud Services as I can see some positives, like better control of my WebJobs configuration.
From the runtime reconfiguration pattern: "Microsoft Azure Cloud Services roles detect and expose two events that are raised when the hosting environment detects a change to the ServiceConfiguration.cscfg files" These events are not fired if you make changes to app.config/web.config files. Only when the cloud service configuration is changed, i.e. if you upload a new configuration file through the azure portal's configure tab or change a setting directly on the azure portal.
According to the debugger, none of the following events are fired when I update the Azure Portal to change an AppSetting for an ASP.NET WebAPI app:
RoleEnvironment.Changing
RoleEnvironment.Changed
RoleEnvironment.StatusCheck
RoleEnvironment.SimultaneousChanging
RoleEnvironment.SimultaneousChanged
RoleEnvironment.Stopping
Do others have different experience?

How to print from MVC3 controller from server

Code below is used to print from ASP .NET MVC3 application to Samsung ML-331x Series printer connected to IP address in LAN and shared in server as Samsung ML-331x Series
Nothing is printed. Execution stops at doc.Print() line.
Every controller call adds new unfinished job:
Those jobs are never finished, only iisreset clears them.
Server is Windows 2003 + IIS
It worked in MVC2 .NET 3.5 application. ForteSSL VPN was installed in server and application was upgraded to
MVC3 and .NET 4.
(After that printing was not checked immediately, so I'm not sure is this related to those changes).
User NETWORK SERVICE with all permissions is added to printer users but problem persists.
Running application in development computer by pressing F5 in visual studio with Web Developer server and calling with controller to print to PDF Creator works OK.
How to print from controller in server ?
public ActionResult PrintTest()
{
var doc = new PrintDocument();
doc.PrinterSettings.PrinterName = "Samsung ML-331x Series";
doc.PrintPage += new PrintPageEventHandler(TestProvideContent);
doc.Print();
return new ContentResult()
{
Content = "Printed"
};
}
public void TestProvideContent(object sender, PrintPageEventArgs e)
{
e.Graphics.DrawString("Test",
new Font("Arial", 12),
Brushes.Black,
e.MarginBounds.Left,
e.MarginBounds.Top);
}
Update
Application runs at full trust Level.
Application log in event viewer does not have any entries about this.
Using Programmatically "hello world" default SERVER-side printer in ASP.NET MVC I tried to use printer name with server name as \\SERVER\Samsung ML-331x Series but problem persists.

Resources