【问题标题】:C# How to use DbContext.Database.SqlQuery with abstract/derived classesC# 如何将 DbContext.Database.SqlQuery 与抽象/派生类一起使用
【发布时间】:2017-04-11 15:28:32
【问题描述】:

所以,我的情况是我正在使用 EF6 和一个组件表,它使用每个层次结构模式的表。我有一个抽象的 Component 类和一些继承它的类,例如 FirstNameComponent。

调用看起来像这样:

var sql = "Select * from Components";
var components = DbContext.Database.SqlQuery<Component>(sql).ToList();

在这种情况下,我必须使用 SqlQuery 关闭 DbContextDbSet

如您所料,这会查询出表中的所有组件并尝试将它们映射到 Component 类,但由于它是抽象的,因此会引发异常。

有没有办法使用自定义模型绑定器或表上带有鉴别器列的东西来告诉 SqlQuery 调用每个组件要使用哪些类?或者用不同的方式将行序列化/反序列化到组件类中?

【问题讨论】:

    标签: c# entity-framework


    【解决方案1】:

    你可以在你的抽象类中定义一个鉴别器属性(基本上是你想要作为鉴别器的列的名称,作为一个字符串)

    说,您将Component 类作为抽象类,而FirstNameComponent 正在继承它。

    所以,你希望你的 Component 类看起来像 -

    public abstract class Component
    {
        public const string Discriminator = "ComponentType";
    
        // Other properties/methods
    }
    

    现在,您的 FirstNameComponent 课程看起来像 -

    public class FirstNameComponent : Component
    {
        public const string TypeOfComponent = nameof(FirstNameComponent);
    
        public override string ComponentType // your discriminator column
        {
            get
            {
                return TypeOfComponent;
            }
        }
        // Other properties/methods
    }
    

    并在使用上下文中的流式 API 构建模型时使用此属性..

    public class ComponentContext: DbContext
    {
        public DbSet<FirstNameComponent> FirstNameComponents { get; set; }
    
        // Other mappings 
    
        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Entity<Component>()
                .Map<FirstNameComponent>(
                    configuration =>
                    configuration.Requires(Component.Discriminator).HasValue(FirstNameComponent.TypeOfComponent))
        }
    }
    

    因此,您的查询将类似于 -

    var components = ComponentContext.Components.OfType<FirstNameComponent>();
    

    甚至——

    var components = ComponentContext.Components.Where(c => c is FirstNameComponent).ToList();
    

    更新

    看看你更新的问题,这里是你如何重组你的查询来获取特定类型的Component -

    string sqlQuery = @"Select * from Components Where ComponentType = @componentType";
    string componentType = nameof(FirstNameComponent); //Can be made more dynamic
    
    // Pass the type of component you need to cast to as a parameter
    SqlParameter parameter = new SqlParameter("@componentType", componentType);
    
    var components = DbContext.Database.SqlQuery<FirstNameComponent>(sqlQuery, parameter);
    

    这应该会给你想要的结果。

    更新 2

    因此,在对此进行了更多研究之后,我发现了 this 博客,其中讨论了使用 Linq to Entities 和 EntitySQL 进行多态查询。

    根据参考资料,您可以尝试另一种方法来获取所有Components -

    string sqlQuery = @"SELECT VALUE c FROM Components AS c";
    System.Data.Entity.Core.Objects.ObjectContext objectContext = ((IObjectContextAdapter)ComponentContext).ObjectContext;
    System.Data.Entity.Core.Objects.ObjectQuery<Component> objectQuery = objectContext.CreateQuery<Component>(sqlQuery);
    List<Component> components = objectQuery.ToList<Component>();
    

    一旦你拥有了所有的Components,你就可以在ComponentType 字段上使用一个简单的Where 子句过滤出所需的类型 -

    var firstNameComponents = components.Where(c => c.ComponentType == nameof(FirstNameComponent)).ToList();
    

    【讨论】:

    • 所以,在这种情况下,我需要使用SqlQuery 关闭DbContext.DatabaseDbSet,因为我需要使用自定义sql 来获取组件。您能否提供一个适用于您布置的映射和SqlQuery 的解决方案?
    • @KevDev424 - 看看我更新的答案...正如我所提到的,您可以编写一个更动态的程序来在运行时解析component 的类型...
    • 这看起来不错,但是如果我在进行查询时不知道类型并且想像默认情况下那样依赖于它自己的上下文映射怎么办?我想使用自定义 sql 获取所有组件,并让抽象到具体的组件映射自动工作。我可能要求太多了。
    • hmmm...我明白您的意思...但是由于您无法实例化抽象类,因此没有直接方法可以同时获取所有components go 然后转换为派生类型。这给你留下了两个选择(虽然可能还有更多..) - 改变你的方法,以便在查询派生类型之前知道你需要强制转换为 OR 使用自动映射器,例如stackoverflow.com/questions/15973258/automapper-and-base-types(他们虽然没有使用过 SqlQuery)
    • 我会将此作为答案,因为我认为这是我能做的最接近的事情。不过,我改变了我的方法,对组件 ID 进行了一次自定义查询,然后使用 DbSet 和带有 ID 的 where 子句进行第二次查询,以获取每个派生类的自定义映射。我认为在一个自定义查询中没有一种好方法可以做到这一点。
    猜你喜欢
    • 2011-02-01
    • 1970-01-01
    • 2020-10-06
    • 1970-01-01
    • 1970-01-01
    • 2011-03-07
    • 1970-01-01
    • 2021-08-21
    • 2011-11-02
    相关资源
    最近更新 更多