Do not await an async method

From this answer on StackOverflow – to avoid the compiler warning you about not awaiting an async method. Not sure if this is the best answer, but it works for me:

public static class TaskExtensions
{
    public static void DoNotAwait(this Task task) { }
}

GetNameAsync().DoNotAwait();

Type-safe JSON result in ASP.NET MVC

public abstract class BaseController : Controller {
    protected internal JsonResult<T> Json<T>(T data) {
        return Json(data, null /* contentType */, null /* contentEncoding */, JsonRequestBehavior.DenyGet);
    }
    protected internal JsonResult<T> Json<T>(T data, string contentType) {
        return Json(data, contentType, null /* contentEncoding */, JsonRequestBehavior.DenyGet);
    }
    protected internal virtual JsonResult<T> Json<T>(T data, string contentType, Encoding contentEncoding) {
        return Json(data, contentType, contentEncoding, JsonRequestBehavior.DenyGet);
    }
    protected internal JsonResult<T> Json<T>(T data, JsonRequestBehavior behavior) {
        return Json(data, null /* contentType */, null /* contentEncoding */, behavior);
    }
    protected internal JsonResult<T> Json<T>(T data, string contentType, JsonRequestBehavior behavior) {
        return Json(data, contentType, null /* contentEncoding */, behavior);
    }
    protected internal virtual JsonResult<T> Json<T>(T data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior) {
        return new JsonResult<T> {
            Data = data,
            ContentType = contentType,
            ContentEncoding = contentEncoding,
            JsonRequestBehavior = behavior
        };
    }
}

public class JsonResult<T> : JsonResult { }
public class Foo { public int FooId { get; set; } }

// Type-safe result, so you can't accidentally return the wrong type.
[HttpPost]
public JsonResult<Foo> GetSomeFoo() {
    return Json(new Foo { FooId = 1 });
}

Read/write large blob to SQL Server from C#

Rather than try to make it happen in one big command, here’s breaking it out into many commands. Whether or not you use the transaction is up to you and your use case – you could always have a “Completed” column and only set that to true after success, if you wanted to skip a transaction.

First, insert a record, leaving the blob column as an empty byte array, making sure you have access to the primary key of the record you just inserted:

using var conn = new SqlConnection(_config.GetConnectionString("MyDB"));
await conn.OpenAsync().ConfigureAwait(false);
using var tran = (SqlTransaction)await conn.BeginTransactionAsync();
int fileID;
using (var comm = conn.CreateCommand())
{
    comm.Transaction = tran;
    comm.CommandText = @"
        insert dbo.Files (FileName, FileContents)
        values (@FileName, 0x);
        select scope_identity();
    ";
    comm.Parameters.AddWithValue("@FileName", fileName);
    fileID = (int)(await comm.ExecuteScalarAsync().ConfigureAwait(false));
}

Then, in the same connection, write the bytes, 8000 at a time, appending the blob column, and commit the transaction once they’re all done. The “set FileContents.Write” function is a little clumsy, but it works:

using var fileStream = System.IO.File.OpenRead(file);
byte[] buffer = new byte[8000];
int count;
while ((count = fileStream.Read(buffer, 0, buffer.Length)) > 0)
{
    byte[] tmp = new byte[count];
    Array.Copy(buffer, 0, tmp, 0, count);
    using var comm = conn.CreateCommand();
    comm.Transaction = tran;
    comm.CommandText = "update dbo.Files set FileContents.write(@ContentChunk, null, null) where FileID = @FileID";
    comm.Parameters.AddWithValue("@FileID", fileID);
    comm.Parameters.AddWithValue("@ContentChunk", tmp);
    await comm.ExecuteNonQueryAsync();
}
await tran.CommitAsync();

To read the data, use the substring function:

private async IAsyncEnumerable<byte[]> ReadFileAsync(int fileID)
{
    int startingByte = 1;
    while (true)
    {
        byte[] bytes;
        using var conn = new SqlConnection(_config.GetConnectionString("MyDB"));
        await conn.OpenAsync().ConfigureAwait(false);
        using var comm = conn.CreateCommand();
        comm.CommandText = @"
            select substring(FileContents, @StartingByte, 8000) [FileContents]
            from dbo.Files
            where FileID = @FileID;
        ";
        comm.Parameters.Add(new SqlParameter("@FileID", SqlDbType.Int) { Value = fileID });
        comm.Parameters.Add(new SqlParameter("@StartingByte", SqlDbType.Int) { Value = startingByte });
        using var rdr = await comm.ExecuteReaderAsync().ConfigureAwait(false);
        if (!await rdr.ReadAsync().ConfigureAwait(false))
        {
            break;
        }
        bytes = (byte[])rdr[0];
        if (bytes == null || bytes.Length == 0)
        {
            break;
        }
        startingByte += bytes.Length;
        yield return bytes;
    }
}


await foreach (var byteArray in ReadImportFileAsync(importBatchID))
{
    fileWriter.Write(byteArray, 0, byteArray.Length);
}

Render ViewComponent as string

Rendering a View to a string is covered here. Turns out there’s a very similar approach to rendering a ViewComponent as a string. Here’s the code, thanks to this comment on the ASP.NET forums:

    public interface IViewRenderService
    {
        Task<string> RenderToStringAsync(string viewName, object model);
    }

    public class ViewRenderService : IViewRenderService
    {
        private readonly IRazorViewEngine _razorViewEngine;
        private readonly ITempDataProvider _tempDataProvider;
        private readonly IServiceProvider _serviceProvider;

        public ViewRenderService(IRazorViewEngine razorViewEngine,
            ITempDataProvider tempDataProvider,
            IServiceProvider serviceProvider)
        {
            _razorViewEngine = razorViewEngine;
            _tempDataProvider = tempDataProvider;
            _serviceProvider = serviceProvider;
        }

        public async Task<string> RenderToStringAsync(string viewName, object model)
        {
            var httpContext = new DefaultHttpContext { RequestServices = _serviceProvider };
            var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());

            using var sw = new StringWriter();
            var viewResult = _razorViewEngine.FindView(actionContext, viewName, false);

            if (viewResult.View == null)
            {
                throw new ArgumentNullException($"{viewName} does not match any available view");
            }

            var viewDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary())
            {
                Model = model
            };

            var viewContext = new ViewContext(
                actionContext,
                viewResult.View,
                viewDictionary,
                new TempDataDictionary(actionContext.HttpContext, _tempDataProvider),
                sw,
                new HtmlHelperOptions()
            );

            await viewResult.View.RenderAsync(viewContext);
            return sw.ToString();
        }
    }

string result = await _viewRenderService.RenderToStringAsync("Shared/Components/Foo/Default", model);

// Don't forget to register the service so you can use constructor injection in your controller:

public void ConfigureServices(IServiceCollection services)
{
    /// ...
    services.AddScoped<IViewRenderService, ViewRenderService>();
}

Is Development in Razor View

I find myself needing to know if I’m in development from inside a Razor view. In the C# code, you can generally use the #DEBUG preprocessor directive, but it’s not quite so easy in Razor.

I’ve used an extension method on HtmlHelper to kind of cheat, but the “correct” way seems to be to inject the host environment and call the IsDevelopment method:

@using Microsoft.Extensions.Hosting
@inject Microsoft.AspNetCore.Hosting.IWebHostEnvironment HostEnvironment

@if (HostEnvironment.IsDevelopment())
{
    <link rel="stylesheet" href="~/css/site.css" asp-append-version="true">
}
else
{
    <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" />
}

@if (HostEnvironment.IsDevelopment())
{
    <script src="~/js/bundle.js" asp-append-version="true"></script>
}
else
{
    <script src="~/js/bundle.min.js" asp-append-version="true"></script>
}

JSONP with jQuery and ASP.NET

JSONP allows you to execute something similar to AJAX, across domains without worrying about whether the request is across domains, or any special CORS configuration.

It’s not generally a huge deal to set up CORS, but JSONP is guaranteed to work in all browsers and servers without any special handling.

Javascript

function go() {
    jQuery.ajax({
        // url: "/Home/DoSomething", // Regular MVC
        // url: "/Handler1.ashx", // WebForms
        data: {xyz:"World"},
        dataType: "jsonp",
        type: "get"
    }).done(function (result) {
        console.log(result);
    });
}

ASP.NET

// Regular MVC - no special filters, return types, etc. 
public ActionResult DoSomething(string xyz) 
{
    Response.ContentType = "application/json";
    var foo = new { FirstName = "John", LastName = "Doe", Message = "Hello " + xyz };
    Response.Write(string.Format("{0}({1});", Request["callback"], JsonConvert.SerializeObject(foo)));
    return null;
}

// WebForms
public class Handler1 : IHttpHandler 
{
    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "application/json";
        var foo = new { FirstName = "John", LastName = "Doe", Message = "Hello " + context.Request["xyz"] };
        context.Response.Write(string.Format("{0}({1});", context.Request["callback"], JsonConvert.SerializeObject(foo)));
    }

    public bool IsReusable { get { return false; } }
}

Add EXIF date to photo

If you have a photo that you need to add a timestamp to the EXIF information, you can use the following .NET code. Apparently there are a ridiculous number of formats and standards which are incompatible with each other and conflicting, but from what I’ve found, this should cover regular use cases.

I’m sure there’s a better solution, but this requires that you have some unrelated image file that has the appropriate tags in it, in case your photo is missing the properties entirely. Not exactly a great solution, but it does seem to work.

static void Go(string oldFile, string newFile) {
    DateTime theDate = new DateTime(2009, 1, 15);

    Func<int, PropertyItem> getPropItem = id => {
        using (var image = Image.FromFile(@"c:\temp\IMG_6139.jpg")) { // Some unrelated image file that already has EXIF data
            return image.GetPropertyItem(id);
        }
    };

    using (var image = Image.FromFile(oldFile)) {
        PropertyItem propItem;
        int ID = 0x9004;
        try {
            propItem = image.GetPropertyItem(ID);
        } catch {
            propItem = getPropItem();
        }
        propItem.Value = Encoding.UTF8.GetBytes(theDate.ToString("yyyy\\:MM\\:dd HH\\:mm\\:ss") + "\0");
        image.SetPropertyItem(propItem);

        ID = 0x0132;
        try {
            propItem = image.GetPropertyItem(ID);
        } catch {
            propItem = getPropItem();
        }
        propItem.Value = Encoding.UTF8.GetBytes(theDate.ToString("yyyy\\:MM\\:dd HH\\:mm\\:ss") + "\0");
        image.SetPropertyItem(propItem);

        image.Save(newFile, ImageFormat.Jpeg);
    }
}

XML Serialization Helpers

public static class Serialization {
    private static readonly Encoding _defaultEncoding = Encoding.UTF8;

    public static T DeserializeXmlFile<T>(string fileName) {
        if (!File.Exists(fileName))
            throw new FileNotFoundException();

        var serializer = new XmlSerializer(typeof(T));
        using (var xmlReader = new XmlTextReader(fileName)) {
            return (T)serializer.Deserialize(xmlReader);
        }
    }

    public static T Deserialize<T>(TextReader textReader) {
        if (textReader == null)
            throw new ArgumentNullException("textReader");

        var serializer = new XmlSerializer(typeof(T));
        return (T)(serializer.Deserialize(textReader));
    }

    public static T Deserialize<T>(string serializedObject) {
        if (string.IsNullOrEmpty(serializedObject))
            return default(T);

        var serializer = new XmlSerializer(typeof(T));
        using (var objectReader = new StringReader(serializedObject)) {
            return (T)serializer.Deserialize(objectReader);
        }
    }

    public static void SerializeToXmlFile<T>(T objectToSerialize, string fileName) {
        SerializeToXmlFile(objectToSerialize, fileName, _defaultEncoding);
    }

    public static void SerializeToXmlFile<T>(T objectToSerialize, string fileName, Encoding encoding) {
        var serializer = new XmlSerializer(typeof(T));
        using (var xmlWriter = new XmlTextWriter(fileName, encoding) { Formatting = Formatting.Indented, IndentChar = '\t', Indentation = 1 }) {
            serializer.Serialize(xmlWriter, objectToSerialize);
        }
    }

    public static string Serialize<T>(T objectToSerialize, bool includeXmlDeclaration, bool indent) {
        var serializer = new XmlSerializer(typeof(T));
        var sb = new StringBuilder(0x1000);

        XmlTextWriter xmlWriter =
            includeXmlDeclaration
            ? new XmlTextWriter(new StringWriter(sb))
            : new XmlTextWriterWithoutDeclaration(new StringWriter(sb));

        if (indent) {
            xmlWriter.Formatting = Formatting.Indented;
            xmlWriter.IndentChar = '\t';
            xmlWriter.Indentation = 1;
        }

        var namespaces = new XmlSerializerNamespaces();
        namespaces.Add(string.Empty, string.Empty);
        serializer.Serialize(xmlWriter, objectToSerialize, namespaces);
        return sb.ToString();
    }

    private class XmlTextWriterWithoutDeclaration
        : XmlTextWriter
    {
        public XmlTextWriterWithoutDeclaration(TextWriter w)
            : base(w)
        { }

        public override void WriteStartDocument() { }

        public override void WriteStartDocument(bool standalone) { }
    }
}

These are just a few helpers to make using the built-in XmlSerializer a little bit easier. It skips the needless XML namespaces, which just ugly up simple XML. If you want to skip the <?xml version="1.0" encoding="utf-16"?> declaration, then you can use that overload.

[Serializable] public class Foo { public string Name { get; set; } public int Age { get; set; }

var foo = new Foo { Name = "john", Age = 34 };
string xml = Serialization.Serialize(foo, true, false); // Keep declaration
Console.WriteLine(xml);
Console.WriteLine();
xml = Serialization.Serialize(foo, false, false); // Remove declaration
Console.WriteLine(xml);
Console.WriteLine();
using (var writer = new StringWriter())
{
    new XmlSerializer(typeof(Foo)).Serialize(writer, foo); // Default serialization
    Console.WriteLine(writer.ToString());
}

Output:

<?xml version="1.0" encoding="utf-16"?><Foo><Name>john</Name><Age>34</Age></Foo>

<Foo><Name>john</Name><Age>34</Age></Foo>

<?xml version="1.0" encoding="utf-16"?>
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Name>john</Name>
  <Age>34</Age>
</Foo>

Windows Services with .NET Framework

This code demonstrates two simple techniques for Windows services in .NET. First, it shows how to create a Console application which also can be installed as a Windows service – this way you can write a Windows service and easily test and debug it without installing it, and without using multiple projects. Second, it shows how to set the service name at install-time instead of compile-time.

Create the project as a Console application, rather than a Windows service. Then just add the following code, and organize your codebase as you wish.

using System;
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;
using System.ServiceProcess;

namespace ConsoleApp1 {
    public partial class Service1 : ServiceBase {
        static void Main(string[] args) {
            var service = new Service1();
            if (Environment.UserInteractive) {
                try {
                    service.OnStart(args);
                } catch (Exception ex) {
                    Console.WriteLine(ex);
                } finally {
                    Console.WriteLine("DONE - Press <ENTER> to exit.");
                    service.OnStop();
                    Console.ReadLine();
                }
            } else {
                Run(service);
            }
        }

        protected override void OnStart(string[] args) { /* implementation */ }
        protected override void OnStop(){ /* implementation */ }
    }

    [RunInstaller(true)]
    public partial class ProjectInstaller : Installer {
        public ProjectInstaller() {
            string[] commandlineArgs = Environment.GetCommandLineArgs();

            string servicename;
            string servicedisplayname;
            ParseServiceNameSwitches(commandlineArgs, out servicename, out servicedisplayname);

            _serviceProcessInstaller = new ServiceProcessInstaller();
            _serviceInstaller = new ServiceInstaller();

            _serviceInstaller.ServiceName = servicename;
            _serviceInstaller.DisplayName = servicedisplayname;

            Installers.AddRange(new Installer[] { _serviceProcessInstaller, _serviceInstaller });
        }

        private ServiceProcessInstaller _serviceProcessInstaller;
        private ServiceInstaller _serviceInstaller;

        private void SetServicePropertiesFromCommandLine(ServiceInstaller serviceInstaller) {
            string[] commandlineArgs = Environment.GetCommandLineArgs();

            string servicename;
            string servicedisplayname;
            ParseServiceNameSwitches(commandlineArgs, out servicename, out servicedisplayname);

            serviceInstaller.ServiceName = servicename;
            serviceInstaller.DisplayName = servicedisplayname;
        }

        // http://www.runeibsen.dk/?p=153
        private void ParseServiceNameSwitches(string[] args, out string name, out string displayName) {
            var nameSwitch = args.FirstOrDefault(x => x.StartsWith("/servicename"));
            var displayNameSwitch = args.FirstOrDefault(x => x.StartsWith("/servicedisplayname"));

            if (nameSwitch == null)
                throw new ArgumentException("Argument 'servicename' is missing");
            if (displayNameSwitch == null)
                throw new ArgumentException("Argument 'servicedisplayname' is missing");
            if (!(nameSwitch.Contains('=') || nameSwitch.Split('=').Length < 2))
                throw new ArgumentNullException("The /servicename switch is malformed");

            if (!(displayNameSwitch.Contains('=') || displayNameSwitch.Split('=').Length < 2))
                throw new ArgumentNullException("The /servicedisplaynameswitch switch is malformed");

            name = nameSwitch.Split('=')[1];
            displayName = displayNameSwitch.Split('=')[1];

            name = name.Trim('"');
            displayName = displayName.Trim('"');
        }
    }
}

Installing it from the command line:

installutil -i /servicename="my service" /servicedisplayname="my display name" ConsoleApp1.exe

Versioned content in MVC

When you add a script or stylesheet to your HTML page, those requests can be cached by the browser, potentially providing outdated content to the browser.

If you’re not using a bundler or anything fancy like that, then the only way to prevent this problem is to create a brand new URL whenever your script file or stylesheet changes. This is easily accomplished with a querystring, so if your app was previously using app.js?v=1 and you change that to app.js?v=2, the browser will definitely not use the cached version, and will instead make a new request to the server, and pull the latest one.

Using the following technique, you can guarantee that you’ll always pull the latest script and stylesheet when working in dev, and when in production, you can define a “version key”, possibly in a config file or database, and if you ever update just the static content, you would only need to update that key, and it would force all browsers to pull new content.

public static class HtmlExtensions {
    private static string GetUniqueVersionKey() {
        #if DEBUG
        return Guid.NewGuid().ToString("N");
        #else
        // This needs to be somewhere you can get to it, like a web.config file or database
        return MvcApplication.GetUniqueVersionKey();
        #endif
    }

    public static MvcHtmlString Script(this HtmlHelper htmlHelper, string scriptFileName) {
        var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
        return new MvcHtmlString(string.Format("<script type=\"text/javascript\" src=\"{0}?uv={1}\"></script>",
            urlHelper.Content(scriptFileName), GetUniqueVersionKey()));
    }

    public static MvcHtmlString Style(this HtmlHelper htmlHelper, string styleFileName) {
        var urlHelper = new UrlHelper(htmlHelper.ViewContext.RequestContext);
        return new MvcHtmlString(string.Format("<link rel=\"stylesheet\" href=\"{0}?uv={1}\">",
            urlHelper.Content(styleFileName), GetUniqueVersionKey()));
    }
}

These will produce the appropriate link or script tag.

@Html.Style("app.css")
@Html.Script("app.js")