Dar*_*rse 5 .net c# asp.net-mvc-5 entity-framework-core
我知道这个问题已被提出,但我找不到让我满意的答案.我想要做的是DbSet<T>根据类型的名称检索一个特定的.
我有以下内容:
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("MyDllAssemblyName")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("MyCallingAssemblyName")]
class MyDbContext : DbContext {
public DbSet<ModelA> A { get; set; }
public DbSet<ModelB> B { get; set; }
public dynamic GetByName_SwitchTest(string name) {
switch (name) {
case "A": return A;
case "B": return B;
}
}
public dynamic GetByName_ReflectionTest(string fullname)
{
Type targetType = Type.GetType(fullname);
var model = GetType()
.GetRuntimeProperties()
.Where(o =>
o.PropertyType.IsGenericType &&
o.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>) &&
o.PropertyType.GenericTypeArguments.Contains(targetType))
.FirstOrDefault();
if (null != model)
return model.GetValue(this);
return null;
}
}
Run Code Online (Sandbox Code Playgroud)
无论是通过简单的开关还是反射,我都可以轻松获取类型.然而,我需要将类型作为动态返回,因为我不知道它将是什么类型的DbSet.然后在同一个程序集中的其他地方,我这样使用它:
// MyDbContext MyDbContextInstance..
var model = MyDbContextInstance.GetByName_SwitchTest("A");
var record1 = model.FirstOrDefault(); // It crashes here with RunTimeBinderException
Run Code Online (Sandbox Code Playgroud)
此时model包含一个InternalDbSet<ModelA>类型的实例.从那里,我对model对象的任何使用我得到一个RunTimeBinderException:'Microsoft.Data.Entity.Internal.InternalDbSet'不包含'FirstOrDefault'的定义
在网上调查,我发现了一篇博客文章解释说(dixit他的博客):
调用FirstOrDefault()失败的原因是模型的类型信息在运行时不可用.它不可用的原因是因为匿名类型不公开.当该方法返回该匿名类型的实例时,它返回一个System.Object,该System.Object引用匿名类型的实例 - 该类型的信息不可用于主程序.
然后他指出了一个解决方案:
解决方案实际上非常简单.我们所要做的就是打开ClassLibrary1项目的AssemplyInfo.cs并向其添加以下行:
[assembly:InternalsVisibleTo("assembly-name")]
我确实在我的代码上尝试了这个解决方案,但它不起作用.有关信息,我有一个asp.net 5解决方案,在dnx dotnet46上运行两个程序集.一个app和一个包含我所有模型和DbContext的dll.我所做的所有相关电话都位于dll上.
这个解决方案有没有机会工作?我错过了什么吗?任何指针都会非常感激?
提前致谢
[编辑]
我试图返回IQueryable<dynamic>而不是dynamic我可以做基本查询model.FirstOrDefault(); 但最重要的是我也希望能够过滤字段:
var record = model.FirstOrDefault(item => item.MyProperty == true);
Run Code Online (Sandbox Code Playgroud)
*免责声明:此回复并未对我的问题给出严格意义上的答案。这是解决我自己的问题的一种不同的方法。我知道这是针对特定情况的具体示例,并不适合所有人。我发布这种方法是希望它对某人有所帮助,但不会将其标记为答案,因为我仍然希望找到真正的解决方案。
首先,让我们接受这样一个事实:我们可以从当前代码中获取的唯一有用信息是记录是否存在。此后的任何动态查询尝试都会给出 RuntimeBinderException。
然后我们继续讲另一个事实;DbContext.Add(object) 和 DbContext.Update(object) 不是基于模板的,因此我们可以使用它们来保存我们的模型(而不是 db.A.Add() 或 db.A.Update() )
就我自己的情况而言,不需要再制定程序了
首先,我需要一个可在所有模型中检索的字段,这显然应该是识别唯一记录的一种方法。
// IModel give me a reliable common field to all my models ( Fits my DB design maybe not yours though )
interface IModel { Guid Id { get; set; } }
// ModelA inherit IModel so that I always have access to an 'Id'
class ModelA : IModel {
public Guid Id { get; set; }
public int OtherField { get; set; }
}
// ModelB inherit IModel so that I always have access to an 'Id'
class ModelB : IModel {
public Guid Id { get; set; }
public string WhateverOtherField { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
我还没有找到一种动态执行智能查询的方法,所以我知道我可以可靠地识别记录并知道它是否存在。
class MyDbContext : DbContext {
public DbSet<ModelA> A { get; set; }
public DbSet<ModelB> B { get; set; }
// In my case, this method help me to know the next action I need to do
// The switch/case option is not pretty but might have better performance
// than Reflection. Anyhow, this is one's choice.
public bool HasRecord_SwitchTest(string name) {
switch (name) {
case "A": return A.AsNoTracking().Any(o => o.Id == id);
case "B": return B.AsNoTracking().Any(o => o.Id == id);
}
return false;
}
// In my case, this method help me to know the next action I need to do
public bool HasRecord_ReflectionTest(string fullname)
{
Type targetType = Type.GetType(fullname);
var model = GetType()
.GetRuntimeProperties()
.Where(o =>
o.PropertyType.IsGenericType &&
o.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>) &&
o.PropertyType.GenericTypeArguments.Contains(targetType))
.FirstOrDefault();
if (null != model)
return (bool)model.GetValue(this).AsNoTracking().Any(o => o.Id == id);
return false;
}
// Update and save immediately - simplified for example
public async Task<bool> UpdateDynamic(object content)
{
EntityEntry entry = Update(content, GraphBehavior.SingleObject);
return 1 == await SaveChangesAsync(true);
}
// Insert and save immediately - simplified for example
public async Task<bool> InsertDynamic(object content)
{
EntityEntry entry = Add(content, GraphBehavior.SingleObject);
return 1 == await SaveChangesAsync(true);
}
}
Run Code Online (Sandbox Code Playgroud)
接下来,我需要使用动态查询将数据从服务器复制到客户端。(为了简化这个例子,我省略了很大一部分架构)
class ReplicationItem
{
public ReplicationAction Action { get; set; } // = Create, Update, Delete
public string ModelName { get; set; } // Model name
public Guid Id { get; set; } // Unique identified across whole platform
}
Run Code Online (Sandbox Code Playgroud)
现在,这是连接各个位的例程
public async void ProcessReplicationItem(ReplicationItem replicationItem)
{
using (var db = new MyDbContext())
{
// Custom method that attempts to get remote value by Model Name and Id
// This is where I get the strongly typed object
var remoteRecord = await TryGetAsync(replicationItem.ModelName, replicationItem.Id);
bool hasRemoteRecord = remoteRecord.Content != null;
// Get to know if a local copy of this record exists.
bool hasLocalRecord = db.HasRecord_ReflectionTest(replicationItem.ModelName, replicationItem.Id);
// Ensure response is valid whether it is a successful get or error is meaningful ( ie. NotFound )
if (remoteRecord.Success || remoteRecord.ResponseCode == System.Net.HttpStatusCode.NotFound)
{
switch (replicationItem.Action)
{
case ReplicationAction.Create:
{
if (hasRemoteRecord)
{
if (hasLocalRecord)
await db.UpdateDynamic(remoteRecord.Content);
else
await db.InsertDynamic(remoteRecord.Content);
}
// else - Do nothing
break;
}
case ReplicationAction.Update:
[etc...]
}
}
}
}
// Get record from server and with 'response.Content.ReadAsAsync' type it
// already to the appropriately
public static async Task<Response> TryGetAsync(ReplicationItem item)
{
if (string.IsNullOrWhiteSpace(item.ModelName))
{
throw new ArgumentException("Missing a model name", nameof(item));
}
if (item.Id == Guid.Empty)
{
throw new ArgumentException("Missing a primary key", nameof(item));
}
// This black box, just extrapolate a uri based on model name and id
// typically "api/ModelA/{the-guid}"
string uri = GetPathFromMessage(item);
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("http://localhost:12345");
HttpResponseMessage response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
return new Response()
{
Content = await response.Content.ReadAsAsync(Type.GetType(item.ModelName)),
Success = true,
ResponseCode = response.StatusCode
};
}
else
{
return new Response()
{
Success = false,
ResponseCode = response.StatusCode
};
}
}
}
public class Response
{
public object Content { get; set; }
public bool Success { get; set; }
public HttpStatusCode ResponseCode { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
ps:我仍然对真正的答案感兴趣,所以如果您有真正的答案可以分享,请继续发布其他答案。