Saya menghadapi masalah yang sama dan saya mencoba menggunakan JsonSetting untuk mengabaikan kesalahan referensi-diri itu agak berhasil sampai saya mendapat kelas yang referensi diri sangat dalam dan proses dot-net saya tergantung pada nilai penulisan Json.
Masalahku
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; }
}
Anda dapat melihat masalah di kelas Pengguna yang dirujuk ke CompanyUser kelas yang merupakan referensi-sendiri.
Sekarang, saya memanggil Metode GetAll yang mencakup semua properti relasional.
cs.GetAll("CompanyUsers", "CompanyUsers.User");
Pada tahap ini proses DotNetCore saya tergantung pada Executing JsonResult, menulis nilai ... dan tidak pernah datang. Di Startup.cs saya, saya sudah mengatur JsonOption. Untuk beberapa alasan EFCore termasuk properti bersarang yang saya tidak minta Ef berikan.
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
perilaku yang diharapkan seharusnya seperti ini
Hei EfCore dapatkah Anda memasukkan data "CompanyUsers" juga di kelas Perusahaan saya sehingga saya dapat dengan mudah mengakses data.
kemudian
Hei EfCore dapatkah Anda juga memasukkan data "CompanyUsers.User" juga sehingga saya dapat dengan mudah mengakses data seperti Perusahaan ini
. CompanyUsers.First (). User.DisplayName
pada tahap ini saya seharusnya hanya mendapatkan "Perusahaan.Perusahaan Pengguna.Pertama ini (). User.DisplayName" dan seharusnya tidak memberi saya Company.Perusahaan Pengguna.Pertama (). Pengguna.Perusahaan Pengguna yang menyebabkan masalah referensi diri; Secara teknis seharusnya tidak memberi saya User.CompanyUsers sebagai CompanyUsers adalah properti navigasi. Tapi, EfCore menjadi sangat bersemangat dan memberi saya User.CompanyUsers .
Jadi, saya memutuskan untuk menulis metode ekstensi agar properti dikecualikan dari objek (sebenarnya tidak termasuk itu hanya menyetel properti ke nol). Tidak hanya itu juga akan bekerja pada properti array. di bawah ini adalah kode saya juga akan mengekspor paket nuget untuk pengguna lain (tidak yakin apakah ini bahkan membantu seseorang). Alasannya sederhana karena saya terlalu malas untuk menulis. Pilih (n => baru {n.p1, n.p2}); Saya hanya tidak ingin menulis pernyataan pilih untuk mengecualikan hanya 1 properti!
Ini bukan kode terbaik (saya akan memperbarui pada tahap tertentu) karena saya telah menulis dengan tergesa-gesa dan meskipun ini dapat membantu seseorang yang ingin mengecualikan (set null) di objek dengan array juga.
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();
}
}
kelas ekstensi di atas akan memberi Anda kemampuan untuk menyetel properti menjadi nol untuk menghindari array genap referensi diri.
Pembuat Ekspresi
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);
}
}
Penggunaan:
Kelas Model
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; }
}
Data Dummy
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" } } },
}
}
};
Kasus:
Kasus 1: Kecualikan hanya properti tanpa array apa pun
p.Exclude(n => n.AddressDetail.CountryDetail.CountryName);
Kasus 2: Kecualikan properti dengan 1 larik
p1.Exclude(n => n.AddressDetail.CountryDetail2.Explode().CountryName);
Kasus 3: Kecualikan properti dengan 2 array bersarang
p1.Exclude(n => n.AddressDetail.CountryDetail2.Explode().CountryDetail.Explode().Name);
Kasus 4: EF GetAll Query With Include
var query = cs.GetAll("CompanyUsers", "CompanyUsers.User").ToArray();
query.Exclude(n => n.Explode().CompanyUsers.Explode().User.CompanyUsers);
return query;
Anda mengetahui bahwa metode Explode () juga merupakan metode ekstensi hanya untuk pembuat ekspresi kami untuk mendapatkan properti dari properti array. Setiap kali ada properti array gunakan .Explode (). YourPropertyToExclude atau .Explode (). Property1.MyArrayProperty.Explode (). MyStupidProperty . Kode di atas membantu saya menghindari referensi-diri sedalam yang saya inginkan. Sekarang saya dapat menggunakan GetAll dan mengecualikan properti yang tidak saya inginkan!
Terima kasih telah membaca posting besar ini!