Mis indispensables de C#. Parte 2: Extensiones

Esta es una serie de 3 artículos cortos sobre características de C# que uso en todas mis aplicaciones.

Extensiones… Muchas extensiones


Me gusta usar extension methods para validaciones. Aquí hay algunas básicas.

public static class ValueExtensions
{
    public static string ThrowIfEmpty(this string value)
    {
        return string.IsNullOrWhiteSpace(value) ?
            throw new ArgumentNullException() :
            value.Trim();
    }

    public static string TrimOrThrow(this string value)
    {
        return value?.Trim() ??
            throw new ArgumentNullException();
    }

    public static Guid ThrowIfEmpty(this Guid value)
    {
        return value == Guid.Empty ?
            throw new ArgumentNullException() :
            value;
    }

    public static DateTime ThrowIfInPast(this DateTime value)
    {
        return value <= DateTime.Now ?
            throw new ArgumentOutOfRangeException() :
            value;
    }

    public static DateTime NowIfDefault(this DateTime value)
    {
        return value == default(DateTime) ?
            DateTime.Now :
            value;
    }
}

Es muy sencillo validar parámetros usando estas extensiones.

public async Task<TransferObject> DoSomething(
    string userId,
    Guid roleId,
    string name,
    DateTime meetingDate,
    DateTime date)
{
    userId.ThrowIfEmpty();
    roleId.ThrowIfEmpty();
    name = name.TrimOrThrow();
    meetingDate.ThrowIfInPast();
    date = date.NowIfDefault();

    return await _store.SaveSomething(userId, roleId, name, meetingDate, date);
}

También las uso para interactuar con la base de datos, si por algún motivo no estoy usando un ORM.

public static class SqlExtensions
{
    public static string GetStringOrNull(this SqlDataReader reader, int index)
    {
        return reader.IsDBNull(index) ?
            null :
            reader.GetString(index);
    }

    public static DateTime GetDateTimeOrNow(this SqlDataReader reader, int index)
    {
        return reader.IsDBNull(index) ?
            DateTime.Now :
            reader.GetDateTime(index);
    }

    public static DateTime? GetDateTimeOrNull(this SqlDataReader reader, int index)
    {
        return reader.IsDBNull(index) ?
            null :
            reader.GetDateTime(index);
    }

    public static Guid GetGuidOrEmpty(this SqlDataReader reader, int index)
    {
        return reader.IsDBNull(index) ?
            Guid.Empty :
            reader.GetGuid(index);
    }

    public static Guid? GetGuidOrNull(this SqlDataReader reader, int index)
    {
        return reader.IsDBNull(index) ?
            null :
            reader.GetGuid(index);
    }

    public static void SetSqlDBNullParameters(this SqlCommand command)
    {
        foreach (IDataParameter param in command.Parameters)
        {
            if (param.Value == null)
            {
                param.Value = DBNull.Value;
            }
        }
    }
}

Así es muy fácil manejar las columnas opcionales de un insert.

string itemKey = "blog.lvbernal.com";
string itemValue = null;

string nonQuery = @"
    INSERT INTO [some].[table] ([key], [value])
    VALUES (@key, @value)";

using SqlConnection conn = new SqlConnection(_connectionString);
conn.Open();

using SqlCommand cmd = new SqlCommand(nonQuery, conn);
cmd.Parameters.AddWithValue("@key", itemKey);
cmd.Parameters.AddWithValue("@value", itemValue);  // Esto genera un error

cmd.SetSqlDBNullParameters();  // Aquí reemplazamos los null por DBNull.Value

Y leer datos desde un SqlDataReader.

using SqlDataReader reader = await cmd.ExecuteReaderAsync();

if (reader.HasRows)
{
    while (reader.Read())
    {
        var item = new ItemDTO(
            id: reader.GetGuid(0),
            name: reader.GetStringOrNull(1),
            owner: reader.GetGuidOrNull(2)
        );

        list.Add(item);
    }
}

Comentarios

Entradas más populares de este blog

Historia de una App: un mes en la Tienda Windows y en Google Play

El Meetup de enero en CaliSharp

El nuevo sistema de autenticación de ASP.NET