Leron
Leron

Reputation: 9856

How Entity Framework 5 executes create/update when there is relation

Let's say I have two entities:

public class Menu
{
  //other properties..
  public virtual ICollection<SubMenu> SubMenus { get; set; }
}

and second entity:

public class SubMenu
{
  //other properties..
  public int MenuID { get; set; }
  public virtual Menu Menu { get; set; }
}

how does EF 5 keeps track on the relations between entities and what I need to pass in order to execute Create/Update operations?

There are several sample scenarios where I'm not pretty sure what I need and what I did not necessary need to pass as values in order to give EF 5 enough information to complete the task.

For example:

Let's say I have submitted form with data for a new Menu containing SubMenu that I want to save to my database. My code will be something like :

Menu menu = new Menu();
menu.Name = model.Name;
menu.Description = model.Description;
//and so on..

then for the sub menu I'll have something similar:

SubMenu subMenu = new SubMenu();
subMenu.Name = model.sub.Name;
subMenu.Image = model.sub.Image;
subMenu.Menu = //The menu does not exist yet...
subMenu.MenuID = //I don't have Menu so no ID either

how exactly EF 5 works in this scenario? How should I proceed in order to create records in Menu and SubMenu?

The second scenario that confuses me is when I have to create new record which I should relate to existing Menu. Again my code looks something like this:

SubMenu subMenu = new SubMenu();
subMenu.Name = model.sub.Name;
subMenu.Image = model.sub.Image;
subMenu.Menu = //hmm?
subMenu.MenuID = //hmm?

In this case I have an existing Menu so I can get both - exact Menu entity for this SubMenu or just the MenuId. When I look at the subMenu entity if I pass the whole Menu object like:

subMenu.Menu = menu //here menu is instance of Menu

I can see that now subMenu.Menu holds all the properties of the object including the MenuId do I need then to provide it anyway - subMenu.MenuId = menu.MenuId? How EF 5 is making the relation and what exactly do I need to provide?

Upvotes: 3

Views: 289

Answers (2)

phil soady
phil soady

Reputation: 11308

VahidN answer will work. But Im not sure it has addressed the Question in full.

EF Can do some checking before is sends commands to the database. Since it has model... You can look at the model too

 ObjectContext objContext = ((IObjectContextAdapter)context).ObjectContext;
 MetadataWorkspace workspace = objContext.MetadataWorkspace;
 IEnumerable<EntityType> managedTypes = workspace.GetItems<EntityType>(DataSpace.OSpace);

There are some referential constraints that the DB returns on saveChanges
It is worth looking at Context.GetValidationErrors()

You can Add MAIN Poco records, commit them (via saveChnages) and then add SubPoco items and save them.
BUT your question seems to ask: What if I add at same time...

You can ALSO add Both the Main and Sub together WITHOUT using NAV property on MAIN poco.

Consider

        var myMAINpoco = new MyMainPoco()
        {   // Id  is set by DB
            Content = "Test",
        };

        repMain.Add(mypoco);  // add to context 

        var mySubPoco2 = new MySubPoco()
        {
            //My id is determined by DB
           // MainId = // I dont know this yet, but thats OK, since I have the nav property set.
           // Ef will update MAINID afterwards for me.
            Content = "TEST content for SUBRECORD ",
            ParentPoco = mypoco // THE NAV property of the New MAINPOCO
                                // that will be  commited with me
        };

        repSub.Add(mySubPoco2);

        //subpoco.mainid = 0
        uow.Commit();
        //subpoco.mainid = n and refers the NEW id Just allocated to MainPoco.Id

Upvotes: 1

VahidN
VahidN

Reputation: 19136

To create Menu and SubMenu at the same time, you need to implement the null object pattern (initialize the SubMenu in the ctor of the Menu class)

public class Menu
{
    public int Id { set; get; }
    public string Name { set; get; }
    public string Description { set; get; }

    public virtual ICollection<SubMenu> SubMenus { get; set; }

    public Menu()
    {
        SubMenus = new List<SubMenu>();
    }
}

Then you can add the sub-menus using SubMenus.Add method

    using (var context = new MyContext())
    {
        Menu menuItem1 = new Menu();
        menuItem1.Name = "modelName";
        menuItem1.Description = "modelDescription";

        SubMenu subMenu1 = new SubMenu();
        subMenu1.Name = "subName1";

        menuItem1.SubMenus.Add(subMenu1); // how to define sum-menus (SubMenus is not null anymore)

        context.Menus.Add(menuItem1);

        context.SaveChanges();
    }

Now EF runs these queries automatically. It inserts the root item, then uses its Id for the child items:

INSERT [dbo].[Menus]([Name], [Description])
VALUES (@0, @1)
SELECT [Id]
FROM [dbo].[Menus]
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()
-- @0: 'modelName' (Type = String, Size = -1)
-- @1: 'modelDescription' (Type = String, Size = -1)
-- Executing at 2013/12/11 02:27:50 ?.? +03:30
-- Completed in 43 ms with result: SqlDataReader

INSERT [dbo].[SubMenus]([Name], [MenuID])
VALUES (@0, @1)
SELECT [Id]
FROM [dbo].[SubMenus]
WHERE @@ROWCOUNT > 0 AND [Id] = scope_identity()
-- @0: 'subName1' (Type = String, Size = -1)
-- @1: '1' (Type = Int32)
-- Executing at 2013/12/11 02:27:50 ?.? +03:30
-- Completed in 26 ms with result: SqlDataReader

And creating a new SubMenu and relating it to an existing Menu, is the same as before. Use existingMenuItem.SubMenus.Add(newSubMenuItem);

    using (var context = new MyContext())
    {
        var existingMenuItem = context.Menus.Find(1);

        SubMenu newSubMenu = new SubMenu();
        newSubMenu.Name = "subName2";

        existingMenuItem.SubMenus.Add(newSubMenu); // how to define sum-menus
        context.SaveChanges();
    }

Upvotes: 3

Related Questions