【问题标题】:How can I avoid NotSupportedException?如何避免 NotSupportedException?
【发布时间】:2019-02-25 13:46:05
【问题描述】:

我把这篇文章加红了:The entity cannot be constructed in a LINQ to Entities query,但猜想它与我的问题并不完全相关。

我有这个代码:

public class Class1
{
        public string Prop1 { get; set; }
        public string Prop2 { get; set; }
        public string Prop3 { get; set; }
}

[Table("Class2")]
public class Class2
{
        [Key]
        [Column("Prop1")]
        public string Prop1 { get; set; }

        [Column("Prop2")]
        public string Prop2 { get; set; }

        [Column("Prop3")]
        public string Prop3 { get; set; }
}

和方法编辑我想在哪里使用这些:

using (var data = new Context())
{
    var config = data.Class2.FirstOrDefault(c => c.Prop1.Contains(some_string));
    if (config != null)
    {
        config.Prop1 = class1_instance.Prop1;
        config.Prop2 = class1_instance.Prop2;
        config.Prop3 = class1_instance.Prop3;
    }
    data.Entry(config).State = EntityState.Modified;
    data.SaveChanges();
}

所以我想要得到的是简化编辑,而不是一个一个地分配每个属性我想写类似config = class1_instance;的东西

所以我从Class2 继承了Class1,但是得到了

System.NotSupportedException(不能在 Linq to Entities 查询中构造实体或复杂类型“Class1”)。

我该如何处理?

【问题讨论】:

  • 你必须在 Class1 和 Class2 之间创建一个Interface。这样你以后就可以转换成一个或另一个。
  • 你从哪里得到异常?什么是完整的例外?您的代码不包含任何继承或 class1=class2 分配?
  • 什么是“blabla”,还有什么是configModel?还不足以看到这里发生了什么。
  • 你可以看看继承映射:weblogs.asp.net/manavi/…
  • @Nekeniehl 嗨!感谢您的回复!我已经创建了接口: public interface IConfig { string Prop1 { get;放; } 字符串 Prop2 { 获取;放; } 字符串 Prop3 { 获取;放; }},并且两个类都继承了这个接口,但是我还是不能写 config = class1_instance;出现错误“无法将类型 Class2 隐式转换为 Class1”。还是我应该做点别的?

标签: c# asp.net entity-framework linq


【解决方案1】:

不要从实体类继承 DTO 类,因为 EF6 会将 DTO 视为参与某些受支持的数据库继承策略的实体。

改为使用由DbEntityEntryCurrentValues 属性返回的DbPropertyValuesSetValues(object)

通过从给定对象中读取值来设置此字典的值。给定的对象可以是任何类型。将读取对象上名称与字典中的属性名称匹配且可以读取的任何属性。其他属性将被忽略。例如,这允许从简单的数据传输对象 (DTO) 复制属性。

例如

Class1 source = ...;
var target = data.Class2.FirstOrDefault(...);
if (target != null)
{
    data.Entry(target).CurrentValues.SetValues(source);
    data.SaveChanges();
}

注意,目标实体已经被db上下文附加(跟踪)了,所以不需要将入口状态设置为Modified

【讨论】:

  • 我试过这个: using (var data = new Contex()) { var config = data.Class2.FirstOrDefault(c => c.OperatorName.Contains(blabla)); if (config != null) { data.Entry(config).CurrentValues.SetValues(configModel); } data.SaveChanges(); } ,但得到“属性 'Prop1' 是对象的关键信息的一部分,不能修改。”例外。
  • PK 值必须相同才能正常工作。所以Class1s 不应该有Prop1 属性,或者它的值必须匹配目标实体的Prop1。没有 EF“开箱即用”的解决方案。如果您无法满足这些要求,那么,请使用您原来手动设置属性的方法。
  • 另一种方法是在调用SetValues之前添加source.Prop1 = target.Prop1;。这样,您将仅手动设置 1 个属性,而所有其他属性将自动设置。
【解决方案2】:

现在 Class1 和 Class2 具有相同的属性。这是有意的,还是偶然的,可能是 Class2 的未来版本具有 Class1 中没有的属性?

在实体框架中,DbSet<...> 代表数据库的表。 DbSet 中的类代表表中的一行。表的列是类的非虚拟属性;虚拟属性表示表之间的关系(一对多,多对多,...)

Class2 代表一个数据库表。如果 Class1 应该等于 Class2,那么 Class1 的原因是什么。如果这种相等性只是现在,并且在未来的版本中它们可能会有所不同,则您必须一个接一个地复制属性。

void UpdateValue(string someString, Class1 value)
{
    using (var dbContext = new DbContext())
    {
        Class2 fetchedData = dbContext.Class2.Where(...).FirstOrDefault();
        if (fetchedData != null)
        {   // data exists. Update the properties
            fetchedData.Prop1 = value.Prop1,
            fetchedData.Prop2 = value.Prop2,
            fetchedData.Prop3 = value.Prop3,

            // future version of Class2 may have properties that are not updated
            // no need to set state to modified. Entity Framework will detect the changes
            dbContext.SaveChanges();
        }
    }
}

如果您绝对确定每个Class2 都是Class1 的特殊类型,那么现在和将来,您可以考虑从 Class1 派生 Class2:

class Class2 : Class1
{
    public int Id {get; set;}
    ... // properties that are in Class2, but not in Class1
}

这意味着 Class1 的每个非虚拟属性都由具有 Classes2 的表中的一列表示。

推导对你没有帮助,即使你推导,你也必须一一复制属性。如果您必须多次执行此操作,请考虑创建一个为您复制正确值的函数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-12-07
    • 2013-02-12
    • 1970-01-01
    • 2011-05-15
    • 2014-05-30
    • 2022-01-26
    • 2014-10-15
    相关资源
    最近更新 更多