英文:
How do I fix `System.MissingMethodException: Constructor on type Microsoft.EntityFrameworkCore.DbSet`1 not found.`?
问题
使用EntityFramework DbContext在.NET 6中将数据库表导出为CSV
我被要求创建一个Blazor ASP.NET Core应用程序,将从MS SQL Server数据库中的每个表导出到自己的.csv
文件中。我已将DbContext
属性中的DbSets
提取到一个通用列表中,但当我尝试将通用对象转换为它们的DbSet
类时,出现以下错误:
在处理请求时发生未经处理的异常。
MissingMethodException: 类型 'Microsoft.EntityFrameworkCore.DbSet`1[[DatabaseTableExport.Data.LoginDbModels.AccountPasswordHistory, DatabaseTableExport, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' 上的构造函数未找到。
System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, object[] args, CultureInfo culture)
如何修复此错误,或者是否有更好的方法从DbContext
中提取DbSets
?
using DatabaseTableExport.Data;
using DatabaseTableExport.Data.LoginDbModels;
using Microsoft.AspNetCore.Components;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace DatabaseTableExport.Pages
{
public partial class Index : ComponentBase
{
[Inject]
private LoginDbContext LoginDbContext { get; set; }
[Inject]
private ApplicationDbContext ApplicationDbContext { get; set; }
protected override void OnInitialized()
{
List<DbSet<object>> dbSets = GetDbSets(LoginDbContext);
// 遍历每个DbSet实例
foreach (var dbSet in dbSets)
{
// 在这里导出到csv
}
}
private static List<DbSet<object>> GetDbSets(DbContext loginDbContext)
{
// 检索上下文类上声明的所有DbSet属性。
var dbSetProperties = loginDbContext.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
.Where(p => p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
.ToList();
// 遍历每个DbSet属性,并将其添加到dbsets列表中。
var dbSets = new List<DbSet<object>>();
foreach (var property in dbSetProperties)
{
// 如果DbSet的类型为空,请跳过。
if (property.PropertyType.GenericTypeArguments.Length <= 0)
{
continue;
}
// 获取泛型类型参数,并创建相应的DbSet实例。
var dbsetGenericType = property.PropertyType.GenericTypeArguments[0];
var convertedDbSet = Activator.CreateInstance(typeof(DbSet<>).MakeGenericType(dbsetGenericType), property.GetValue(loginDbContext));
dbSets.Add((DbSet<object>)convertedDbSet);
}
// 返回DbSet对象列表。
return dbSets;
}
}
}
英文:
Database Table Export to CSV using EntityFramework DbContext .NET 6
I have been tasked to create a Blazor ASP.NET Core application that will export each table from an MS SQL Server database into it's own .csv
file. I have extracted the DbSets
from the DbContext
properties into a generic list, but when I attempt to cast the generic objects to their DbSet
class I get the following error:
> An unhandled exception occurred while processing the request.
> MissingMethodException: Constructor on type
> 'Microsoft.EntityFrameworkCore.DbSet`1[[DatabaseTableExport.Data.LoginDbModels.AccountPasswordHistory,
> DatabaseTableExport, Version=1.0.0.0, Culture=neutral,
> PublicKeyToken=null]]' not found.
> System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder
> binder, object[] args, CultureInfo culture)
How do I fix this error, or is there a better way to be extracting the DbSets
from the DbContext
?
using DatabaseTableExport.Data;
using DatabaseTableExport.Data.LoginDbModels;
using Microsoft.AspNetCore.Components;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace DatabaseTableExport.Pages
{
public partial class Index : ComponentBase
{
[Inject]
private LoginDbContext LoginDbContext { get; set; }
[Inject]
private ApplicationDbContext ApplicationDbContext { get; set; }
protected override void OnInitialized()
{
List<DbSet<object>> dbSets = GetDbSets(LoginDbContext);
// iterate through each DbSet instance
foreach (var dbSet in dbSets)
{
// export to csv here
}
}
private static List<DbSet<object>> GetDbSets(DbContext loginDbContext)
{
// Retrieve all the DbSet properties declared on the context class.
var dbSetProperties = loginDbContext.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
.Where(p => p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>))
.ToList();
// Iterate over each DbSet property and add it to the list of dbsets.
var dbSets = new List<DbSet<object>>();
foreach (var property in dbSetProperties)
{
// If the type of the DbSet is null, skip it.
if (property.PropertyType.GenericTypeArguments.Length <= 0)
{
continue;
}
// Get the generic type arguments and create a corresponding DbSet instance.
var dbsetGenericType = property.PropertyType.GenericTypeArguments[0];
var convertedDbSet = Activator.CreateInstance(typeof(DbSet<>).MakeGenericType(dbsetGenericType), property.GetValue(loginDbContext));
dbSets.Add((DbSet<object>)convertedDbSet);
}
// Return the list of DbSet objects.
return dbSets;
}
}
}
答案1
得分: 1
以下是您要翻译的内容:
"EF Core works without DbContext
properties. Metadata information can be retrieved from DbContext's Model and Export can be initiated via CallBack."
回调接口:
public interface IRecordsetProcessor
{
void ProcessRecordset<T>(DbContext context, IQueryable<T> recordset, IEntityType entityType)
where T: class;
}
Recordset枚举实现:
public static class DbContextUtils
{
public static void ProcessRecordset<T>(DbContext context, IEntityType entityType, IRecordsetProcessor processor)
where T : class
{
processor.ProcessRecordset(context, context.Set<T>(), entityType);
}
private static MethodInfo _processMethod = typeof(DbContextUtils).GetMethod(nameof(ProcessRecordset))!;
public static void ProcessRecordsets(DbContext context, IRecordsetProcessor processor)
{
var entityTypes = context.Model.GetEntityTypes()
.Where(et => !et.IsOwned())
.ToList();
foreach (var et in entityTypes)
{
_processMethod.MakeGenericMethod(et.ClrType).Invoke(null, new object[] { context, et, processor });
}
}
}
导出的简单示例实现:
public class ExportProcessor : IRecordsetProcessor
{
private const string ColumnSeparator = ","
static string PreparePropertyValue(IProperty prop, object record)
{
var clrValue = prop.GetGetter().GetClrValue(record);
// without converter
var value = clrValue;
// with converter
/*
var converter = prop.GetValueConverter();
var value = converter == null ? clrValue : converter.ConvertToProvider(clrValue);
*/
if (value == null)
return ""
var strValue = value.ToString() ?? ""
if (strValue.Contains(ColumnSeparator) || strValue.Contains('\"'))
{
strValue = "\"" + strValue.Replace("\"", "\"\"") + "\""
}
return strValue;
}
public void ProcessRecordset<T>(DbContext context, IQueryable<T> recordset, IEntityType entityType) where T : class
{
var exportItems = new List<string>();
var properties = entityType.GetProperties().ToList();
// produce header
exportItems.Add(string.Join(ColumnSeparator, properties.Select(p => p.GetColumnName())));
foreach (var record in recordset.AsNoTracking())
{
exportItems.Add(string.Join(ColumnSeparator, properties.Select(p => PreparePropertyValue(p, record))));
}
// TODO: save to file, or do that without intermediate list
}
}
使用示例:
DbContextUtils.ProcessRecordsets(context, new ExportProcessor());
英文:
I would suggest different approach. EF Core works without DbContext
properties. Metadata information can be retrieved from DbContext's Model and Export can be initiated via CallBack.
Callback interface:
public interface IRecordsetProcessor
{
void ProcessRecordset<T>(DbContext context, IQueryable<T> recordset, IEntityType entityType)
where T: class;
}
Recordset enumeration Implemantation:
public static class DbContextUtils
{
public static void ProcessRecordset<T>(DbContext context, IEntityType entityType, IRecordsetProcessor processor)
where T : class
{
processor.ProcessRecordset(context, context.Set<T>(), entityType);
}
private static MethodInfo _processMethod = typeof(DbContextUtils).GetMethod(nameof(ProcessRecordset))!;
public static void ProcessRecordsets(DbContext context, IRecordsetProcessor processor)
{
var entityTypes = context.Model.GetEntityTypes()
.Where(et => !et.IsOwned())
.ToList();
foreach (var et in entityTypes)
{
_processMethod.MakeGenericMethod(et.ClrType).Invoke(null, new object[] { context, et, processor });
}
}
}
Simple sample implementation of export:
public class ExportProcessor : IRecordsetProcessor
{
private const string ColumnSeparator = ",";
static string PreparePropertyValue(IProperty prop, object record)
{
var clrValue = prop.GetGetter().GetClrValue(record);
// without converter
var value = clrValue;
// with converter
/*
var converter = prop.GetValueConverter();
var value = converter == null ? clrValue : converter.ConvertToProvider(clrValue);
*/
if (value == null)
return "";
var strValue = value.ToString() ?? "";
if (strValue.Contains(ColumnSeparator) || strValue.Contains('\"'))
{
strValue = "\"" + strValue.Replace("\"", "\"\"") + "\"";
}
return strValue;
}
public void ProcessRecordset<T>(DbContext context, IQueryable<T> recordset, IEntityType entityType) where T : class
{
var exportItems = new List<string>();
var properties = entityType.GetProperties().ToList();
// produce header
exportItems.Add(string.Join(ColumnSeparator, properties.Select(p => p.GetColumnName())));
foreach (var record in recordset.AsNoTracking())
{
exportItems.Add(string.Join(ColumnSeparator, properties.Select(p => PreparePropertyValue(p, record))));
}
// TODO: save to file, or do that without intermediate list
}
}
Usage sample:
DbContextUtils.ProcessRecordsets(context, new ExportProcessor());
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论