gpt4 book ai didi

c# - 在运行时动态生成 'myType' 时,歧义鉴别器 'myType'

转载 作者:可可西里 更新时间:2023-11-01 10:00:52 32 4
gpt4 key购买 nike

我有一个在运行时动态创建新类型的应用程序,创建该类型的对象并将它们插入 object 类型的 MongoDB 数据库集合中。使用 shell 我可以看到对象被正确插入并且 _t 值是动态创建的类的正确名称。

我正在尝试使用 AsQueryable 从我的集合中检索对象,同时应用 LINQ 查询将结果过滤为特定类型的对象。

这很好用:

_collection.AsQueryable<object>();

同时:

_collection.AsQueryable<object>().Where(t => t.GetType() == type);

抛出异常:

Ambiguous discriminator 'myType'
at MongoDB.Bson.Serialization.BsonSerializer.LookupActualType(Type nominalType, BsonValue discriminator)
at MongoDB.Bson.Serialization.Conventions.StandardDiscriminatorConvention.GetActualType(BsonReader bsonReader, Type nominalType)
at MongoDB.Bson.Serialization.Serializers.ObjectSerializer.Deserialize(BsonReader bsonReader, Type nominalType, IBsonSerializationOptions options)
at MongoDB.Driver.Internal.MongoReplyMessage`1.ReadBodyFrom(BsonBuffer buffer)
at MongoDB.Driver.Internal.MongoReplyMessage`1.ReadFrom(BsonBuffer buffer)
at MongoDB.Driver.Internal.MongoConnection.ReceiveMessage[TDocument](BsonBinaryReaderSettings readerSettings, IBsonSerializer serializer, IBsonSerializationOptions serializationOptions)
at MongoDB.Driver.Operations.QueryOperation`1.GetFirstBatch(IConnectionProvider connectionProvider)
at MongoDB.Driver.Operations.QueryOperation`1.Execute(IConnectionProvider connectionProvider)
at MongoDB.Driver.MongoCursor`1.GetEnumerator()
at MongoDB.Driver.Linq.IdentityProjector`1.GetEnumerator()
at System.Linq.Enumerable.First[TSource](IEnumerable`1 source)
at MongoDB.Driver.Linq.SelectQuery.<TranslateFirstOrSingle>b__a(IEnumerable source)
at MongoDB.Driver.Linq.SelectQuery.Execute()
at MongoDB.Driver.Linq.MongoQueryProvider.Execute(Expression expression)
at MongoDB.Driver.Linq.MongoQueryProvider.Execute[TResult](Expression expression)
at System.Linq.Queryable.First[TSource](IQueryable`1 source)
at MongoDBTest.Program.RetreiveTransaction(String transactionType, Int32 version) in c:\projects\mrp\trunk\Source\POC\MongoDBTest\MongoDBTest\Program.cs:line 192
at MongoDBTest.Program.DynamicDBExample() in c:\projects\mrp\trunk\Source\POC\MongoDBTest\MongoDBTest\Program.cs:line 163
at MongoDBTest.Program.Main(String[] args) in c:\projects\mrp\trunk\Source\POC\MongoDBTest\MongoDBTest\Program.cs:line 28
at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()

当类型是我动态生成的类型时(它适用于其他类型)。

这也行得通:

_collection.FindAs<object>(Query.EQ("_t", type.Name)).AsQueryable();

但不幸的是,它从数据库返回该类型的所有文档,然后任何 LINQ 查询都在本地而不是在数据库中执行,这不是我想要的。


这是我用来在运行时创建类型的代码:

public static Type CompileResultType(string className, Dictionary<string, string> fields)
{
TypeBuilder tb = GetTypeBuilder(className);
ConstructorBuilder constructor = tb.DefineDefaultConstructor(MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName);

foreach (var field in fields)
{
CreateProperty(tb, field.Key, Type.GetType(field.Value));
}

Type objectType = tb.CreateType();
return objectType;
}

private static TypeBuilder GetTypeBuilder(string className)
{
var an = new AssemblyName("DynamicClassAssembly");
AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
TypeBuilder tb = moduleBuilder.DefineType("DynamicClassNamespace."+className
, TypeAttributes.Public |
TypeAttributes.Class |
TypeAttributes.AutoClass |
TypeAttributes.AnsiClass |
TypeAttributes.BeforeFieldInit |
TypeAttributes.AutoLayout
, null);
return tb;
}

private static void CreateProperty(TypeBuilder tb, string propertyName, Type propertyType)
{
FieldBuilder fieldBuilder = tb.DefineField("_" + propertyName, propertyType, FieldAttributes.Private);

PropertyBuilder propertyBuilder = tb.DefineProperty(propertyName, PropertyAttributes.HasDefault, propertyType, null);
MethodBuilder getPropMthdBldr = tb.DefineMethod("get_" + propertyName, MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig, propertyType, Type.EmptyTypes);
ILGenerator getIl = getPropMthdBldr.GetILGenerator();

getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
getIl.Emit(OpCodes.Ret);

MethodBuilder setPropMthdBldr =
tb.DefineMethod("set_" + propertyName,
MethodAttributes.Public |
MethodAttributes.SpecialName |
MethodAttributes.HideBySig,
null, new[] { propertyType });

ILGenerator setIl = setPropMthdBldr.GetILGenerator();
Label modifyProperty = setIl.DefineLabel();
Label exitSet = setIl.DefineLabel();

setIl.MarkLabel(modifyProperty);
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldarg_1);
setIl.Emit(OpCodes.Stfld, fieldBuilder);

setIl.Emit(OpCodes.Nop);
setIl.MarkLabel(exitSet);
setIl.Emit(OpCodes.Ret);

propertyBuilder.SetGetMethod(getPropMthdBldr);
propertyBuilder.SetSetMethod(setPropMthdBldr);
}

展示此行为的完整代码:

static void ProcessTransaction(IncomingTransaction input, string transactionType, int version)
{
var configuration = GetConfiguration(transactionType, version);

//configuration.Fields is just a Dictionary of field names -> types
Type dynamicType = DynamicClassHelper.CompileResultType(transactionType + version, configuration.Fields);

object transaction = Activator.CreateInstance(dynamicType);

//AutoMapper, just populates the data on transaction object
Mapper.DynamicMap(input, transaction, typeof(IncomingTransaction), transaction.GetType());

//Just a wrapper around MongoDB, creates a MongoCollection<object>
var db = new MongoTransactionDB<object>(connectionString, databaseName, "transactions", new ConsoleLogger());

//just calls Insert() on the collection
db.AddTransaction(transaction);
}

static void RetreiveTransaction(string transactionType, int version)
{
var db = new MongoTransactionDB<object>(connectionString, databaseName, "transactions", new ConsoleLogger());

var config = GetConfiguration(transactionType, version);

Type dynamicType = DynamicClassHelper.CompileResultType(transactionType + version, config.Fields);

//!!! This is where the exception is thrown !!!
var result = db.GetAllTransactionsOfType(dynamicType).First();
}

//From MongoTransactionDB class...
public IQueryable<TTransactionBase> GetAllTransactionsOfType(Type type)
{
return _collection.AsQueryable().Where(t => t.GetType() == type);
}

将动态对象插入 MongoDB 后的结果(transactionType = "Cash"version = 1):

enter image description here

最佳答案

您始终可以使用 Inject 将 mongo 查询与 LINQ 结合使用并让它们在 MongoDB 而不是客户端中运行:

var queryable = _collection.AsQueryable().Where(_ => Query.EQ("_t", type.Name).Inject()).Where(...);

Inject is a pseudo-method that is used to inject a lower level MongoDB query into a LINQ query.

关于c# - 在运行时动态生成 'myType' 时,歧义鉴别器 'myType',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25187477/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com