Aynı sorunla karşı karşıyaydım ve JsonSetting kullanarak kendi kendini referanslama hatasını göz ardı etmek için çalıştım, çok derinlemesine bir referans alana kadar ve nokta net sürecim Json yazma değerine dayanıyor.
Benim sorunum
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; }
}
Sorunu CompanyUser'a başvurduğu Kullanıcı sınıfında görebilirsiniz. kendi kendine sınıfına .
Şimdi, tüm ilişkisel özellikleri içeren GetAll yöntemini çağırıyorum.
cs.GetAll("CompanyUsers", "CompanyUsers.User");
Bu aşamada DotNetCore işlemim JsonResult'u yürütmek , değer yazmak ... ve asla gelmek istemiyor . Startup.cs dosyalarında JsonOption'u zaten ayarladım. Nedense EFCore Ef vermek istemiyorum iç içe özellik dahil.
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
beklenen davranış bu olmalı
Hey EfCore, lütfen "CompanyUsers" verilerini Şirket sınıfıma da dahil edebilir, böylece verilere kolayca erişebilirim.
sonra
Hey EfCore ayrıca "CompanyUsers.User" verilerini de dahil edebilir, böylece böyle verilere kolayca erişebilirim
Company.CompanyUsers.First () .
bu aşamada ben sadece bu "Company.CompanyUsers.First (). User.DisplayName" almalı ve bana Company.CompanyUsers.First (). User.CompanyUsers kendi kendine başvuru sorunu neden olmamalıdır ; CompanyUsers bir navigasyon özelliği olduğu için teknik olarak bana User.CompanyUsers vermemelidir. Ancak, EfCore çok heyecanlanıyor ve bana User.CompanyUsers veriyor .
Bu nedenle, nesnenin dışında bırakılacak özellik için bir uzantı yöntemi yazmaya karar verdim (aslında sadece özelliği null olarak ayarlaması hariç değil). Sadece dizi özellikleri üzerinde de çalışacak. aşağıda da diğer kullanıcılar için nuget paketini ihraç edeceğim kod (bu bile birine yardımcı olup olmadığından emin değilim). Nedeni basit çünkü yazamayacak kadar tembelim .Select (n => new {n.p1, n.p2});Sadece 1 özelliği hariç tutmak için select deyimi yazmak istemiyorum!
Acele yazdım ve bu da dizilerle nesnede dışlamak (null) ayarlamak isteyen birine yardımcı olabilir, ancak bu en iyi kod (bazı aşamada güncelleyeceğim) değil.
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();
}
}
Yukarıdaki eklenti sınıfı, kendiliğinden başvuru yapan döngü dizilerinden bile kaçınmak için özelliği null değerine ayarlayabilmenizi sağlar.
İfade Oluşturucu
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);
}
}
Usages:
Model Sınıfları
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; }
}
Kukla Veriler
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" } } },
}
}
};
Durumlar:
Durum 1: Yalnızca dizisi olmayan mülkü hariç tutma
p.Exclude(n => n.AddressDetail.CountryDetail.CountryName);
Durum 2: 1 dizili özelliği hariç tutma
p1.Exclude(n => n.AddressDetail.CountryDetail2.Explode().CountryName);
Durum 3: İç içe 2 dizili özelliği hariç tutma
p1.Exclude(n => n.AddressDetail.CountryDetail2.Explode().CountryDetail.Explode().Name);
Durum 4: İçerdiği EF GetAll Sorgusu
var query = cs.GetAll("CompanyUsers", "CompanyUsers.User").ToArray();
query.Exclude(n => n.Explode().CompanyUsers.Explode().User.CompanyUsers);
return query;
Explode () yönteminin, yalnızca ifade oluşturucumuzun özelliği dizi özelliğinden alması için bir uzantı yöntemi olduğunu fark ettiniz . Bir dizi özelliği olduğunda .Explode () .ProProtytyToExclude veya .Explode (). Property1.MyArrayProperty.Explode () kullanın . Yukarıdaki kod ben kadar derin istediği kadar kendini referans önlemek için bana yardımcı olur. Şimdi GetAll kullanabilirim ve istemediğim özelliği hariç tutabilirim!
Bu büyük gönderiyi okuduğunuz için teşekkürler!