Phil's Notes

Latest 5 Posts

  1. Fast Inserts to Oracle with C#

  2. Recently I found myself having to do bulk record inserts to an Oracle table on an API I created with .Net 6. I was using EntityFrameworkCore for this and read that even though I was using AddRange, the inserts actually happen one record at a time and would end up taking a very long time to complete.

    After some research and experimentation, I found that OracleBulkCopy was able to do the job for me. Actually, there was a the Z.EntityFramework.Extensions.EFCore NuGet package that would have done the work for me very, very easily, but the license price was a bit too steep for me at the moment. For a large company with the budget, though, I would recommend it especially to keep your code simpler.

    For my solution, you need to first make sure you have Oracle.ManagedDataAccess installed from NuGet. You can use the Core version instead, if you are using a .Net core project.

    Make sure to include the using statement:

    using Oracle.ManagedDataAccess.Client;

    This method does require your records to be in a DataTable, though, but otherwise here's some example code on how to use OracleBulkCopy:

    // Your records need to be in a DataTable.
    var records = <your_records>;

    using OracleConnection connection = new (<your_connection_string>);
    connection.Open();

    using OracleBulkCopy bulk = new(connection);
    bulk.DestinationTableName = "<destination_table>";
    bulk.ColumnMappings.Add(new OracleBulkCopyColumnMapping { DestinationColumn = "<destination_column>", SourceColumn = "<source_column>" }; // You can add more columns as needed
    bulk.WriteToServer(records);

    You'll probably also want to wrap the code from the connection to the WriteToServer in a try/catch block.


  3. Mixing Generics in C#

  4. Recently I've come across a situation where I wanted to keep a list of validation errors for fields with different data types. Upon writing this article, however, I realized that I could have simplified the entire process by keeping the expected and actual values all as strings. Since I went through the trouble of going through this, though, I will share what I learned in case it might help in a different situation.

    First, we have to create a validation error type. In order to be able to create a list of them, though, you will need to create an interface so that they don't all need to have the same data type.

    public interface IValidationError
    {
    string FieldName { get; set; }
    Type Type { get; }
    object ExpectedValue { get; set; }
    object ActualValue { get; set; }
    }

    public class ValidationError<T> : IValidationError
    {
    public string FieldName { get; set; } = string.Empty;
    Type IValidationError.Type => typeof(T);
    public T ExpectedValue { get; set; }
    public T ActualValue { get; set; }
    object IValidationError.ExpectedValue
    {
    get => ExpectedValue.ToString();
    set => ExpectedValue = (T)value;
    }
    object IValidationError.ActualValue
    {
    get => ActualValue.ToString();
    set => ActualValue = (T)value;
    }
    }

    Now you should be able to use the new class to create a list like so:

    List<IValidationError> validationErrors = new();

    validationErrors.Add(new ValidationError<string>()
    {
    FieldName = "Reference",
    ExpectedValue = "A26",
    ActualValue = "A33"
    });

    validationErrors.Add(new ValidationError<decimal>()
    {
    FieldName = "Cost",
    ExpectedValue = 9.99m,
    ActualValue = 10.99m
    });

    To get the values and use them:

    string errorMessageFormat = "{0} was supposed to be {1} but is instead {2}. ";
    string errorMessage = "";

    if (validationErrors.Count > 0)
    {
    foreach (var error in validationErrors)
    {
    errorMessage += string.Format(errorMessageFormat, error.FieldName, error.ExpectedValue, error.ActualValue);
    }

    Console.WriteLine(errorMessage.Trim());
    }

    Here's the whole example:

    using System;

    namespace MixingGenerics
    {
    internal class Program
    {
    static void Main()
    {
    List<IValidationError> validationErrors = new();

    validationErrors.Add(new ValidationError<string>()
    {
    FieldName = "Reference",
    ExpectedValue = "A26",
    ActualValue = "A33"
    });

    validationErrors.Add(new ValidationError<decimal>()
    {
    FieldName = "Cost",
    ExpectedValue = 9.99m,
    ActualValue = 10.99m
    });

    string errorMessageFormat = "{0} was supposed to be {1} but is instead {2}. ";
    string errorMessage = "";

    if (validationErrors.Count > 0)
    {
    foreach (var error in validationErrors)
    {
    errorMessage += string.Format(errorMessageFormat, error.FieldName, error.ExpectedValue, error.ActualValue);
    }

    Console.WriteLine(errorMessage.Trim());
    }
    }
    }

    public interface IValidationError
    {
    string FieldName { get; set; }
    Type Type { get; }
    object ExpectedValue { get; set; }
    object ActualValue { get; set; }
    }

    public class ValidationError<T> : IValidationError
    {
    public string FieldName { get; set; } = string.Empty;
    Type IValidationError.Type => typeof(T);
    public T ExpectedValue { get; set; }
    public T ActualValue { get; set; }
    object IValidationError.ExpectedValue
    {
    get => ExpectedValue.ToString();
    set => ExpectedValue = (T)value;
    }
    object IValidationError.ActualValue
    {
    get => ActualValue.ToString();
    set => ActualValue = (T)value;
    }
    }
    }

  5. Simple Regex Example in C#

  6. Sometimes you just need a quick example of something you know how to do, but you can't remember the syntax for since you only need to use it from time to time.

    using System;
    using System.Text.RegularExpressions;

    class Program
    {
    static void Main()
    {
    Regex rx = new(@":(\d+)-");

    string text = "File:24524-abc.txt";

    Match match = rx.Match(text);

    if (match.Success)
    {
    Console.WriteLine(match.Groups[1].Value);
    }
    }
    }

  7. 405 Method Not Allowed on PUT Requests in IIS

  8. If you publish your .NET Core API to IIS and you are having trouble with PUT requests, but your GET and POST ones work just fine, you might be having trouble with the WebDAV module. One way to get around this issue is update your web.config to remove the WebDAV packages like below.

    <system.webServer>
    <modules>
    <remove name="WebDAVModule" />
    </modules>
    <handlers>
    <remove name="WebDAV" />
    </handlers>
    </system.webServer>

    There are also a few more methods to remove the WebDAV modules. You can take a look at this Stack Overflow article.


  9. EACCES Error with Vue using Vite

  10. Depending on your computer set-up, you might see this error when trying to start a Vite project without making any changes to the default template:

    Error: listen EACCES: permission denied on 127.0.0.1:3000

    For whatever reason, your computer isn't letting you use port 3000. Luckily, the fix is simple enough. You just need to add a line to the vite.config.ts file to specify the port you want to use.

    Inside the defineConfig object, add the following line:

    server: { port: 8080 }

    Of course, you can change 8080 to be whatever port number you have access to. The complete file should look something like this:

    import { defineConfig } from 'vite'
    import vue from '@vitejs/plugin-vue'

    // https://vitejs.dev/config/
    export default defineConfig({
    plugins: [vue()],
    server: { port: 8080 }
    })

More posts can be found in the archive.