C # change Dapper dynamic para quejarse si la propiedad no existe

c# dapper dynamic

Pregunta

Estamos utilizando Dapper para recuperar datos de nuestra base de datos SQL, que devuelve nuestros datos como una colección de 'dinámico'. Entiendo el poder de los tipos dinámicos, y la flexibilidad de "tipar pato", básicamente "si grazna como un pato, entonces es un pato, no necesito declarar que es un pato".

Sin embargo, no entiendo por qué si trato de obtener una propiedad de un objeto dinámico que no tiene, ¿por qué no se queja? Por ejemplo, si tuviera algo que no fuera un pato y lo llamara "Quack", habría pensado que era razonable esperar que se quejara. EDITAR: vea los comentarios, esto parece ser algo sobre la dinámica que Dapper me está dando porque un objeto dinámico estándar da un error de tiempo de ejecución si la propiedad no existe.

¿Hay alguna manera de hacer que se queje?

El código que tengo es una secuencia de líneas que toman propiedades del 'dinámico' y las asignan a la propiedad correspondiente en el objeto fuertemente tipado. Los nombres de las propiedades no siempre se vinculan (debido a los estándares de nombres de bases de datos heredados). Por el momento, si un nombre de campo está mal escrito en la dinámica, entonces fallará silenciosamente. Quiero que se queje. No quiero tener que volver a escribir cada línea de código en 5 líneas de la propiedad "does [hard-coded name] existe en la dinámica" / "si no se queja" / "obtener el valor y ponerlo en el lugar correcto ".

EDITAR: Aquí está el código específico, en caso de que ayude ... el nombre de campo que es incorrecto es "result.DecisionLevel", y no obtengo un error de tiempo de ejecución, simplemente asigna null en la propiedad de destino

        var results = _connection.Query("usp_sel_Solution", new { IdCase = caseId }, commandType: CommandType.StoredProcedure);
        return results.Select(result => new Solution
        {
            IsDeleted = result.IsDeleted,
            FriendlyName = result.FriendlyName,
            DecisionLevel = (DecisionLevel?)result.DecisionLevel,
        }).ToList();

SOLUCIÓN: La respuesta aceptada a esto , combinada con la respuesta de Sergey me llevó a esta solución:

internal class SafeDynamic : DynamicObject
{
    private readonly IDictionary<string, object> _source;

    public SafeDynamic(dynamic source)
    {
        _source = source as IDictionary<string, object>;
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (_source.TryGetValue(binder.Name, out result) == false)
        {
            throw new NotSupportedException(binder.Name);
        }

        return true;
    }

    // I'll refactor this later, probably to an extension method...
    public static IEnumerable<dynamic> Create(IEnumerable<dynamic> rows)
    {
        return rows.Select(x => new SafeDynamic(x));
    }
}

El único cambio en el código de muestra es ajustar la llamada al método Dapper's Query:

        var results = SafeDynamic.Create(_connection.Query("usp_sel_Solution", new { IdCase = caseId }, commandType: CommandType.StoredProcedure));

Gracias.

Para la posteridad, estoy agregando el enlace a la solución que he proporcionado para saber cómo hacer lo mismo para Query <T> y tenga en cuenta la Edición 25/1/17 "Mejoras para evitar problemas de subprocesamiento en el diccionario estático", que también se aplica a la solución que se muestra aquí.

Respuesta aceptada

Puede agregar envoltura a los objetos de origen que tiene e implementar el comportamiento deseado en él (lanzar o no trowing una excepción, proporcionar un valor predeterminado o corregir los nombres de las propiedades). Algo como esto:

clase pública WrapperDynamic: DynamicObject {private dynamic _source; public WrapperDynamic (fuente dinámica) {_source = source; }

public override bool TryGetMember(GetMemberBinder binder, out object result)
{                        
    if (_source.CheckTheProperyExist(binder))
    {
        result = _source.GetProperty(binder);
        return true;
    }
    return false;
}

}

Debería implementar CheckTheProperyExist y GetProperty según el tipo de objetos fuente.

Debes agregar cobertura para ti selecciona

return results.Select(x=>new WrapperDynamic(x))
.Select(result => new Solution
        {
            IsDeleted = result.IsDeleted,
            FriendlyName = result.FriendlyName,
            DecisionLevel = (DecisionLevel?)result.DecisionLevel,
        }).ToList();

Puede agregar la conversión de nombre para los nombres heredados en este contenedor.


Respuesta popular

Todo el comportamiento esperado puede diferir según el objeto dinámico que se construya.

No es un requisito lanzar una excepción si un miembro dinámico no es parte de un objeto dinámico.

Por ejemplo:

public class MyDynamic : DynamicObject
{
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
           // I always set the result to null and I return true to tell
           // the runtime that I could get the value but I'm lying it!
           result = null;
           return true;
    }
}


dynamic myDynamic = new MyDynamic();
string text = myDynamic.text; // This won't throw a runtime exception!

Probablemente, y por ejemplo, Dapper intente obtener el miembro dinámico y no se queje si no se encuentra ningún miembro, y esto podría ser por diseño.

ExpandoObject es un objeto dinámico que implementa IDictionary<string, object> para que pueda verificar efectivamente si un miembro existe en el objeto dinámico usando ContainsKey :

dynamic expando = new ExpandoObject();
expando.text = "hello world";

if((IDictionary<string, object>)expando).ContainsKey("text")) 
{
    // True
}

Por cierto, si una biblioteca de terceros (o incluso uno de primera) ha implementado un objeto dinámico que no duele cuando accede a un miembro no existente, no podrá forzar lo contrario. Tendrá que vivir con eso, porque es una decisión de diseño.

Como el tipado de pato depende en gran medida de la documentación, si sabe que un objeto dinámico funciona de esa manera, sabrá qué propiedad no se configuró si recibe el valor predeterminado del tipo de propiedad:

dynamic dyn = ...;

// x should be null once it's set and you'll know that
// dyn had no x member...
string x = dyn.x;


Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué