public static class StringExtensions
{
public static string JsonAsQueryString(this string json)
{
return ParseJObject(json, string.Empty);
}
private static string ParseJObject(string json, string parentField = "")
{
var jo = (JObject)JsonConvert.DeserializeObject(json);
var query = String.Join("&",
jo.Children().Cast<JProperty>()
.Select(jp => StringExtensions.ParseJProperty(jp, parentField)));
return query;
}
private static string ParseJProperty(JProperty jp, string parentField)
{
var parentFormat = string.IsNullOrWhiteSpace(parentField) ? string.Empty : $"{parentField}.";
if (jp.Value as JArray != null)
{
// this object is an array; repeat names
var tokens = string.Join("&", jp.Value
.Select(t => $"{parentFormat}{jp.Name}={HttpUtility.UrlEncode(t.ToString())}"));
return tokens;
}
else if (jp.Value.Type == JTokenType.Object)
{
// nested object; make a recursive call
return StringExtensions.ParseJObject(jp.Value.ToString(), $"{parentFormat}{jp.Name}");
}
else
{
// normal member
return $"{parentFormat}{jp.Name}={HttpUtility.UrlEncode(jp.Value.ToString())}";
}
}
}
Just some random development ramblings mostly related to the Microsoft .NET platform.
Tuesday, January 23, 2018
[Solved] Convert complex C#/JSON object to query string, ASPNET Core 2.0
I had to call an internal web page using HttpClient to get the HTML. The Razor Page's OnGetAsync expected a C# complex object (nested types) as a parameter. Here is a string extension, JsonAsQueryString, to convert a complex object into a query string for ASPNET Core 2.0. I just converted the C# complex object to JSON using JsonConvert.Serialize, then use this extension method.
Friday, December 15, 2017
Vue 2.0 modal in Bootstrap 4, a confirmation modal example for everyone
I was a bit tired of looking for a suitable example of using a Bootstrap 4.0 modal within Vue.js.
Here is a Vue component to do just that. I've included an example of how to use. After the initial comments of the Example, everything else is your JavaScript component file.
Here is a Vue component to do just that. I've included an example of how to use. After the initial comments of the Example, everything else is your JavaScript component file.
I welcome comments if folks have improvements...but let's keep it simple for everyone else.
// jQuery required as Bootstrap uses it, but if you use another non-jQuery
// modal framework, you can modify the show() function to remove jQuery modal 'show'
//
// Example:
//
// <div id="app">
// <button type="button" class="btn btn-secondary" v-on:click="$refs.modal.show()">Modal</button>
// <confirm-component ref="modal" v-bind:options="confirmOptions()" v-on:closeCancel="cancel" v-on:closeNo="no" v-on:closeYes="yes"></confirm-component>
// </div>
//
// <script>
// var vueApp = new Vue({
// el: '#app',
// ...
//
// confirmOptions() {
// return {
// title: '<i>Do you really want to do this?</i>',
// message: 'Going to do something really <strong>major</strong>...continue?',
//
// textCancel: 'CANCEL', // defaults to 'Cancel'
// textNo: 'NO', // defaults to 'No'
// textYes: 'YES', // defaults to 'Yes'
//
// showNo: true, // defaults to true
// showNo: false, // defaults to false
// showYes: true // defaults to true
// };
// }
// cancel() {
// alert('cancel');
// },
// no() {
// alert('no');
// },
// yes() {
// alert('yes');
// }
// });
// </script>
"use strict";
Vue.component('confirm-component', {
props: ['options'],
methods: {
show() {
$('#confirmModalComponent').modal('show');
}
},
computed: {
modalTitle() {
return this.options.title ? this.options.title : '';
},
modalMessage() {
return this.options.message ? this.options.message : '';
},
modalTextCancel() {
return this.options.textCancel ? this.options.textCancel : 'Cancel';
},
modalTextNo() {
return this.options.textNo ? this.options.textNo : 'No';
},
modalTextYes() {
return this.options.textYes ? this.options.textYes : 'Yes';
},
showCancel() {
return this.options.showCancel ? this.options.showCancel : true;
},
showNo() {
return this.options.showNo ? this.options.showNo : false;
},
showYes() {
return this.options.showYes ? this.options.showYes : true;
},
closeCancel() {
this.$emit('closeCancel');
},
closeNo() {
this.$emit('closeNo');
},
closeYes() {
this.$emit('closeYes');
}
},
template: `
<div class="modal fade" id="confirmModalComponent" tabindex="-1" role="dialog" aria-labelledby="confirmModalComponentLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="confirmModalComponentLabel" v-html="modalTitle"></h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body" v-html="modalMessage">
</div>
<div class="modal-footer justify-content-between">
<div>
<button v-if="showCancel" type="button" class="btn btn-primary" :click="closeCancel" data-dismiss="modal">{{modalTextCancel}}</button>
</div>
<div>
<button v-if="showNo" type="button" class="btn btn-primary" :click="closeNo" data-dismiss="modal">{{modalTextNo}}</button>
<button v-if="showYes" type="button" class="btn btn-success" :click="closeYes" data-dismiss="modal">{{modalTextYes}}</button>
</div>
</div>
</div>
</div>
</div>
`
});
Saturday, November 25, 2017
Easily convert JSON to C# strongly-typed classes
Check out this handy online tool to convert JSON to C# strongly-typed classes!
Sunday, September 17, 2017
URL rewrite for https and www in ASPNET Core (Solved)
If you want to url rewrite to both https and www using ASP.NET Core 2.0, here is some pretty straight-forward C# you can add to your Startup.cs.
Woo-hoo!
// ...
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
// ...
}
else
{
// url rewrite; do this before UseStaticFiles
var options = new RewriteOptions().Add(
new RedirectRules(rewriteHttps: true, rewriteWww: true));
app.UseRewriter(options);
}
// ...
}
// ...
public class RedirectRules : Microsoft.AspNetCore.Rewrite.IRule
{
private readonly bool _rewriteHttps;
private readonly bool _rewriteWww;
public RedirectRules(bool rewriteHttps, bool rewriteWww)
{
_rewriteHttps = rewriteHttps;
_rewriteWww = rewriteWww;
}
public void ApplyRule(RewriteContext context)
{
//
// redirect two ways
// - http to https (if enabled)
// - non-www host to www host (if enabled)
//
const string hostRoot = "mydomain.com";
var request = context.HttpContext.Request;
var currentHost = request.Host;
HostString newHost;
// do we need to rewrite from http to https
var isNewProtocol = _rewriteHttps && request.Scheme.ToLower() == "http";
// do we need to rewrite from non-www host to www host
if (_rewriteWww && currentHost.Host == hostRoot)
newHost = new HostString($"www.{hostRoot}", currentHost.Port ?? 80);
// if either rewrite done, create a new url
if (isNewProtocol || newHost.HasValue)
{
var newUrl = new StringBuilder()
.Append(isNewProtocol ? "https" : request.Scheme)
.Append("://")
.Append(newHost.HasValue ? newHost : currentHost)
.Append(request.PathBase)
.Append(request.Path)
.Append(request.QueryString);
context.HttpContext.Response.Redirect(newUrl.ToString(), permanent: true);
context.Result = RuleResult.EndResponse;
}
}
}
Tuesday, August 22, 2017
Dotnet Core 1.1 to 2.0 conversion
Microsoft recently released Dotnet core 2.0. I was eager to convert my Core 1.1 code so I jumped right in. Just a bit of background on what I was dealing with (all Core 1.1):
- Around 18 class libraries
- Two MVC Core websites
- Entity Framework Core
- Identity Authentication
- MailKit (for SMTP email)
It took me probably four (4) hours to do the migration. Not too painful, but I wanted to share several of the other websites that I bookmarked to help me get through it all. Hopefully these will help you as well.
I think the issue that caused me the most pain (i.e. changes) was the Identity stuff. It was a large number of changes, but just working to determine the differences between what the Core 1.1 template originally added vs. what Core 2.0 template had (which BTW, the 2.0 template still used Core 1.1 except for ASP.NET).
I was also able to remove MailKit totally once SmtpClient support came back to Dotnet Core.
Best of luck!
Friday, April 21, 2017
Entity Framework Core: Migrate and update a repository in class library
As of April 2017, Entity Framework Core (and Visual Studio 2017) does not support having your database repository in a separate class library. Here are the steps I followed in order to work around this. This was done successfully even with multiple repositories (i.e. more than one DbContext).
Hope this can help others as it took me awhile to get it right. I did these steps for more than one DbContext and database so I'm sure this works as of the time I wrote this post.
Here is what I did to get things working nicely:
Added nuget packages:
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.SqlServer.Design
Microsoft.EntityFrameworkCore.Tools
Added Program.cs to the class library project:
Added property group to .csproj:
<PropertyGroup>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
</PropertyGroup>
Set xxxxRepositoryModel as startup project.
Set the Package Manager Console "Default project" to your repository project.
Open Package Manager Console and then:
PM> cd xxxxRepositoryModel
PM> Add-Migration Initial -context xxxxDbContext
PM> Update-Database -c xxxxDbContext
Note: If this is a brand new repository based on an EXISTING database and you have done the Package Manager command, Scaffold-DbContext, then you will need to:
Hope this can help others as it took me awhile to get it right. I did these steps for more than one DbContext and database so I'm sure this works as of the time I wrote this post.
Added nuget packages:
Microsoft.EntityFrameworkCore
Microsoft.EntityFrameworkCore.SqlServer
Microsoft.EntityFrameworkCore.SqlServer.Design
Microsoft.EntityFrameworkCore.Tools
Added Program.cs to the class library project:
public class Program { public static void Main(string[] args) { } public class xxxxContextFactory : IDbContextFactory<xxxxDbContext> { public xxxxDbContext Create(DbContextFactoryOptions options) { // // this is only used for data migrations and db updates; the connection // string is not used for production // var optionsBuilder = new DbContextOptionsBuilder<xxxxDbContext>(); optionsBuilder.UseSqlServer("Server=.;Database=xxxxDatabase;Trusted_Connection=True;MultipleActiveResultSets=true"); return new xxxxDbContext(optionsBuilder.Options); } } }
Added property group to .csproj:
<PropertyGroup>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
</PropertyGroup>
Set xxxxRepositoryModel as startup project.
Set the Package Manager Console "Default project" to your repository project.
Open Package Manager Console and then:
PM> cd xxxxRepositoryModel
PM> Add-Migration Initial -context xxxxDbContext
PM> Update-Database -c xxxxDbContext
Note: If this is a brand new repository based on an EXISTING database and you have done the Package Manager command, Scaffold-DbContext, then you will need to:
- After you perform an Add-Migration...
- Modify your Migrations/yyyymmddhhmmss_Initial.cs file and...
- Completely empty the Up and Down methods
- Then continue to perform the Update-Database
- This gives you a clean starting point from which to make new database model changes (migrations) in the future.
Tuesday, March 21, 2017
SOLVED: Configure Raspberry Pi Wi-Fi the easy way
I recently had to configure the Wi-Fi on my Raspberry Pi which was running on Raspian Jessie Lite 8.0 (Debian). It took a few attempts, but in the end, what I ended up with was very simple and it worked nicely.
I just modified /etc/network/interfaces with (changed in red text):
# interfaces(5) file used by ifup(8) and ifdown(8)
# Please note that this file is written to be used with dhcpcd
# For static IP, consult /etc/dhcpcd.conf and 'man dhcpcd.conf'
# Include files from /etc/network/interfaces.d:
source-directory /etc/network/interfaces.d
auto lo
iface lo inet loopback
allow-hotplug eth0
auto eth0 iface
iface eth0 inet dhcp
# Wireless configuration
auto wlan0
allow-hotplug wlan0
iface wlan0 inet dhcp
wpa-ssid "myVisibleSSID"
wpa-psk "password"
I just modified /etc/network/interfaces with (changed in red text):
# interfaces(5) file used by ifup(8) and ifdown(8)
# Please note that this file is written to be used with dhcpcd
# For static IP, consult /etc/dhcpcd.conf and 'man dhcpcd.conf'
# Include files from /etc/network/interfaces.d:
source-directory /etc/network/interfaces.d
auto lo
iface lo inet loopback
allow-hotplug eth0
auto eth0 iface
iface eth0 inet dhcp
# Wireless configuration
auto wlan0
allow-hotplug wlan0
iface wlan0 inet dhcp
wpa-ssid "myVisibleSSID"
wpa-psk "password"
Then just remove the network cable and reboot. Presto magic!
Saturday, March 11, 2017
Writing your own plugin for GpioWeb web service framework on Raspberry Pi
The RaspberryPi.NET.GpioWeb project is an extensible web service framework which not only comes with many built-in plugins for controlling the Raspberry Pi GPIO pins via a RESTful web service, but allows developers to write their own custom GPIO plugins. Woo hoo!
We created a good video to help folks understand how to write their own custom plugins to extend this GPIO web service framework. As a reminder to folks, the framework is .NET based and executes on the Pi using Mono.
You can view the video at:
https://youtu.be/PwNvpr27Gw4
Would love to get some constructive input on if this is adequate to help folks with writing their own plugins.
The GpioWeb project and source code can be found on both Github and Bitbucket:
Github
https://github.com/paultechguy/RaspberryPi.DotNet.GpioWeb
Bitbucket
https://bitbucket.org/PaulTechGuy/raspberrypi.dotnet.gpioweb
Thanks.
We created a good video to help folks understand how to write their own custom plugins to extend this GPIO web service framework. As a reminder to folks, the framework is .NET based and executes on the Pi using Mono.
You can view the video at:
https://youtu.be/PwNvpr27Gw4
Would love to get some constructive input on if this is adequate to help folks with writing their own plugins.
The GpioWeb project and source code can be found on both Github and Bitbucket:
Github
https://github.com/paultechguy/RaspberryPi.DotNet.GpioWeb
Bitbucket
https://bitbucket.org/PaulTechGuy/raspberrypi.dotnet.gpioweb
Thanks.
Git: Sync a single local repository to both Github and Bitbucket
It turns out to be fairly simple to sync a single local git repository to both Github and Bitbucket. I use SourceTree for my single repository, which originally pointed solely to Bitbucket. I did an Import of that repository into Github and now want all my commits using SourceTree to push to both remote repositories.
I just modified my local .git/config file to include another pushUrl. No changes required in SourceTree, but when SourceTree does a push, both remotes will be updated.
Before:
[remote "origin"]
url = https://bitbucket.org/xyz/bitbucket.repository.name
fetch = +refs/heads/*:refs/remotes/origin/*
pushurl = https://bitbucket.org/xyz/bitbucket.repository.name
After:
[remote "origin"]
url = https://bitbucket.org/xyz/bitbucket.repository.name
fetch = +refs/heads/*:refs/remotes/origin/*
pushurl = https://bitbucket.org/xyz/bitbucket.repository.name
pushurl = https://github.com/xyz/github.repository.name
Wednesday, March 1, 2017
Resolved: Enable CORS on OWIN the right way
I was having difficulty, probably like you, trying to figure out how to get CORS working on a OWIN self-hosted application. There were so many answers posted, but seemed like so many of them were based on older versions of Visual Studio / .NET.
Here is what I finally found to work for me; hopefully this will help you as well. This is as of Visual Studio 2015 in early March 2017.
- Add nuget packages to the web api project:
- Microsoft.AspNet.WebApi.Cors
- Microsoft.Owin.Cors (this will also install Microsoft.AspNet.Cors)
- Modify startup.cs, Configuration(IAppBuilder app), add this line:
app.UseCors(CorsOptions.AllowAll);
and add it before the line:
app.UseWebApi(config); - Add the following attribute on my web api controller class:
[EnableCors("*", "*", "*")]
These three steps got things finally working.
Control Raspberry Pi camera (tilt/pan) with C# / Mono
We updated the GpioWeb repository to include an example of how to control a tilt/pan camera hooked up to the Raspberry Pi (2 servos). This was done in C# / Mono.
See the ServoPanTiltDemo.html file in the GpioWeb.PluginServoSimple directory.
The GpioWeb repository is located at:
Note: Remember that servos are all a bit different so you will need to slightly adjust the following values in the json "config" file (see instructions in the ServoPanTiltDemo.html file); values tend to run in the range of 100 to 700.
- pwmMinPulse
- pwmMaxPulse
If you download the GpioExamples repository, there is a servo example that you can run to determine the min/max pulse values for a specific servo. When you run the example, it will ask for degrees (0 to 180), but if you prefix your input with "!" then it will be interpreted as a raw pulse value. For example, "!125". Very helpful.
Subscribe to:
Posts (Atom)
Can't RDP? How to enable / disable virtual machine firewall for Azure VM
Oh no! I accidentally blocked the RDP port on an Azure virtual machine which resulted in not being able to log into the VM anymore. I did ...
-
Don't want Office 2007 installed on your web server to access Excel 2007 content? Here is a Visual Studio C# solution that demonstrates ...
-
I finally found a good algorithm to validate a file path (had to fix two bugs in it though). I use this static method in Windows Forms when...