首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >动态地在DbSet中查找泛型DbContext

动态地在DbSet中查找泛型DbContext
EN

Stack Overflow用户
提问于 2015-11-26 13:56:01
回答 2查看 13.3K关注 0票数 10

我知道这个问题已经问过了,但我找不到一个让我满意的答案。我想要做的是根据特定的DbSet<T>类型的名称检索它。

我有以下几点:

代码语言:javascript
复制
[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;
    }
}

无论是通过简单的切换还是反射,我都可以获得类型本身。但是,我需要将该类型作为动态类型返回,因为我不知道它将是什么DbSet类型。然后,在同一个程序集中的其他地方,我就这样使用它:

代码语言:javascript
复制
// MyDbContext MyDbContextInstance..
var model = MyDbContextInstance.GetByName_SwitchTest("A");
var record1 = model.FirstOrDefault(); // It crashes here with RunTimeBinderException

此时,model包含一个InternalDbSet<ModelA>类型的实例。在那里,我对model对象所做的任何使用都会得到一个RunTimeBinderException :RunTimeBinderException不包含“FirstOrDefault”的定义

在网上调查时,我发现一个博客帖子解释说(他的博客被撤销了):

调用FirstOrDefault()失败的原因是模型的类型信息在运行时不可用。它不可用的原因是匿名类型不公开。当该方法返回该匿名类型的实例时,它返回一个System.Object,该实例引用一个匿名类型的实例--该类型的信息对主程序不可用。

然后他指出了一个解决方案:

解决办法其实很简单。我们所要做的就是打开ClassLibrary1项目的ClassLibrary1,并向其添加以下行:[assembly:InternalsVisibleTo("assembly-name")]

我确实在我的代码上尝试过这个解决方案,但是它不起作用。关于信息,我有一个asp.net 5解决方案,其中两个程序集运行在dnx dotnet46上。一个应用程序和一个dll包含了我的所有模型和DbContext。不过,我所做的所有相关调用都位于dll上。

这个解决方案有机会起作用吗?我漏掉了什么吗?有什么建议会很感激的吗?

提前感谢

编辑

我尝试返回IQueryable<dynamic>而不是dynamic,我可以执行基本的查询model.FirstOrDefault(); ,但最重要的是,也希望能够在字段上进行筛选:

代码语言:javascript
复制
var record = model.FirstOrDefault(item => item.MyProperty == true);
EN

回答 2

Stack Overflow用户

发布于 2015-11-26 14:26:27

*免责声明:这个回答并没有给我的问题一个严格的感觉答案。这是解决我自己问题的另一种方法。我知道,这是一个特定的例子,一个特定的情况,不会对每个人都适用。我张贴这个方法是希望它能帮助别人,但不会把它标记为答案,因为我仍然希望找到一个真正的解决方案。

首先,让我们接受这样一个事实:我们可以从当前代码中获得的唯一有用信息是记录是否存在。在此之后,任何动态查询的尝试都会给RuntimeBinderException带来好处。

然后让我们继续讨论另一个事实:DbContext.Add(对象)和DbContext.Update(对象)不是基于模板的,因此我们可以使用它们来保存模型(而不是db.A.Add()或db.A.Update() )

在我自己的情况下,不需要再制定一个程序。

  1. 定义模型有点不同

首先,我需要一个可以跨所有模型检索的字段,这显然应该是识别唯一记录的一种方法。

代码语言:javascript
复制
// 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; }
}
  1. 重新使用动态查询来完成我们知道的工作。

我还没有找到一种动态地执行智能查询的方法,所以我知道我可以可靠地识别一个记录,并知道它是否存在。

代码语言:javascript
复制
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);
    }
}
  1. 一点水管,让我对自己的处境有所了解

接下来,我需要使用动态查询将数据从服务器复制到我的客户端。(为了简化这个示例,我省略了一大块体系结构)

代码语言:javascript
复制
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
}
  1. 连接比特。

现在,下面是连接比特的例程

代码语言:javascript
复制
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; }
}

ps:我仍然对一个真正的答案感兴趣,所以如果你有一个真实的答案,请继续张贴其他答案。

票数 0
EN

Stack Overflow用户

发布于 2015-12-14 15:36:56

那么,当我在编译时不知道<T>时,我是如何做到的呢?

首先,需要获取类型,因为DbContext.Set方法返回一个非泛型的DbSet实例,用于访问上下文和底层存储中给定类型的实体。

代码语言:javascript
复制
public virtual DbSet Set(Type entityType)

注在这里,参数是一个集合应该是returned.And集合的实体类型,给定的实体类型是返回值。

代码语言:javascript
复制
var type = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(t => t.Name == <Pass your table name>);

一旦我有了这种类型

代码语言:javascript
复制
if(type != null)
{
DbSet context = context.Set(type);
}

或者一个班轮

代码语言:javascript
复制
DbSet mySet = context.Set(Type.GetType("<Your Entity Name>"));
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33940507

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档