如何避免Linq-to-Sql查询中的循环

本文关键字:循环 查询 何避免 Linq-to-Sql | 更新日期: 2023-09-27 18:26:10

当前我使用[JsonIgnore]属性来避免模型中的循环引用。但我认为,它将应用于所有实现中。

现在我正在使用这样的

我有两种型号:

public class Project
{
    public int ProjectID { get; set; }
    [Required]
    public string ProjectName { get; set; }
    public DateTime? EstimatedEndDate { get; set; }
    public DateTime? ProjectStartDate { get; set; }
    public string OrderNumber { get; set; }
    public string ProjectDescription { get; set; }
    public virtual List<ServiceObject> ServiceObjects { get; set; }
    [Required]
    public string RegardingUser { get; set; }
    public string CreatedBy { get; set; }
    public DateTime CreatedDate { get; set; }
    public string ModifiedBy { get; set; }
    public DateTime ModifiedDate { get; set; }
    public bool IsProjectDeleted { get; set; }
    [NotMapped]
    public int? ServiceObjectTemplateID { get; set; }
}

另一种型号是-

public class ServiceObject
{
    public int ServiceObjectID { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string RegardingUser { get; set; }
    public int? ParentServiceObjectID { get; set; }
    [ForeignKey("ParentServiceObjectID")]
    public virtual List<ServiceObject> ServiceObjects { get; set; }
    public int ProjectID { get; set; }
    [JsonIgnore]
    public virtual Project Project { get; set; }<--------------------------- 
    public virtual List<ServicePicture> ServicePictures { get; set; }
    public bool IsServiceObjectDeleted { get; set; }
    public string CreatedBy { get; set; }
    public DateTime CreatedDate { get; set; }
    public string ModifiedBy { get; set; }
    public DateTime ModifiedDate { get; set; }
}

但我想将JsonIgnore属性或Linq中的任何其他属性应用于Sql查询本身。我的意思是我想动态地应用它。

查询类似于-

var projectList = (from pro in context.Projects.All
                   select pro).ToList();

我想有条件地申请。

提前谢谢。

如何避免Linq-to-Sql查询中的循环

XY问题。您的实际问题是"我想在运行时控制哪些属性被序列化"。

控制序列化哪些属性的最常见方法是使用JsonIgnoreAttribute。然而,正如其他人所评论的那样,属性是在编译时应用的,通常不能在运行时修改(您也不希望这样做,因为它实际上是在运行时更改的全局设置,您会遇到大量的线程问题)。

相反,答案是使用IContractResolver接口来更改序列化行为。

public class OmitPropertyContractResolver
       : IContractResolver
{
    private readonly string[] _ignoredProperties;
    private readonly IContractResolver _resolver;
    public OmitPropertyContractResolver(IContractResolver resolver, params string[] ignoredProperties)
    {
        _ignoredProperties = ignoredProperties;
        _resolver = resolver;        
    }
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var properties = _resolver.CreateProperties(type, memberSerialization);
        return properties
               .Where(p => _ignoredProperties.Contains(p.Name) == false)
               .ToList();
    }
}

另一种可能性是在Json.net设置中使用ReferenceLoopHandling设置。

var serializer = new JsonSerializer(
                      new JsonSerializerSetting()
                               { 
                                   ReferenceLoopHandling = ReferenceLoopHandling.Ignore 
                               } );

这将只序列化每个对象一次。

最后一个选项是使用ItemReferenceLoopHandling。这是序列化(和反序列化)具有循环引用的对象图(注意,图而非树)的非标准方式。

这种方法的缺点是它是非标准的,并且可能不适用于客户端。

好的,根据您的输入,正如我所怀疑的,您看到的是一个典型的EF代码优先问题。EF为每个实体生成动态代理,以便跟踪对其的更改。但是,这会使序列化变得困难,因为它们是作为dynamic对象创建的。有几种方法可以避免这种情况:

选项A:仅针对特定查询禁用代理创建:

// disable proxy creation just for the duration of query
context.Configuration.ProxyCreationEnabled = false;
// do the query, serialize, go crazy!
// enable proxy again. or if your context goes out of scope after this call, then you can ignore re-enabling.
context.Configuration.ProxyCreationEnabled = true;

选项B:如果您不打算在稍后修改和更新实体

var entity = context.Where(...);  // or whatever query
// detach entity
context.Entry(entity).State = EntityState.Detached; 

选项C:通过修改类型定义来避免代理:

如果创建密封类型或没有虚拟属性的类型,EF将不会创建代理,因为存在代理没有什么可做的。

public sealed class Project {...}
// or make it non virtual
...
public List<ServiceObject> ServiceObjects { get; set; }
...

注意在这种情况下,您必须手动加载相关对象:

context.Projects.Include(p => p.ServiceObjects).ToList();

选项D:永久禁用代理创建:

// in your initializer or DbContext class' constructor
DbContext.Configuration.ProxyCreationEnabled = false;

注意:如果禁用代理创建,EF将不会自动跟踪更改。您可以使用快照方法手动跟踪它们。

根据您的设计,您可以使用其中一个或多个选项。我通常坚持选择A或B。