none
Entity Framework Code First Many-To-Many RRS feed

  • Question

  • Hello!

    I'm new in entity framework, and I have to do next:

    There is one class, which is called User and another one-called Group.

    User can be contained in many groups. And group can be contained in many groups. And group can contain many users.

    So, my classes are:

    public class SecurityUser : SecuritySubject
        {
            /// <summary>
            /// Login, used to log in to system
            /// </summary>
            public string Login { get; set; }
            /// <summary>
            /// Storaged password hash. Used only if IsIntegratedSubject equals True
            /// </summary>
            public byte[] PasswordHash { get; set; }
        }public abstract class SecuritySubject : GuidKeyDatabaseEntity
        {
            #region Constructor
            protected SecuritySubject()
            {
               Groups = new HashSet<SecurityGroup>();
            }
            #endregion
    
            #region Properties
            /// <summary>
            /// Name of subject
            /// </summary>
            public string Name { get; set; }
            /// <summary>
            /// Description of subject
            /// </summary>
            public virtual string Description { get; set; }
            /// <summary>
            /// Flag, representing value, is this subject NT-integrated, or custom
            /// </summary>
            public bool IsIntegratedSubject { get; set; }
            /// <summary>
            /// Get or sets value, indicating, in which groups is current subject contained
            /// </summary>
            public ICollection<SecurityGroup> Groups { get; set; }
            #endregion
        }
    
    
    
                
    public class SecurityGroup : SecuritySubject
        {
            #region Constructor
            public SecurityGroup()
            {
                ChildSubjects = new HashSet<SecuritySubject>();
    
            }
            #endregion
    
            #region Properties
            /// <summary>
            /// Gets or sets value, representing enumeration of children
            /// </summary>
            public ICollection<SecuritySubject> ChildSubjects { get; set; } 
            #endregion
        }



    When i test it, It's allright. But only after filling: 

    using (var entry = new DatabaseEntryPoint(new SqlConnection("Server=msk-vm-develop; Initial Catalog=Test; Integrated Security=True;"), true))
                {
                    var user = new SecurityUser();
                    var firstGroup = new SecurityGroup { Name = "First group" };
                    var secondGroup = new SecurityGroup { Name = "Second group" };
                    entry.SecuritySubjects.Add(firstGroup);
                    entry.SecuritySubjects.Add(secondGroup);
                    entry.SecuritySubjects.Add(user);
                    firstGroup.ChildSubjects.Add(user);
                    user.Groups.Add(secondGroup);
                    firstGroup.ChildSubjects.Add(secondGroup);
                    entry.SaveChanges();
                    foreach (var securityGroup in entry.SecuritySubjects.OfType<SecurityGroup>())
                    {
                        foreach (var securitySubject in securityGroup.ChildSubjects)
                        {
                            foreach (var groupInWhichContained in securitySubject.Groups)
                            {
    
                            }
                        }
                    }
                }


    I see, that user is contained in both groups - firstGroup and secondGroup, and secondGroup is a child of firstGroup. 

    But! If I restart my test, after commenting all adding and creation operations:

    using (var entry = new DatabaseEntryPoint(new SqlConnection("Server=msk-vm-develop; Initial Catalog=Test; Integrated Security=True;"), true))
                {
                    //var user = new SecurityUser();
                    //var firstGroup = new SecurityGroup { Name = "First group" };
                    //var secondGroup = new SecurityGroup { Name = "Second group" };
                    //entry.SecuritySubjects.Add(firstGroup);
                    //entry.SecuritySubjects.Add(secondGroup);
                    //entry.SecuritySubjects.Add(user);
                    //firstGroup.ChildSubjects.Add(user);
                    //user.Groups.Add(secondGroup);
                    //firstGroup.ChildSubjects.Add(secondGroup);
                    //entry.SaveChanges();
                    foreach (var securityGroup in entry.SecuritySubjects.OfType<SecurityGroup>())
                    {
                        foreach (var securitySubject in securityGroup.ChildSubjects)
                        {
                            foreach (var groupInWhichContained in securitySubject.Groups)
                            {
    
                            }
                        }
                    }
                }


    ..., I recieve all groups successfully in first foreach cycle, but in nested cycle, when I'm waiting for secondGroup object and user object (in iteration of secondGroup), I see, that ChildObjects enumeration is empty.

    Can you help me with this behavior? Maybe, I've missed for any attributes?

    Thanks a lot!

    My DbContext implementation:

    public class DatabaseEntryPoint : DbContext
        {
            #region Constructor
            /// <summary>
            /// Initializes a new entity connection to database. 
            /// This connection ownes passing to parameter connection. 
            /// This means, that when this entry point will be disposed, <param name="databaseConnection"> will be disposed too</param>
            /// </summary>
            /// <param name="databaseConnection"></param>
            public DatabaseEntryPoint(DbConnection databaseConnection) : this(databaseConnection, true)
            {
                
            }
            /// <summary>
            /// Initializes a  new entity connection to database
            /// </summary>
            /// <param name="databaseConnection">Database connection, which will be used by this entry point</param>
            /// <param name="connectionOwner">If true, then <param name="databaseConnection"> will be disposed, when this point will be disposed, owerwise, false</param></param>
            public DatabaseEntryPoint(DbConnection databaseConnection, bool connectionOwner) : base(databaseConnection, connectionOwner)
            {
                Database.CreateIfNotExists();
                if (!Database.CompatibleWithModel(false))
                    Database.Initialize(false);
            }
            #endregion
    
            #region Properties
            public DbSet<Entities.Security.Subjects.SecuritySubject> SecuritySubjects { get; set; }
            #endregion
        }

    Thursday, February 21, 2013 10:45 AM

Answers

  • Hi Anton,

    Welcome to the MSDN forum.

    I think you have not loaded the related entities. Since you have not defined navigation property as virtual, it means you turn off lazy loading. You also have not use eager loading or explicit loading, so the related entities are not loaded. You could define navigation property as virtual to turn on lazy loading. You also could using this:

                    foreach (var securityGroup in entry.SecuritySubjects.Include(s => s.Groups.Select(p => p.ChildSubjects)).OfType<SecurityGroup>())
                    {
                        foreach (var securitySubject in securityGroup.ChildSubjects)
                        {
                            foreach (var groupInWhichContained in securitySubject.Groups)
                            {
    
                            }
                        }
                    }

    Best Regards,


    Alexander Sun [MSFT]
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Marked as answer by Anton Matyushov Wednesday, February 27, 2013 12:08 PM
    Friday, February 22, 2013 8:54 AM

All replies

  • Hi Anton,

    Welcome to the MSDN forum.

    I think you have not loaded the related entities. Since you have not defined navigation property as virtual, it means you turn off lazy loading. You also have not use eager loading or explicit loading, so the related entities are not loaded. You could define navigation property as virtual to turn on lazy loading. You also could using this:

                    foreach (var securityGroup in entry.SecuritySubjects.Include(s => s.Groups.Select(p => p.ChildSubjects)).OfType<SecurityGroup>())
                    {
                        foreach (var securitySubject in securityGroup.ChildSubjects)
                        {
                            foreach (var groupInWhichContained in securitySubject.Groups)
                            {
    
                            }
                        }
                    }

    Best Regards,


    Alexander Sun [MSFT]
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Marked as answer by Anton Matyushov Wednesday, February 27, 2013 12:08 PM
    Friday, February 22, 2013 8:54 AM
  • Alexander, thank you very much! I marked my properties as vitrual, and it works fine now.
    Wednesday, February 27, 2013 12:09 PM