나는 같은 문제에 직면 해 있었고 자체 참조 오류를 무시하기 위해 JsonSetting을 사용하려고 시도했습니다.
내 문제
public partial class Company : BaseModel
{
public Company()
{
CompanyUsers = new HashSet<CompanyUser>();
}
public string Name { get; set; }
public virtual ICollection<CompanyUser> CompanyUsers { get; set; }
}
public partial class CompanyUser
{
public int Id { get; set; }
public int CompanyId { get; set; }
public int UserId { get; set; }
public virtual Company Company { get; set; }
public virtual User User { get; set; }
}
public partial class User : BaseModel
{
public User()
{
CompanyUsers = new HashSet<CompanyUser>();
}
public string DisplayName { get; set; }
public virtual ICollection<CompanyUser> CompanyUsers { get; set; }
}
CompanyUser를 참조하는 User 클래스에서 문제를 볼 수 있습니다.자체 참조 인 클래스를 .
이제 모든 관계형 속성을 포함하는 GetAll 메서드를 호출합니다.
cs.GetAll("CompanyUsers", "CompanyUsers.User");
이 단계에서 내 DotNetCore 프로세스는 JsonResult 실행, 값 쓰기 ... 에 멈추고 결코 오지 않습니다. 내 Startup.cs에서 이미 JsonOption을 설정했습니다. 어떤 이유로 EFCore는 Ef에게 묻지 않는 중첩 된 속성을 포함합니다.
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
예상되는 동작은 다음과 같습니다
EfCore, 회사 클래스에 "CompanyUsers"데이터를 포함 시켜서 데이터에 쉽게 액세스 할 수 있도록하십시오.
그때
EfCore 도이 Company.CompanyUsers.First (). User.DisplayName 과 같은 데이터에 쉽게 액세스 할 수 있도록 "CompanyUsers.User" 데이터
도 포함 시키십시오 .
이 단계에서는이 "Company.CompanyUsers.First (). User.DisplayName"만 가져와야 하며 자체 참조 문제를 일으키는 Company.CompanyUsers.First (). User.CompanyUsers 를 제공해서는 안됩니다 . 기술적으로 그것은 UserUser 를 제공해서는 안됩니다. CompanyUsers 는 탐색 속성입니다. 그러나 EfCore는 매우 흥분되어 User.CompanyUsers를 제공합니다 .
그래서 객체에서 속성이 제외되도록 확장 메소드를 작성하기로 결정했습니다 (실제로는 속성을 null로 설정하는 것을 제외하지는 않습니다). 뿐만 아니라 배열 속성에서도 작동합니다. 아래는 다른 사용자를 위해 너겟 패키지를 내보내는 코드입니다 (이것이 누군가에게 도움이되는지 확실하지 않습니다). 쓰기가 너무 게으 르기 때문에 이유는 간단합니다. .Select (n => new {n.p1, n.p2}); 단지 1 개의 속성 만 제외하도록 select 문을 작성하고 싶지 않습니다!
이것은 서둘러 작성 한 최고의 코드 (일부 단계에서 업데이트 할 것)는 아니지만 배열을 사용하여 객체에서 제외 (널 설정)하려는 사람에게 도움이 될 수 있습니다.
public static class PropertyExtensions
{
public static void Exclude<T>(this T obj, Expression<Func<T, object>> expression)
{
var visitor = new PropertyVisitor<T>();
visitor.Visit(expression.Body);
visitor.Path.Reverse();
List<MemberInfo> paths = visitor.Path;
Action<List<MemberInfo>, object> act = null;
int recursiveLevel = 0;
act = (List<MemberInfo> vPath, object vObj) =>
{
// set last propert to null thats what we want to avoid the self-referencing error.
if (recursiveLevel == vPath.Count - 1)
{
if (vObj == null) throw new ArgumentNullException("Object cannot be null");
vObj.GetType().GetMethod($"set_{vPath.ElementAt(recursiveLevel).Name}").Invoke(vObj, new object[] { null });
return;
}
var pi = vObj.GetType().GetProperty(vPath.ElementAt(recursiveLevel).Name);
if (pi == null) return;
var pv = pi.GetValue(vObj, null);
if (pi.PropertyType.IsArray || pi.PropertyType.Name.Contains("HashSet`1") || pi.PropertyType.Name.Contains("ICollection`1"))
{
var ele = (IEnumerator)pv.GetType().GetMethod("GetEnumerator").Invoke(pv, null);
while (ele.MoveNext())
{
recursiveLevel++;
var arrItem = ele.Current;
act(vPath, arrItem);
recursiveLevel--;
}
if (recursiveLevel != 0) recursiveLevel--;
return;
}
else
{
recursiveLevel++;
act(vPath, pv);
}
if (recursiveLevel != 0) recursiveLevel--;
};
// check if the root level propert is array
if (obj.GetType().IsArray)
{
var ele = (IEnumerator)obj.GetType().GetMethod("GetEnumerator").Invoke(obj, null);
while (ele.MoveNext())
{
recursiveLevel = 0;
var arrItem = ele.Current;
act(paths, arrItem);
}
}
else
{
recursiveLevel = 0;
act(paths, obj);
}
}
public static T Explode<T>(this T[] obj)
{
return obj.FirstOrDefault();
}
public static T Explode<T>(this ICollection<T> obj)
{
return obj.FirstOrDefault();
}
}
위의 확장 클래스를 사용하면 자체 참조 루프 짝수 배열을 피하기 위해 속성을 null로 설정할 수 있습니다.
식 작성기
internal class PropertyVisitor<T> : ExpressionVisitor
{
public readonly List<MemberInfo> Path = new List<MemberInfo>();
public Expression Modify(Expression expression)
{
return Visit(expression);
}
protected override Expression VisitMember(MemberExpression node)
{
if (!(node.Member is PropertyInfo))
{
throw new ArgumentException("The path can only contain properties", nameof(node));
}
Path.Add(node.Member);
return base.VisitMember(node);
}
}
사용법 :
모델 클래스
public class Person
{
public string Name { get; set; }
public Address AddressDetail { get; set; }
}
public class Address
{
public string Street { get; set; }
public Country CountryDetail { get; set; }
public Country[] CountryDetail2 { get; set; }
}
public class Country
{
public string CountryName { get; set; }
public Person[] CountryDetail { get; set; }
}
더미 데이터
var p = new Person
{
Name = "Adeel Rizvi",
AddressDetail = new Address
{
Street = "Sydney",
CountryDetail = new Country
{
CountryName = "AU"
}
}
};
var p1 = new Person
{
Name = "Adeel Rizvi",
AddressDetail = new Address
{
Street = "Sydney",
CountryDetail2 = new Country[]
{
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A1" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A2" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A3" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A4" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A5" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A6" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A7" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A8" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A9" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A1" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A2" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A3" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A4" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A5" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A6" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A7" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A8" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
new Country{ CountryName = "AU", CountryDetail = new Person[]{ new Person { Name = "A9" }, new Person { Name = "A1" }, new Person { Name = "A1" } } },
}
}
};
사례 :
사례 1 : 배열이없는 속성 만 제외
p.Exclude(n => n.AddressDetail.CountryDetail.CountryName);
사례 2 : 1 배열의 속성 제외
p1.Exclude(n => n.AddressDetail.CountryDetail2.Explode().CountryName);
사례 3 : 중첩 배열이 2 개인 속성 제외
p1.Exclude(n => n.AddressDetail.CountryDetail2.Explode().CountryDetail.Explode().Name);
사례 4 : 포함 된 EF GetAll 쿼리
var query = cs.GetAll("CompanyUsers", "CompanyUsers.User").ToArray();
query.Exclude(n => n.Explode().CompanyUsers.Explode().User.CompanyUsers);
return query;
해당 통지가 () 분해 방법을 우리 식 작성기는 배열 속성의 속성을 가져 단지 자사도 확장 방법을. 배열 속성이있을 때마다 .Explode (). YourPropertyToExclude 또는 .Explode (). Property1.MyArrayProperty.Explode (). MyStupidProperty를 사용하십시오 . 위의 코드는 내가 원하는 깊이의 자체 참조를 피하는 데 도움이됩니다. 이제 GetAll을 사용하고 내가 원하지 않는 속성을 제외시킬 수 있습니다!
이 큰 글을 읽어 주셔서 감사합니다!