”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 收集和处理 INMET-BDMEP 气候数据

收集和处理 INMET-BDMEP 气候数据

发布于2024-11-02
浏览:260

Os dados climáticos desempenham um papel crucial em diversos setores, auxiliando em estudos e previsões que impactam áreas como agricultura, planejamento urbano, e gestão de recursos naturais.

O Instituto Nacional de Meteorologia (INMET) oferece, mensalmente, o Banco de Dados Meteorológicos (BDMEP) em seu site. Este banco de dados contém uma série histórica de informações climáticas coletadas por centenas de estações de medição distribuídas por todo o Brasil. No BDMEP, você encontrará dados detalhados sobre pluviometria, temperatura, umidade do ar e velocidade do vento.

Com atualizações horárias, esses dados são bastante volumosos, proporcionando uma base rica para análises detalhadas e tomadas de decisão informadas.

Neste post vou mostrar como coletar e tratar os dados climáticos do INMET-BDMEP. Vamos coletar os arquivos de dados brutos disponíveis no site do INMET e depois vamos tratar esses dados para facilitar a análise.

1. Pacotes Python necessários

Para alcançar os objetivos mencionados será preciso ter instalado apenas três pacotes:

  • httpx para fazer as requisições HTTP
  • Pandas para a leitura e tratamento dos dados
  • tqdm para mostrar uma barra de progresso amigável no terminal enquanto o programa baixa ou lê os arquivos

Para instalar os pacotes necessários execute o seguinte comando no terminal:

pip install httpx pandas tqdm

Se estiver usando um ambiente virtual (venv) com poetry, por exemplo, use o seguinte comando:

poetry add httpx pandas tqdm

2. Coleta dos arquivos

2.1 Padrão da URL dos arquivos

O endereço dos arquivos de dados do BDMEP seguem um padrão bem simples. O padrão é o seguinte:

https://portal.inmet.gov.br/uploads/dadoshistoricos/{year}.zip

A única parte que muda é o nome do arquivo que é, simplesmente, o ano de referência dos dados. Mensalmente, o arquivo para o ano mais recente (corrente) é substituído com dados atualizados.

Assim fica fácil criar um código para coletar automaticamente os arquivos de dados de todos os anos disponíveis.

Aliás, a série histórica disponível começa no ano 2000.

2.2 Estratégia de coleta

Para a coleta dos arquivos de dados do INMET-BDMEP vamos usar a biblioteca httpx para fazer as requisições HTTP e a biblioteca tqdm para mostrar uma barra de progresso amigável no terminal.

Primeiro, vamos importar os pacotes necessários:

import datetime as dt
from pathlib import Path

import httpx
from tqdm import tqdm

Já identificamos o padrão da URL dos arquivos de dados do INMET-BDMEP. Agora vamos criar uma função que aceita um ano como argumento e devolve a URL do arquivo referente a esse ano.

def build_url(year):
    return f"https://portal.inmet.gov.br/uploads/dadoshistoricos/{year}.zip"

Para checar se o arquivo da URL foi atualizado podemos utilizar a informação presente no cabeçalho retornado por uma requisição HTTP. Em servidores bem configurados, podemos pedir apenas esse cabeçalho com o método HEAD. Nesse o servidor foi bem configurado e podemos usar desse método.

A resposta à requisição HEAD terá o seguinte formato:

Mon, 01 Sep 2024 00:01:00 GMT

Para parsear essa data/hora fiz a seguinte função em Python, que aceita uma string e devolve um objeto datetime:

def parse_last_modified(last_modified: str) -> dt.datetime:
    return dt.datetime.strptime(
        last_modified,
        "%a, %d %b %Y %H:%M:%S %Z"
    )

Assim, podemos usar a data/hora da última modificação para incluir no nome do arquivo que vamos baixar, usando interpolação de strings (f-strings):

def build_local_filename(year: int, last_modified: dt.datetime) -> str:
    return f"inmet-bdmep_{year}_{last_modified:%Y%m%d}.zip"

Dessa forma, é possível verificar facilmente se o arquivo com os dados mais recentes já existe no nosso sistema de arquivos local. Se o arquivo já existir, o programa pode ser encerrado; caso contrário, devemos prosseguir com a coleta do arquivo, fazendo a requisição ao servidor.

A função download_year abaixo faz o download do arquivo referente a um ano específico. Se o arquivo já existe no diretório de destino, a função simplesmente retorna sem fazer nada.

Note como usamos o tqdm para mostrar uma barra de progresso amigável no terminal enquanto o arquivo é baixado.

def download_year(
    year: int,
    destdirpath: Path,
    blocksize: int = 2048,
) -> None:

    if not destdirpath.exists():
        destdirpath.mkdir(parents=True)

    url = build_url(year)

    headers = httpx.head(url).headers
    last_modified = parse_last_modified(headers["Last-Modified"])
    file_size = int(headers.get("Content-Length", 0))

    destfilename = build_local_filename(year, last_modified)
    destfilepath = destdirpath / destfilename
    if destfilepath.exists():
        return

    with httpx.stream("GET", url) as r:
        pb = tqdm(
            desc=f"{year}",
            dynamic_ncols=True,
            leave=True,
            total=file_size,
            unit="iB",
            unit_scale=True,
        )
        with open(destfilepath, "wb") as f:
            for data in r.iter_bytes(blocksize):
                f.write(data)
                pb.update(len(data))
        pb.close()

2.3 Coleta dos arquivos

Agora que temos todas as funções necessárias, podemos fazer a coleta dos arquivos de dados do INMET-BDMEP.

Usando um loop for podemos baixar os arquivos de todos os anos disponíveis. O código a seguir faz exatamente isso. Começando do ano 2000 até o ano corrente.

destdirpath = Path("data")
for year in range(2000, dt.datetime.now().year   1):
    download_year(year, destdirpath)

3. Leitura e tratamento dos dados

Com os arquivos de dados brutos do INMET-BDMEP baixados, agora podemos fazer a leitura e o tratamento dos dados.

Vamos importar os pacotes necessários:

import csv
import datetime as dt
import io
import re
import zipfile
from pathlib import Path

import numpy as np
import pandas as pd
from tqdm import tqdm

3.1 Estrutura dos arquivos

Dentro do arquivo ZIP disponibilizado pelo INMET encontramos diversos arquivos CSV, um para cada estação meteorológica.

Porém, nas primeiras linhas desses arquivos CSV encontramos informações sobre a estação, como a região, a unidade federativa, o nome da estação, o código WMO, as coordenadas geográficas (latitude e longitude), a altitude e a data de fundação. Vamos extrair essas informações para usar como metadados.

3.2 Leitura dos dados com pandas

A leitura dos arquivos será feita em duas partes: primeiro, será feita a leitura dos metadados das estações meteorológicas; depois, será feita a leitura dos dados históricos propriamente ditos.

3.2.1 Metadados

Para extrair os metadados nas primeiras 8 linhas do arquivo CSV vamos usar o pacote embutido csv do Python.

Para entender a função a seguir é necessário ter um conhecimento um pouco mais avançado de como funciona handlers de arquivos (open), iteradores (next) e expressões regulares (re.match).

def read_metadata(filepath: Path | zipfile.ZipExtFile) -> dict[str, str]:
    if isinstance(filepath, zipfile.ZipExtFile):
        f = io.TextIOWrapper(filepath, encoding="latin-1")
    else:
        f = open(filepath, "r", encoding="latin-1")
    reader = csv.reader(f, delimiter=";")
    _, regiao = next(reader)
    _, uf = next(reader)
    _, estacao = next(reader)
    _, codigo_wmo = next(reader)
    _, latitude = next(reader)
    try:
        latitude = float(latitude.replace(",", "."))
    except:
        latitude = np.nan
    _, longitude = next(reader)
    try:
        longitude = float(longitude.replace(",", "."))
    except:
        longitude = np.nan
    _, altitude = next(reader)
    try:
        altitude = float(altitude.replace(",", "."))
    except:
        altitude = np.nan
    _, data_fundacao = next(reader)
    if re.match("[0-9]{4}-[0-9]{2}-[0-9]{2}", data_fundacao):
        data_fundacao = dt.datetime.strptime(
            data_fundacao,
            "%Y-%m-%d",
        )
    elif re.match("[0-9]{2}/[0-9]{2}/[0-9]{2}", data_fundacao):
        data_fundacao = dt.datetime.strptime(
            data_fundacao,
            "%d/%m/%y",
        )
    f.close()
    return {
        "regiao": regiao,
        "uf": uf,
        "estacao": estacao,
        "codigo_wmo": codigo_wmo,
        "latitude": latitude,
        "longitude": longitude,
        "altitude": altitude,
        "data_fundacao": data_fundacao,
    }

Em resumo, a função read_metadata definida acima lê as primeiras oito linhas do arquivo, processa os dados e retorna um dicionário com as informações extraídas.

3.2.2 Dados históricos

Aqui, finalmente, veremos como fazer a leitura do arquivo CSV. Na verdade é bastante simples. Basta usar a função read_csv do Pandas com os argumentos certos.

A seguir está exposto a chamada da função com os argumentos que eu determinei para a correta leitura do arquivo.

pd.read_csv(
    "arquivo.csv",
    sep=";",
    decimal=",",
    na_values="-9999",
    encoding="latin-1",
    skiprows=8,
    usecols=range(19),
)

Primeiro é preciso dizer que o caractere separador das colunas é o ponto-e-vírgula (;), o separador de número decimal é a vírgula (,) e o encoding é latin-1, muito comum no Brasil.

Também é preciso dizer para pular as 8 primeiras linhas do arquivo (skiprows=8), que contém os metadados da estação), e usar apenas as 19 primeiras colunas (usecols=range(19)).

Por fim, vamos considerar o valor -9999 como sendo nulo (na_values="-9999").

3.3 Tratamento dos dados

Os nomes das colunas dos arquivos CSV do INMET-BDMEP são bem descritivos, mas um pouco longos. E os nomes não são consistentes entre os arquivos e ao longo do tempo. Vamos renomear as colunas para padronizar os nomes e facilitar a manipulação dos dados.

A seguinte função será usada para renomear as colunas usando expressões regulares (RegEx):

def columns_renamer(name: str) -> str:
    name = name.lower()
    if re.match(r"data", name):
        return "data"
    if re.match(r"hora", name):
        return "hora"
    if re.match(r"precipita[çc][ãa]o", name):
        return "precipitacao"
    if re.match(r"press[ãa]o atmosf[ée]rica ao n[íi]vel", name):
        return "pressao_atmosferica"
    if re.match(r"press[ãa]o atmosf[ée]rica m[áa]x", name):
        return "pressao_atmosferica_maxima"
    if re.match(r"press[ãa]o atmosf[ée]rica m[íi]n", name):
        return "pressao_atmosferica_minima"
    if re.match(r"radia[çc][ãa]o", name):
        return "radiacao"
    if re.match(r"temperatura do ar", name):
        return "temperatura_ar"
    if re.match(r"temperatura do ponto de orvalho", name):
        return "temperatura_orvalho"
    if re.match(r"temperatura m[áa]x", name):
        return "temperatura_maxima"
    if re.match(r"temperatura m[íi]n", name):
        return "temperatura_minima"
    if re.match(r"temperatura orvalho m[áa]x", name):
        return "temperatura_orvalho_maxima"
    if re.match(r"temperatura orvalho m[íi]n", name):
        return "temperatura_orvalho_minima"
    if re.match(r"umidade rel\. m[áa]x", name):
        return "umidade_relativa_maxima"
    if re.match(r"umidade rel\. m[íi]n", name):
        return "umidade_relativa_minima"
    if re.match(r"umidade relativa do ar", name):
        return "umidade_relativa"
    if re.match(r"vento, dire[çc][ãa]o", name):
        return "vento_direcao"
    if re.match(r"vento, rajada", name):
        return "vento_rajada"
    if re.match(r"vento, velocidade", name):
        return "vento_velocidade"

Agora que temos os nomes das colunas padronizados, vamos tratar a data/hora. Os arquivos CSV do INMET-BDMEP têm duas colunas separadas para data e hora. Isso é inconveniente, pois é mais prático ter uma única coluna de data/hora. Além disso existem inconsistências nos horários, que às vezes têm minutos e às vezes não.

As três funções a seguir serão usadas para criar uma única coluna de data/hora:

def convert_dates(dates: pd.Series) -> pd.DataFrame:
    dates = dates.str.replace("/", "-")
    return dates


def convert_hours(hours: pd.Series) -> pd.DataFrame:

    def fix_hour_string(hour: str) -> str:
        if re.match(r"^\d{2}\:\d{2}$", hour):
            return hour
        else:
            return hour[:2]   ":00"

    hours = hours.apply(fix_hour_string)
    return hours


def fix_data_hora(d: pd.DataFrame) -> pd.DataFrame:
    d = d.assign(
        data_hora=pd.to_datetime(
            convert_dates(d["data"])   " "   convert_hours(d["hora"]),
            format="%Y-%m-%d %H:%M",
        ),
    )
    d = d.drop(columns=["data", "hora"])
    return d

Existe um problema com os dados do INMET-BDMEP que é a presença de linhas vazias. Vamos remover essas linhas vazias para evitar problemas futuros. O código a seguir faz isso:

# Remove empty rows
empty_columns = [
    "precipitacao",
    "pressao_atmosferica",
    "pressao_atmosferica_maxima",
    "pressao_atmosferica_minima",
    "radiacao",
    "temperatura_ar",
    "temperatura_orvalho",
    "temperatura_maxima",
    "temperatura_minima",
    "temperatura_orvalho_maxima",
    "temperatura_orvalho_minima",
    "umidade_relativa_maxima",
    "umidade_relativa_minima",
    "umidade_relativa",
    "vento_direcao",
    "vento_rajada",
    "vento_velocidade",
]
empty_rows = data[empty_columns].isnull().all(axis=1)
data = data.loc[~empty_rows]

Problema resolvido! (•̀ᴗ•́)و ̑̑

3.4 Encapsulando em funções

Para finalizar esta seção vamos encapsular o código de leitura e tratamento em funções.

Primeiro uma função para a leitura do arquivo CSV contino no arquivo comprimido.

def read_data(filepath: Path) -> pd.DataFrame:
    d = pd.read_csv(
        filepath,
        sep=";",
        decimal=",",
        na_values="-9999",
        encoding="latin-1",
        skiprows=8,
        usecols=range(19),
    )
    d = d.rename(columns=columns_renamer)

    # Remove empty rows
    empty_columns = [
        "precipitacao",
        "pressao_atmosferica",
        "pressao_atmosferica_maxima",
        "pressao_atmosferica_minima",
        "radiacao",
        "temperatura_ar",
        "temperatura_orvalho",
        "temperatura_maxima",
        "temperatura_minima",
        "temperatura_orvalho_maxima",
        "temperatura_orvalho_minima",
        "umidade_relativa_maxima",
        "umidade_relativa_minima",
        "umidade_relativa",
        "vento_direcao",
        "vento_rajada",
        "vento_velocidade",
    ]
    empty_rows = d[empty_columns].isnull().all(axis=1)
    d = d.loc[~empty_rows]

    d = fix_data_hora(d)

    return d

Tem um problema com a função acima. Ela não lida com arquivos ZIP.

Criamos, então, a função read_zipfile para a leitura de todos os arquivos contidos no arquivo ZIP. Essa função itera sobre todos os arquivos CSV no arquivo zipado, faz a leitura usando a função read_data e os metadados usando a função read_metadata, e depois junta os dados e os metadados em um único DataFrame.

def read_zipfile(filepath: Path) -> pd.DataFrame:
    data = pd.DataFrame()
    with zipfile.ZipFile(filepath) as z:
        files = [zf for zf in z.infolist() if not zf.is_dir()]
        for zf in tqdm(files):
            d = read_data(z.open(zf.filename))
            meta = read_metadata(z.open(zf.filename))
            d = d.assign(**meta)
            data = pd.concat((data, d), ignore_index=True)
    return data

No final, basta usar essa última função definida (read_zipfile) para fazer a leitura dos arquivos ZIP baixados do site do INMET. (. ❛ ᴗ ❛.)

df = reader.read_zipfile("inmet-bdmep_2023_20240102.zip")
# 100%|████████████████████████████████████████████████████████████████████████████████| 567/567 [01:46



4. Gráfico de exemplo

Para finalizar, nada mais satisfatório do que fazer gráficos com os dados que coletamos e tratamos. ヾ(≧▽≦*)o

Nessa parte uso o R com o pacote tidyverse para fazer um gráfico combinando a temperatura horária e a média diária em São Paulo.

library(tidyverse)

dados  filter(uf == "SP")


# Temperatura horária em São Paulo
dados_sp_h 
  group_by(data_hora) |>
  summarise(
    temperatura_ar = mean(temperatura_ar, na.rm = TRUE),
  )


# Temperatura média diária em São Paulo
dados_sp_d 
  group_by(data = floor_date(data_hora, "day")) |>
  summarise(
    temperatura_ar = mean(temperatura_ar, na.rm = TRUE),
  )


# Gráfico combinando temperatura horária e média diária em São Paulo
dados_sp_h |>
  ggplot(aes(x = data_hora, y = temperatura_ar))  
  geom_line(
    alpha = 0.5,
    aes(
      color = "Temperatura horária"
    )
  )  
  geom_line(
    data = dados_sp_d,
    aes(
      x = data,
      y = temperatura_ar,
      color = "Temperatura média diária"
    ),
    linewidth = 1
  )  
  labs(
    x = "Data",
    y = "Temperatura (°C)",
    title = "Temperatura horária e média diária em São Paulo",
    color = "Variável"
  )  
  theme_minimal()  
  theme(legend.position = "top")
ggsave("temperatura_sp.png", width = 16, height = 8, dpi = 300)


Coletando e Tratando os Dados Climáticos do INMET-BDMEP


Temperatura horária e média diária em São Paulo em 2023

5. Conclusão

Neste texto mostrei como coletar e tratar os dados climáticos do INMET-BDMEP. Os dados coletados são muito úteis para estudos e previsões nas mais variadas áreas. Com os dados tratados, é possível fazer análises e gráficos como o que mostrei no final.

Espero que tenha gostado do texto e que tenha sido útil para você.

Criei um pacote Python com as funções que mostrei neste texto. O pacote está disponível no meu repositório Git. Se quiser, pode baixar o pacote e usar as funções no seu próprio código.

Repositório Git: https://github.com/dankkom/inmet-bdmep-data

(~ ̄▽ ̄)~

版本声明 本文转载于:https://dev.to/dankkom/coletando-e-tratando-os-dados-climaticos-do-inmet-bdmep-1f8l?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何使用Python理解有效地创建字典?
    如何使用Python理解有效地创建字典?
    在python中,词典综合提供了一种生成新词典的简洁方法。尽管它们与列表综合相似,但存在一些显着差异。与问题所暗示的不同,您无法为钥匙创建字典理解。您必须明确指定键和值。 For example:d = {n: n**2 for n in range(5)}This creates a dicti...
    编程 发布于2025-04-06
  • 如何有效地转换PHP中的时区?
    如何有效地转换PHP中的时区?
    在PHP 利用dateTime对象和functions DateTime对象及其相应的功能别名为时区转换提供方便的方法。例如: //定义用户的时区 date_default_timezone_set('欧洲/伦敦'); //创建DateTime对象 $ dateTime = ne...
    编程 发布于2025-04-06
  • 如何限制动态大小的父元素中元素的滚动范围?
    如何限制动态大小的父元素中元素的滚动范围?
    在交互式接口中实现垂直滚动元素的CSS高度限制问题:考虑一个布局,其中我们具有与用户垂直滚动一起移动的可滚动地图div,同时与固定的固定sidebar保持一致。但是,地图的滚动无限期扩展,超过了视口的高度,阻止用户访问页面页脚。$("#map").css({ marginT...
    编程 发布于2025-04-06
  • 找到最大计数时,如何解决mySQL中的“组函数\”错误的“无效使用”?
    找到最大计数时,如何解决mySQL中的“组函数\”错误的“无效使用”?
    如何在mySQL中使用mySql 检索最大计数,您可能会遇到一个问题,您可能会在尝试使用以下命令:理解错误正确找到由名称列分组的值的最大计数,请使用以下修改后的查询: 计数(*)为c 来自EMP1 按名称组 c desc订购 限制1 查询说明 select语句提取名称列和每个名称...
    编程 发布于2025-04-06
  • 如何在Java的全屏独家模式下处理用户输入?
    如何在Java的全屏独家模式下处理用户输入?
    Handling User Input in Full Screen Exclusive Mode in JavaIntroductionWhen running a Java application in full screen exclusive mode, the usual event ha...
    编程 发布于2025-04-06
  • 如何克服PHP的功能重新定义限制?
    如何克服PHP的功能重新定义限制?
    克服PHP的函数重新定义限制在PHP中,多次定义一个相同名称的函数是一个no-no。尝试这样做,如提供的代码段所示,将导致可怕的“不能重新列出”错误。 但是,PHP工具腰带中有一个隐藏的宝石:runkit扩展。它使您能够灵活地重新定义函数。 runkit_function_renction_re...
    编程 发布于2025-04-06
  • Java是否允许多种返回类型:仔细研究通用方法?
    Java是否允许多种返回类型:仔细研究通用方法?
    在Java中的多个返回类型:一种误解类型:在Java编程中揭示,在Java编程中,Peculiar方法签名可能会出现,可能会出现,使开发人员陷入困境,使开发人员陷入困境。 getResult(string s); ,其中foo是自定义类。该方法声明似乎拥有两种返回类型:列表和E。但这确实是如此吗...
    编程 发布于2025-04-06
  • 如何在鼠标单击时编程选择DIV中的所有文本?
    如何在鼠标单击时编程选择DIV中的所有文本?
    在鼠标上选择div文本单击带有文本内容,用户如何使用单个鼠标单击单击div中的整个文本?这允许用户轻松拖放所选的文本或直接复制它。 在单个鼠标上单击的div元素中选择文本,您可以使用以下Javascript函数: function selecttext(canduterid){ if(do...
    编程 发布于2025-04-06
  • 如何实时捕获和流媒体以进行聊天机器人命令执行?
    如何实时捕获和流媒体以进行聊天机器人命令执行?
    在开发能够执行命令的chatbots的领域中,实时从命令执行实时捕获Stdout,一个常见的需求是能够检索和显示标准输出(stdout)在cath cath cant cant cant cant cant cant cant cant interfaces in Chate cant inter...
    编程 发布于2025-04-06
  • 如何使用替换指令在GO MOD中解析模块路径差异?
    如何使用替换指令在GO MOD中解析模块路径差异?
    在使用GO MOD时,在GO MOD 中克服模块路径差异时,可能会遇到冲突,其中可能会遇到一个冲突,其中3派对软件包将另一个带有导入套件的path package the Imptioned package the Imptioned package the Imported tocted pac...
    编程 发布于2025-04-06
  • Python读取CSV文件UnicodeDecodeError终极解决方法
    Python读取CSV文件UnicodeDecodeError终极解决方法
    在试图使用已内置的CSV模块读取Python中时,CSV文件中的Unicode Decode Decode Decode Decode decode Error读取,您可能会遇到错误的错误:无法解码字节 在位置2-3中:截断\ uxxxxxxxx逃脱当CSV文件包含特殊字符或Unicode的路径逃...
    编程 发布于2025-04-06
  • 如何从PHP中的Unicode字符串中有效地产生对URL友好的sl。
    如何从PHP中的Unicode字符串中有效地产生对URL友好的sl。
    为有效的slug生成首先,该函数用指定的分隔符替换所有非字母或数字字符。此步骤可确保slug遵守URL惯例。随后,它采用ICONV函数将文本简化为us-ascii兼容格式,从而允许更广泛的字符集合兼容性。接下来,该函数使用正则表达式删除了不需要的字符,例如特殊字符和空格。此步骤可确保slug仅包含...
    编程 发布于2025-04-06
  • 如何干净地删除匿名JavaScript事件处理程序?
    如何干净地删除匿名JavaScript事件处理程序?
    删除匿名事件侦听器将匿名事件侦听器添加到元素中会提供灵活性和简单性,但是当要删除它们时,可以构成挑战,而无需替换元素本身就可以替换一个问题。 element? element.addeventlistener(event,function(){/在这里工作/},false); 要解决此问题,请考虑...
    编程 发布于2025-04-06
  • 如何使用Regex在PHP中有效地提取括号内的文本
    如何使用Regex在PHP中有效地提取括号内的文本
    php:在括号内提取文本在处理括号内的文本时,找到最有效的解决方案是必不可少的。一种方法是利用PHP的字符串操作函数,如下所示: 作为替代 $ text ='忽略除此之外的一切(text)'; preg_match('#((。 &&& [Regex使用模式来搜索特...
    编程 发布于2025-04-06
  • 为什么我在Silverlight Linq查询中获得“无法找到查询模式的实现”错误?
    为什么我在Silverlight Linq查询中获得“无法找到查询模式的实现”错误?
    查询模式实现缺失:解决“无法找到”错误在Silverlight应用程序中,尝试使用LINQ建立LINQ连接以错误而实现的数据库”,无法找到查询模式的实现。”当省略LINQ名称空间或查询类型缺少IEnumerable 实现时,通常会发生此错误。 解决问题来验证该类型的质量是至关重要的。在此特定实例中...
    编程 发布于2025-04-06

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3