मैं उसी समस्या का सामना कर रहा था और मैंने JsonSetting का उपयोग करके स्वयं-संदर्भित त्रुटि को अनदेखा करने का प्रयास किया, जब तक कि मुझे एक वर्ग नहीं मिला, जो कि आत्म-संदर्भित बहुत गहराई से है और मेरी डॉट-नेट प्रक्रिया Json लेखन मूल्य पर लटकी हुई है।
मेरी समस्या
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; }
}
आप उपयोगकर्ता वर्ग में कंपनीयूज़र के संदर्भ में समस्या देख सकते हैं जो वर्ग जो एक आत्म-संदर्भित है।
अब, मैं GetAll मेथड को कॉल कर रहा हूं जिसमें सभी रिलेशनल गुण शामिल हैं।
cs.GetAll("CompanyUsers", "CompanyUsers.User");
इस स्तर पर मेरी DotNetCore प्रक्रिया निष्पादन JsonResult पर लटकी रहती है, मूल्य लिखती है ... और कभी नहीं आती है। मेरे Start.up में, मैंने पहले ही JsonOption सेट कर दिया है। किसी कारण से EFCore नेस्टेड प्रॉपर्टी में शामिल है, जो मैं Ef को देने के लिए नहीं कह रहा हूं।
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
अपेक्षित व्यवहार यह होना चाहिए
हे EfCore आप कृपया "CompanyUsers" डेटा के साथ-साथ मेरी कंपनी वर्ग में शामिल कर सकते हैं ताकि मैं आसानी से डेटा तक पहुँच पाऊँ।
फिर
हे EfCore आप "CompanyUsers.User" डेटा को भी शामिल कर सकते हैं ताकि मैं इस Company.CompanyUsers.First () जैसे डेटा तक आसानी से पहुँच
सकूँ। User.DisplayName
इस स्तर पर मुझे केवल यह "Company.CompanyUgers.First ()। User.DisplayName" प्राप्त करना चाहिए और इसे मुझे Company.CompanyUsers.First () नहीं देना चाहिए । उपयोगकर्ता.कंपनीउपयोगकर्ता जो स्व-संदर्भ समस्या का कारण बनते हैं ; तकनीकी तौर पर यह मुझे User.CompanyUsers नहीं देना चाहिए क्योंकि CompanyUser एक नेविगेशनल प्रॉपर्टी है। लेकिन, EfCore बहुत उत्साहित है और मुझे User.CompanyUsers दे रहा है ।
इसलिए, मैंने संपत्ति से बाहर निकालने के लिए एक विस्तार विधि लिखने का फैसला किया (यह वास्तव में इसे छोड़कर नहीं है कि यह संपत्ति को शून्य करने के लिए सेट कर रहा है)। इतना ही नहीं यह एरे प्रॉपर्टीज पर भी काम करेगा। नीचे वह कोड है जो मैं अन्य उपयोगकर्ताओं के लिए नगेट पैकेज निर्यात करने के लिए भी जा रहा हूं (यह सुनिश्चित नहीं है कि यह किसी की मदद भी करता है)। कारण सरल है क्योंकि मैं लिखने में बहुत आलसी हूं । चयन (n => नया {n.p1, n.p2}); मैं सिर्फ 1 संपत्ति को बाहर करने के लिए चयन कथन लिखना नहीं चाहता!
यह सबसे अच्छा कोड नहीं है (मैं कुछ चरण में अपडेट करूंगा) जैसा कि मैंने जल्दी में लिखा है और हालांकि यह किसी ऐसे व्यक्ति की मदद कर सकता है जो ऑब्जेक्ट में सरणियों के साथ भी सेट करना चाहता है।
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();
}
}
उपरोक्त विस्तार वर्ग आपको स्वयं-संदर्भित लूप को भी सरणियों से बचने के लिए संपत्ति को शून्य करने की क्षमता प्रदान करेगा।
अभिव्यक्ति बिल्डर
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 ()। MyStupidProetty का उपयोग करें । उपरोक्त कोड सेल्फ-रेफ़रेंसिंग से बचने में मेरी मदद करता है ताकि मैं जितना चाहता हूं उतना गहरा हो। अब मैं GetAll का उपयोग कर सकता हूं और उस संपत्ति को बाहर कर सकता हूं जो मैं नहीं चाहता;
इस बड़ी पोस्ट को पढ़ने के लिए धन्यवाद!