Ich hatte das gleiche Problem und habe versucht, mit JsonSetting den selbstreferenzierenden Fehler zu ignorieren, bis ich eine Klasse bekam, die sehr tief auf sich selbst verweist und mein Punktnetzprozess vom Json-Schreibwert abhängt.
Mein Problem
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; }
}
Sie können das Problem in der Benutzerklasse sehen, die auf CompanyUser verweist Klasse , die sich selbst referenziert.
Jetzt rufe ich die GetAll-Methode auf, die alle relationalen Eigenschaften enthält.
cs.GetAll("CompanyUsers", "CompanyUsers.User");
In dieser Phase hängt mein DotNetCore-Prozess an der Ausführung von JsonResult, dem Schreiben von Werten ... und kommt nie. In meiner Startup.cs habe ich bereits die JsonOption festgelegt. Aus irgendeinem Grund enthält EFCore verschachtelte Eigenschaften, die ich Ef nicht geben soll.
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
erwartetes Verhalten sollte dies sein
Hey EfCore, können Sie bitte auch "CompanyUsers" -Daten in meine Company-Klasse aufnehmen, damit ich leicht auf die Daten zugreifen kann?
dann
Hey EfCore, können Sie bitte auch die "CompanyUsers.User" -Daten angeben, damit ich einfach auf die Daten wie diese
Company.CompanyUsers.First () zugreifen kann. User.DisplayName
Zu diesem Zeitpunkt sollte ich nur dieses "Company.CompanyUsers.First (). User.DisplayName" erhalten und es sollte mir nicht Company.CompanyUsers.First (). User.CompanyUsers geben, das das Problem der Selbstreferenzierung verursacht. Technisch sollte es mir nicht User.CompanyUsers geben, da CompanyUsers eine Navigationseigenschaft ist. Aber EfCore wird sehr aufgeregt und gibt mir User.CompanyUsers .
Daher habe ich beschlossen, eine Erweiterungsmethode für die Eigenschaft zu schreiben, die vom Objekt ausgeschlossen werden soll (sie schließt eigentlich nicht aus, sondern setzt die Eigenschaft nur auf null). Nicht nur, dass es auch mit Array-Eigenschaften funktioniert. Unten ist der Code, den ich auch für andere Benutzer exportieren werde (nicht sicher, ob dies überhaupt jemandem hilft). Der Grund ist einfach, weil ich zu faul bin, um zu schreiben. Wählen Sie (n => new {n.p1, n.p2});Ich möchte einfach keine select-Anweisung schreiben, um nur 1 Eigenschaft auszuschließen!
Dies ist nicht der beste Code (ich werde ihn irgendwann aktualisieren), da ich es eilig habe und obwohl dies jemandem helfen könnte, der das Objekt auch mit Arrays ausschließen (null setzen) möchte.
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();
}
}
Mit der obigen Erweiterungsklasse können Sie die Eigenschaft auf null setzen, um zu vermeiden, dass die selbstreferenzierende Schleife auch Arrays enthält.
Ausdrucksgenerator
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);
}
}
Verwendung:
Modellklassen
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; }
}
Dummy-Daten
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" } } },
}
}
};
Fälle:
Fall 1: Schließen Sie nur Eigenschaften ohne Array aus
p.Exclude(n => n.AddressDetail.CountryDetail.CountryName);
Fall 2: Eigenschaft mit 1 Array ausschließen
p1.Exclude(n => n.AddressDetail.CountryDetail2.Explode().CountryName);
Fall 3: Eigenschaft mit 2 verschachtelten Arrays ausschließen
p1.Exclude(n => n.AddressDetail.CountryDetail2.Explode().CountryDetail.Explode().Name);
Fall 4: EF GetAll-Abfrage mit Includes
var query = cs.GetAll("CompanyUsers", "CompanyUsers.User").ToArray();
query.Exclude(n => n.Explode().CompanyUsers.Explode().User.CompanyUsers);
return query;
Sie haben festgestellt, dass die Explode () -Methode auch eine Erweiterungsmethode ist, nur damit unser Ausdrucksgenerator die Eigenschaft von der Array-Eigenschaft abruft. Wenn eine Array-Eigenschaft vorhanden ist, verwenden Sie .Explode (). YourPropertyToExclude oder .Explode (). Property1.MyArrayProperty.Explode (). MyStupidProperty . Der obige Code hilft mir, die Selbstreferenzierung so tief wie möglich zu vermeiden. Jetzt kann ich GetAll verwenden und die Eigenschaft ausschließen, die ich nicht will!
Vielen Dank für das Lesen dieses großen Beitrags!