Funciones personalizadas en expresiones LINQ

En las últimas versiones de Entity Framework ciertos métodos que antes si eran aceptados, como también se podía realizar en SQL, han dejado de estar disponibles. Aquellas funciones heredadas directamente de SQL han quedado englobadas en la misma clase. Algo que antes era habitual pero no precisamente una buena forma de solucionar… las conversiones de tipos de datos, ¿quien no ha convertido un string en entero en SQL?

LINQ to Entities does not recognize the method 'XXXXXXX' method, and this method cannot be translated into a store expression

Si hablamos con propiedad y utilizando todo el potencial de los ORM no es una buena práctica, debemos revisar y guardar los datos con el tipo más apropiado, pero si nos vemos forzados a utilizarlas… podemos personalizar funciones dentro de nuestro modelo de datos.

Abrimos nuestro EDMX y definimos la función:

    <edmx:ConceptualModels>
      <Schema Namespace="NombreDeMiModelo" Alias="Self" annotation:UseStrongSpatialTypes="false" xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation" xmlns:customannotation="http://schemas.microsoft.com/ado/2013/11/edm/customannotation" xmlns="http://schemas.microsoft.com/ado/2009/11/edm">
        <Function Name="convertirDouble" ReturnType="Edm.Double"> 
        <Parameter Name="stringvalue" Type="Edm.String" /> 
        <DefiningExpression> 
            cast(stringvalue as Edm.Double)
        </DefiningExpression> 
        </Function>
...

Definimos en nuestra clase la función:

using System;
using System.Data.Entity;
using System.Data.Objects.DataClasses;

public partial class MiModeloContext
{
    /// <summary>
    ///     Este método existe para poder ser usando dentro de expresiones LINQ
    ///     convierte un texto en número (Double)
    /// </summary>

    [DbFunction("NombreDeMiModelo", "convertirDouble")]
    public static double convertirDouble(string stringvalue)
    {
        return Double.Parse(stringvalue);
    }
}

Ya podemos utilizar la función dentro de las expresiones:

datos = miEntidad.miDato.Where(x => SqlFunctions.IsNumeric(x.aux) > 0 &&
MiModeloContext.ParseDouble(x.aux) > numero)
.FirstOrDefault();

Aunque deberemos revisar si realmente nuestro modelo de datos es coherente…

Referencia: Calling Functions in LINQ to Entities Queries

LINQ: could not find an implementation of the query pattern

En modelos de datos generados con LINQ de forma automática, el acceso separado en diferentes clases puede ocasionar errores del tipo:

CS1936	Could not find an implementation of the query pattern for source type 'DbSet<nombreTabla>'.  'Where' not found.

A priori puede parecer un error en la implementación, alguna modificación de clases incorrecta, siempre esta la posibilidad de volver a generar el modelo de datos, si el error persiste debemos comprar que tenemos referenciada el espacio de nombres de LINQ

using System.Linq;

Una error tan sencillo y obvio que puede resultar confuso de resolver.

Microsoft SQL Server Management Studio: cache is out of date

Es agosto, el software tambien necesita vacaciones…

Si durante las consultas a algunas tablas en MSSMS aparece el error «The Visual Studio componente cache is out of date» la solución rápida y sencilla pasa por vaciar la carpeta temporales de windows %TMP%

C:\Users\nombre_usuario_windows\AppData\Local\Temp\

Si además el servidor SQL esta en medio de la playa mientras tu sigues programando, puedes probar a seguir con la limpieza de más memorias intermedias, si utilizas Server 2016 o Azure puedes limpiar la cache de los procedimientos de la base de datos:

USE NombreDeMiBBDD;
GO
ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE;

 

Mientras esperas que la consulta devuelva los valores esperados ;D

 

 

Cambiar compatibilidad en todas las bases de datos

Antes o después de ciertas migraciones podemos cambiar el nivel de compatibilidad SQL con el motor instalado. Si desconocemos la versión, podemos obtenerlas con la siguiente consulta:

SELECT SERVERPROPERTY('ProductVersion');  

Así como el nivel actual de las bases de datos:

SELECT name, compatibility_level FROM sys.databases;

Para realizar el cambio a todas, excepto las 4 primeras del sistema:

declare @nivel_compatible  varchar(max)
set     @nivel_compatible  = ''
select  @nivel_compatible = @ nivel_compatible +

from   sys.databases
where  database_id > 4 and compatibility_level not in ('100')
exec   (@ nivel_compatible)

En este ejemplo cambiaríamos el nivel de compatibilidad de las que no son 100 a 100, jugando con la clausula WHERE de la instrucción podemos realizar los cambios que necesitemos.

 

Fuente: Diferencias entre niveles de compatibilidad

Recuperar bases de datos MySQL desde ficheros

El motor de almacenamiento utilizado por MySQL/MariaDB hasta su versión 5.5 es MyISAM, en versiones posteriores se incorpora InnoDB.

A modo de resumen breve, dada la popularidad en entornos web en los que las consultas de lectura (SELECT) predominan sobre las de escritura (INSERT/UPDATE) sigue siendo una de las opciones más utilizadas en entornos en los que prima: velocidad, sin bloqueo de registros o tablas, sin características ACID (Atomicity, Consistency, Isolation, Durability) internas, recursos hardware limitados…

Cada una de las bases de datos generadas en su directorio contiene 3 ficheros por cada tabla con extensiones:

  • FRM: definición de la esctructura de la tabla
  • MYD: datos almacenados
  • MYI: índices

Podemos copiar desde entornos linux a windows y viceversa, con la consideración de dar los permisos suficientes de acceso sobre los ficheros.

  • En Windows: C:\ProgramData\MySQL\MySQL Server 5.X\data
  • En Linux: /var/lib/mysql/

Puedes conocer el directorio de datos realizando consulta en las variables globales:

mysql -s -N -uUSER -p information_schema -e 'SELECT Variable_Value FROM GLOBAL_VARIABLES WHERE Variable_Name = "datadir"'

O realizando la consulta sobre la variable directamente:

select @@datadir;

Una vez localizado los directorios, debemos parar el servicio MySQL, copiar la carpeta completa de nuestra base de datos en el directorio y asignar los permisos de acceso.

Los índices puedes ser regenerados o reparados mediante sentencias posteriormente:

check table nombreTabla;

repair table nombreTabla