Reflection To Update Source Entity with Children To Target With Children - c#

For all you Reflection experts I need some help. I am working on a gneric method to be able to use hierarchical data assignments in an MVC application using CodeFirst and EF5. A user would apply changes to the parent object (source) and this method would populate the child object(s) (target). It's not working as I would expect. Any pointers would be greatly appreciated.
The below method "CopyEntity" is intended to take a source entity that has child entities and update a target entity of the same type and structure. We are only looking to update those entities that inherit a base type of 'Association' and also do not have a 'NoInherit' attribute on the members. Not every property would be updated from the 'Association'
public class ClassCode : Inheritable<ClassCode>
{
// Base Properties ----------------------------------
public int LobId { get; set; }
public virtual LOB LOB { get; set; }
public bool IsVirtual { get; set; }
//public string Name { get; set; }
public string Description { get; set; }
[Required(ErrorMessage = "Select Code")]
public string Code { get; set; }
//enable to force recreation of the database
public string IID { get; set; }
public virtual ICollection<ClassSubjectivityAssoc> Subjectivities{ get; set; }
}
public class ClassSubjectivityAssoc : Association
{
[Column("SubjectivityId")]
public override int AssociationId { get; set; }
[ForeignKey("AssociationId")]
public virtual Subjectivity Subjectivity { get; set; }
public int ClassCodeId { get; set; }
[ForeignKey("ClassCodeId")]
public virtual ClassCode ClassCode { get; set; }
}
public abstract class Association : BaseEntity
{
public DateTime Start { get; set; }
public DateTime? End { get; set; }
public bool IsCurrent { get; set; }
public int Order { get; set; }
public bool BreakInheritance { get; set; }
public int? InhId { get; set; }
public virtual ClassCode InhClass { get; set; }
public int CodeId { get; set; }
[ForeignKey("ClassCodeId")]
public virtual Code ClassCode { get; set; }
public abstract int AssociationId {get; set;}
}
public class BaseEntity
{
private static DateTime CREATE_DATE
{
get { return DateTime.Now; }
}
[NoInherit]
public int Id { get; set; }
[NoInherit]
public string CreatedBy { get; set; }
[NoInherit]
public DateTime Created { get; set; }
[NoInherit]
public string LastModifiedBy { get; set; }
[NoInherit]
public DateTime LastModified { get; set; }
public bool IsActive { get; set; }
}
protected void CopyEntity(Inheritable<T> source, Inheritable<T> target)
{
Type sourceType = source.GetType();
Type targetType = target.GetType();
// ensure that the types can be assigned to one another
//if (!targetType.IsAssignableFrom(sourceType)) return;
// loop through the properties of the source system
foreach (PropertyInfo sourceMember in sourceType.GetProperties().Where(sourceMember => sourceMember.GetCustomAttribute(typeof(NoInheritAttribute)) == null))
{
var targetMember = targetType.GetProperty(sourceMember.Name);
// if the property doesn't exist in the target, let's get out
if (targetMember == null) continue;
// check if this property is a Collection
bool isCollection = (sourceMember.PropertyType.IsGenericType && sourceMember.PropertyType.GetInterfaces().Any(x =>
x.IsGenericType &&
x.GetGenericTypeDefinition() == typeof(IEnumerable<>)));
// if we are not dealing with associations, let's get outta herre
if (!(isCollection
? sourceMember.PropertyType.GetGenericArguments().Single()
: sourceMember.PropertyType)
.IsSubclassOf(typeof(Association))) continue;
if (isCollection)
{
IEnumerable<Association> targetCollection = (IEnumerable<Association>) targetMember.GetValue(target);
// Loop through the collection and evaluate
foreach (Association sourceAssociation in (IEnumerable) sourceMember.GetValue(source))
{
Association targetAssociation =
targetCollection.SingleOrDefault(t => t.AssociationId == sourceAssociation.AssociationId);
CopyAssociation(sourceAssociation, targetAssociation);
}
}
else
{
Association sourceAssociation = (Association) sourceMember.GetValue(source);
Association targetAssociation = (Association) targetMember.GetValue(target);
CopyAssociation(sourceAssociation, targetAssociation);
}
}
}
The problem is: It does not seem to be traversing the child objects properly and does not update the target as I would have expected. Looking for input and suggections as I am not as proficient in Reflection as I would like.
UPDATE: 12/06/2012
Ok, So I believe I've found the problem but still looking for a solution. This problem came to me from another dev on my team with the assumption that the object model was sound. However, it seems that the entity that we are currently testing does not retrieve the seeded values from the db.
There is nothing defined in the OnModelCreating() method defining this association although the tables are being properly created from the entity model and the data is being loaded properly from the dbSeed() method. I can run a select statement from the db and get the proper data. It seems that the entity model foreign key associations might be invalid and thus preventing the correct data retrieval. Any help would be greatly appreciated.
UPDATE: Ok, finally retrieving the correct data. This finally did it.
modelBuilder.Entity<ClassSubjectivityAssoc>()
.HasRequired(c => c.ClassCode)
.WithMany(u => u.Subjectivities)
.HasForeignKey(f => f.ClassCodeId);

I'm not really sure if I understand the question, but it seems that you just need to make sure that sourceMember is either an Association (or descendant) or a collection of Association.
You could do that in a quick and dirty way with casting the resulting value to Association, and checking if the cast succeeded, like this for simple properties:
Association sourceAssociation = sourceMember.GetValue(source) as Association;
if (sourceAssociation != null) // the source is a valid Association
{
Association targetAssociation = targetMember.GetValue(target) as Association;
if (targetAssociation != null) // the destionation too
{
CopyAssociation(sourceAssociation, targetAssociation);
}
}
and with similar code for the collections as well
The method you have shown will not descend a object tree, so it will only work on Association objects that are directly attached to the Entity.

See original post "UPDATES" for solution.

Related

1:1 self relation in EF

I have a object of type ScheduleItem
public class ScheduleItem : EntityBase
{
[MaxLength(500)]
[Required]
public string TitleAr { get; set; }
[MaxLength(300)]
[Required]
public string TitleEn { get; set; }
public int? ParentScheduleItemId { get; set; }
public int? PreviousDestinationId { get; set; }
public int? NextDestinationId { get; set; }
public ScheduleItem ParentScheduleItem { get; set; }
[InverseProperty("ParentScheduleItem")]
public ICollection<ScheduleItem> ChildrenScheduleItems { set; get; }
public ScheduleItem PreviousDestination { get; set; }
public ScheduleItem NextDestination { get; set; }
}
This object is the parent object, inside it there's a collection of children.
Each child has an optional reference to the next child (the last child will have NextDestinationId = null) and an optional reference to the previous child (PreviousDestinationId should be null for the first child).
Model Builder code:
modelBuilder.Entity<ScheduleItem>().HasOptional(t => t.NextDestination).WithMany().HasForeignKey(t => t.NextDestinationId);
modelBuilder.Entity<ScheduleItem>().HasOptional(t => t.PreviousDestination).WithMany().HasForeignKey(t => t.PreviousDestinationId);
However, when saving I get this error
unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.
What's the solution to this problem? do I have to call SaveChanges() twice?
The solution is to rethink the relationship and structure. I have no comment on the parent/child relationship as you do not explain why a parent of the same type (a recursive relationship) is necessary.
The sibling relationship is not necessary, remove that all together and replace it with a SortOrder column of numeric type. The parent should then return a sorted list/collection based on this type. The schedule items should not have to know about the next/previous schedule items that are adjacent to them, let the parent worry about that.
So replace
public int? PreviousDestinationId { get; set; }
public int? NextDestinationId { get; set; }
public ScheduleItem PreviousDestination { get; set; }
public ScheduleItem NextDestination { get; set; }
with
// sort from low-to-high in the context of a collection
public int SortOrder { get; set; }

EF7 Implement TPH + M2M

Is there a better way to accomplish this end-goal of having easily-queryable (and Include-able) cross-sections of a related many-to-many entity stored in the same table?
I started off without implementing TPH in the join table, but that makes consuming one type or another in queries more involved, afaict.
// table Related: [Id]
public class Related
{
public Guid Id { get; set; }
public List<RelatedOther> RelatedOthers { get; set; } = new List<RelatedOther>();
public List<RelatedOtherOne> RelatedOtherOnes { get; set; } = new List<RelatedOtherOne>();
public List<RelatedOtherTwo> RelatedOtherTwos { get; set; } = new List<RelatedOtherTwo>();
}
// table RelatedOther: [RelatedId, OtherId, Type]
public abstract class RelatedOther
{
public Guid RelatedId { get; set; }
public Guid OtherId { get; set; }
public Related Related { get; set; }
public Other Other { get; set; }
public abstract RelatedOtherType Type { get; }
}
public class RelatedOtherOne : RelatedOther
{
public override RelatedOtherType Type => RelatedOtherType.One;
// should be unnecessary, 'Other' should be correct type
public OtherOne OtherOne { get; set; }
}
public class RelatedOtherTwo : RelatedOther
{
public override RelatedOtherType Type => RelatedOtherType.Two;
// should be unnecessary, 'Other' should be correct type
public OtherTwo OtherTwo { get; set; }
}
public enum RelatedOtherType : int
{
One = 1,
Two = 2
}
// table Other: [Id, OneProp, TwoProp]
public abstract class Other
{
public Guid Id { get; set; }
public List<RelatedOther> RelatedOthers { get; set; } = new List<RelatedOther>();
}
public class OtherOne : Other
{
public string OneProp { get; set; }
}
public class OtherTwo : Other
{
public string TwoProp { get; set; }
}
TPH is mapped like this
M2M is mapped like this + discriminator in HasKey()
This gets even more complicated (if not impossible?) when the 'Related' entity evolves into a TPH strategy like the 'Other'.
I have no easy solution but as I stumbled across the same problem I thought I'll share what I have so far.
I found out that I usually need to load all or many types of the relations to the classes of a TPH structure.
So I use the base many-to-many class to load the related objects. Thus this class cannot be abstract:
public class Event2Location
{
[Required]
public Event Event { get; set; }
public int EventId { get; set; }
[Required]
public Location Location { get; set; }
public int LocationId { get; set; }
public byte EntityType { get; set; }
}
The derived class only adds some properties for easier access:
public class Event2Country : Event2Location
{
[NotMapped]
public Country Country
{
get { return base.Location as Country; }
set { base.Location = value; }
}
[NotMapped]
public int CountryId
{
get { return base.LocationId; }
set { base.LocationId = value; }
}
}
In the Event class I have:
public virtual ICollection<Event2Location> Event2Locations { get; set; }
[NotMapped]
public virtual ICollection<Event2Country> Event2Countries => Event2Locations?.OfType<Event2Country>().ToList();
// I should probably add some caching here if accessed more often
[NotMapped]
public virtual ICollection<Event2City> Event2Cities => Event2Locations?.OfType<Event2City>().ToList();
So when I load the joined tables I can use
.Include(e => e.Event2Locations).ThenInclude(j => j.Location)
And I can access the relations of a specific type as needed with the NotMapped Collections.
I still use the derived Event2... classes to add a new relationship.
As you see I have added a column EntityType to the many-to-many class which I use as TPH discriminator. With this column I can also declare which types of Relations/entities I want to load if I do not want to load all.
modelBuilder.Entity<Event2Location>()
.HasDiscriminator<byte>("EntityType")
.HasValue<Event2Location>(0)
.HasValue<Event2Country>(1)
This is surely far from perfect but I finally gave up on optimizing that. First EFCore has to become more mature. Second I want to see how I actually use these structures.
PS: Actually my Location TPH structure has parent-child-relationships within it. Here I did not create a TPH structure for the relation class (as you said - not possible or at least not reasonable). I added ParentType and ChildType. Thus I can determine which relations I actually want to load. Then I fetch the related Locations of the types I need manually on the client side from the result.

Entity Framework - Custom Field that get data from another Table

i have this scenario:
public class Source
{
public int ID { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public virtual ICollection<InvoiceMembership> InvoiceMemberships { get; set;}
}
public class InvoiceMembership
{
public int ID { get; set; }
[Column(TypeName = "date")]
public DateTime StartDate { get; set; }
[Column(TypeName = "date")]
public DateTime? EndDate { get; set; }
public virtual Source source { get; set; }
public virtual InvoiceTemplate InvoiceTemplate { get; set; }
}
public class InvoiceTemplate
{
public int ID { get; set; }
public String Name { get; set; }
public String Description { get; set; }
public bool Enabled { get; set; }
public int NumberOfPayment { get; set; }
}
How can i have a field named CurrentTemplate with type of InvoiceTemplate in Source Entity That have EndDate=null in related InvoiceMembership row?
EDIT:
i use bellow code, but it's not really true way!
[NotMapped]
public InvoiceTemplate CurrentTemplate { get {
var context=new MedicalContext();
var template = context.InvoiceMemberships.Where(m => m.source == this).Where(m => m.EndDate == null).Select(m => m.InvoiceTemplate);
if (template != null)
return (InvoiceTemplate)template;
else
return null;
} }
Yes you can, but with EF computed properties are such a hassle.
Let's say you have:
public class Source
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
[NotMapped]
public InvoiceTemplate CurrentTemplate
{
get
{
return InvoiceMemberships
.Where(i = i.EndDate == null)
.Select(i => i.InvoiceTemplate)
.FirstOrDefault();
}
}
public virtual ICollection<InvoiceMembership> InvoiceMemberships { get; set;}
}
There just too many conditions (in my opinion) that should be met to make this work:
CurrentTemplate can't be used directly in a LINQ query: EF will complain that the expression can't be translated into SQL.
You always have to Include() InvoiceMemberships.InvoiceTemplate to be able to access CurrentTemplate in memory, after the context is disposed. This will always load all InvoiceMemberships + InvoiceTemplate for each Source object (in one query).
Without using Include() you can only access CurrentTemplate before the context is disposed. This will trigger lazy loading of all InvoiceMemberships + InvoiceTemplate for each Source object in separate queries.
As you see, you'll always load (much) more data than needed to get only one InvoiceMembership per Source.
The most efficient way is to query the required data into a projection, so the predicate EndDate == null can be included in the SQL query.
We'll have to wait for NHibernate-style formula properties.
Expose your Foreign Key value directly in your InvoiceMembership POCO (it must be there in the database anyway if the relation exists), and the entire query will be elegantly converted to SQL directly by L2E:
public class InvoiceMembership
{
public int ID { get; set; }
public int SourceId { get; set; }
[ForeignKey("SourceId")]
public virtual Source Source { get; set; }
public virtual InvoiceTemplate InvoiceTemplate { get; set; }
}
And in Source:
[NotMapped]
public InvoiceTemplate CurrentTemplate
{
get
{
using (var context = new MedicalContext())
return context.InvoiceMemberships
.Where(m => m.SourceId == this.ID)
.Where(m => m.EndDate == null)
.Select(m => m.InvoiceTemplate)
.FirstOrDefault();
}
}
However, this has the drawback that - each time the property is accessed - the database will be queried. It might be best to move this method to the InvoiceMembership class, where you know your InvoiceMembership objects are getting loaded anyway, and make it a method:
public class InvoiceMembership
{
public int ID { get; set; }
public int SourceId { get; set; }
[ForeignKey("SourceId")]
public virtual Source Source { get; set; }
public virtual InvoiceTemplate InvoiceTemplate { get; set; }
static public InvoiceTemplate ReadCurrentTemplate(int sourceId)
{
using (var context = new MedicalContext())
return context.InvoiceMemberships
.Where(m => m.SourceId == sourceId)
.Where(m => m.EndDate == null)
.Select(m => m.InvoiceTemplate)
.FirstOrDefault();
}
}
So, now, you have a method, not a property, to no longer hide the fact that an action is being taken each time you access it ... the name ReadCurrentTemplate tells you that. And why not make it static now? Furthermore, making it static means you no longer have to worry about NotMappedAttribute.
But we'd still like to have access in Source, wouldn't we (especially if dropping the ICollection<InvoiceMembership> navigation property, as I see in your comments you wish to do)? This is now no longer an EF concern at all, but a regular, lazy-loading (if you wish it to be), concern in the Source class:
readonly Lazy<InvoiceTemplate> _currentTemplate;
public Source()
{
_currentTemplate = new Lazy<InvoiceTemplate>(t => t = InvoiceMembership.ReadCurrentTemplate(ID));
}
[NotMapped]
public InvoiceTemplate CurrentTemplate
{
get { return _currentTemplate.Value; }
}
So, doing this, you're still going to take the hit of running the database query to get the value of CurrentTemplate, but only once to evaluate the Lazy private backer; afterward, for the life of this Source object, this property will be the same as the first time it was read. This seemed to fit the model of how you were going to use it, considering it was only ever a read-only property in your examples. And it would only be for the life of the Source object, which should be in a context anyway.
If not, this should be method ReadCurrentTemplate (non-static, no parameter) on Source as well, and simply return InvoiceMembership.ReadCurrentTemplate(ID) directly, to indicate that each time it's called it's reading from the Database.

Entity framework 6 - Some objects of the list returned by context have an empty property

I am retrieving a list of entities like this :
var traductions = from t in context.Traductions
where t.User.ID == user.ID
&& (DateTime.Now < t.EndDate)
select t;
var list = traductions.ToList();
Here is the model :
Here is the first class :
public class Original
{
public int ID { get; set; }
public string Name { get; private set; }
public Traduction Traduction { get; private set; }
}
And here is the second class :
public class Traduction
{
public int ID { get; set; }
public Original Original { get; private set; }
public string Content { get; private set; }
}
I get 4 entities - that's good - however one of the properties of some of my retrieved objects is null - Original. In 3 of the 4 object the Original property is null, but this property is correctly populated in the 4th. I have checked in the Database directly and the Primary Keys / FK are consistent.
What do you think about it? I don't understand because normally if there is a problem this property should be null for all the retrieved objects, but here the property is correctly populated for one of the objects.
Thanks
My first thought is you might have a circular reference. Is it possible to have a Traduction, that contains an Original that contains the first Traduction? If you do that could be what you're seeing. You'll definitely have problems if/when you try to serialize things.
The following won't help a circular reference, but could be why the data is not loading as you'd expect.
You could try using lazy loading. To do that you'll use the 'virtual' keyword in your classes like:
public class Original
{
public int ID { get; set; }
public string Name { get; private set; }
public virtual Traduction Traduction { get; private set; }
}
public class Traduction
{
public int ID { get; set; }
public virtual Original Original { get; private set; }
public string Content { get; private set; }
}
Or you can specifically include the objects in your query:
var traductions = from t in context.Traductions.Include("Original")
where t.User.ID == user.ID
&& (DateTime.Now < t.EndDate)
select t;

Why is my code doing lazy loading even after I turned it off at every possible point?

I would like to get Exams and Test entities that have a UserTest entity with a UserId that is either equal to "0" or to a provided value. I had a number of suggestions but so far none have worked. One suggestion was to start by getting UserTest data and the other solution was to start by getting Exam data. Here's what I have when I used the UserTests as the source starting point.
I have the following LINQ:
var userTests = _uow.UserTests
.GetAll()
.Include(t => t.Test)
.Include(t => t.Test.Exam)
.Where(t => t.UserId == "0" || t.UserId == userId)
.ToList();
When I check _uow.UserTests with the debugger it's a repository and when I check the dbcontext's configuration.lazyloading then it is set to false.
Here's my classes:
public class Exam
{
public int ExamId { get; set; }
public int SubjectId { get; set; }
public string Name { get; set; }
public virtual ICollection<Test> Tests { get; set; }
}
public class Test
{
public int TestId { get; set; }
public int ExamId { get; set; }
public string Title { get; set; }
public virtual ICollection<UserTest> UserTests { get; set; }
}
public class UserTest
{
public int UserTestId { get; set; }
public string UserId { get; set; }
public int TestId { get; set; }
public int QuestionsCount { get; set; }
}
When I looked at the output I saw something like this:
[{"userTestId":2,
"userId":"0",
"testId":12,
"test":{
"testId":12,"examId":1,
"exam":{
"examId":1,"subjectId":1,
"tests":[
{"testId":13,"examId":1,"title":"Sample Test1",
"userTests":[
{"userTestId":3,
"userId":"0",
Note that it gets a UserTest object, then gets a test object and then an exam object. However the exam object contains a test collection and then it heads back down again and gets the different tests and the unit tests inside of those:
UserTest > Test > Exam > Test > UserTest ?
I have tried hard to ensure lazy loading is off and debug tell me it's set to false. I am using EF6 and WebAPI but not sure if that makes a difference as I am debugging at the C# level.
You can't avoid that the inverse navigation properties are populated by EF, no matter if you load related entities with eager or lazy loading. This relationship fixup (as already explained by #Colin) is a feature you can't turn off.
You could solve the problem by nullifying the unwished inverse navigation properties after the query is finished:
foreach (var userTest in userTests)
{
if (userTest.Test != null)
{
userTest.Test.UserTests = null;
if (userTest.Test.Exam != null)
{
userTest.Test.Exam.Tests = null;
}
}
}
However, in my opinion the flaw of your design is that you try to serialize entities instead of data transfer objects ("DTOs") that are specialized to the view where you want to send the data to. By using DTOs you can avoid the inverse navigation properties that you don't want altogether and maybe other entity properties that you don't need in your view. You would define three DTO classes, for example:
public class ExamDTO
{
public int ExamId { get; set; }
public int SubjectId { get; set; }
public string Name { get; set; }
// no Tests collection here
}
public class TestDTO
{
public int TestId { get; set; }
public string Title { get; set; }
// no UserTests collection here
public ExamDTO Exam { get; set; }
}
public class UserTestDTO
{
public int UserTestId { get; set; }
public string UserId { get; set; }
public int QuestionsCount { get; set; }
public TestDTO Test { get; set; }
}
And then use a projection to load the data:
var userTests = _uow.UserTests
.GetAll()
.Where(ut => ut.UserId == "0" || ut.UserId == userId)
.Select(ut => new UserTestDTO
{
UserTestId = ut.UserTestId,
UserId = ut.UserId,
QuestionsCount = ut.QuestionsCount,
Test = new TestDTO
{
TestId = ut.Test.TestId,
Title = ut.Test.Title,
Exam = new ExamDTO
{
ExamId = ut.Test.Exam.ExamId,
SubjectId = ut.Test.Exam.SubjectId,
Name = ut.Test.Exam.Name
}
}
})
.ToList();
You could also "flatten" the object graph by defining only a single DTO class that contains all the properties you need for the view:
public class UserTestDTO
{
public int UserTestId { get; set; }
public string UserId { get; set; }
public int QuestionsCount { get; set; }
public int TestId { get; set; }
public string TestTitle { get; set; }
public int ExamId { get; set; }
public int ExamSubjectId { get; set; }
public string ExamName { get; set; }
}
The projection would become simpler and look like this:
var userTests = _uow.UserTests
.GetAll()
.Where(ut => ut.UserId == "0" || ut.UserId == userId)
.Select(ut => new UserTestDTO
{
UserTestId = ut.UserTestId,
UserId = ut.UserId,
QuestionsCount = ut.QuestionsCount,
TestId = ut.Test.TestId,
TestTitle = ut.Test.Title,
ExamId = ut.Test.Exam.ExamId,
ExamSubjectId = ut.Test.Exam.SubjectId,
ExamName = ut.Test.Exam.Name
})
.ToList();
By using DTOs you do not only avoid the problems of inverse navigation properties but also follow good security practices to "white-list" the exposed property values from your database explicitly. Imagine you would add a test access Password property to the Test entity. With your code that serializes eagerly loaded full entities with all properties the password would get serialized as well and run over the wire. You don't have to change any code for this to happen and in the worst case you wouldn't be aware that you are exposing passwords in a HTTP request. On the other hand when you are defining DTOs a new entity property would only be serialized with your Json data if you add this property explicitly to the DTO class.
Your query will load all UserTests into the context where UserId == "0" || UserId == userId and you have eagerly loaded the related Test and its related Exams.
Now in the debugger you can see that the Exams are linked to some Tests in memory and are assuming that is because they have been lazy-loaded. Not true. They are in memory because you loaded all UserTests into the context where UserId == "0" || UserId == userId and you have eagerly loaded the related Test. And they are linked to the navigation property because EF performs a "fix-up" based on foreign keys.
The Exam.Tests navigation property will contain any entities loaded into the context with the correct foreign key, but will not necessarily contain all Tests linked to the Exam in the database unless you eagerly load it or turn on lazy loading
I believe that deferred execution causes nothing to happen unless something is actually read from userTests. Try to include var userTestsAsList = userTests.ToList() and check with the debugger if userTestsAsList contains the desired sequence.
As far as I can read your POCO-Relationships and your query, your Repo is returning what you asked for. But did you know you asked for this?
You are navigating from Grand-Child <UserTest> to Child <Test> to Parent <Exam>
Your Entity of <Exam> is being treated as a Grand-Child when it seems to be a Grand-Parent (in fact, a graph root) having children of <Test> who have children / grand-children of type <UserTest>.
As you are eager loading (and serializing?), of course your <Exam> should eager load its <Test> Collection, which should load their <UserTest> Collections.
By working your way down the graph, you are causing a full circle.
Did you mean to have the opposite relationships?
public class Exam
{
public int ExamId { get; set; }
public int TestId { get; set; }
public int SubjectId { get; set; }
public string Name { get; set; }
}
public class Test
{
public int TestId { get; set; }
public int ExamId { get; set; }
public string Title { get; set; }
public virtual ICollection<UserTest> UserTests { get; set; }
}
public class UserTest
{
public int UserTestId { get; set; }
public string UserId { get; set; }
public int TestId { get; set; }
public int QuestionsCount { get; set; }
public virtual ICollection<Exam> Exams { get; set; }
}
I'm making many assumptions about your data. This relationships simply makes more sense as real-world entities and by your usage. That Users have Tests and Exams rather than the reverse. If so, this relationship should work with your linq query.
If you try something like:
var userTest = _uow.UserTests
.GetAll()
.Where(t => t.UserId == "0" || t.UserId == userId)
.First();
var name = userTest.Test.Title;
Would your code throw an exception because the Test property hasn't been loaded? I suspect the problem is your repository is using LINQ to SQL and not LINQ to Entities. You can't turn off Lazy Loading with LINQ to SQL. You would have to show how your repository works to fix the problem in that case.
Is there any reson you are using "virtual" for your collections? If you're using "include", I would recommend getting rid of the "virtual"

Categories

Resources