none
Self defined foreign key processing and EF Code First RRS feed

  • Question

  • In my project i have three classical layers. Business layer must be independent from interface and hide data layer. So Business layer create and call data context itself in static methods. By this reason and for performance data context recreated at each static cal in USING statement. By this way i cant use lazy loading and i implement analogue by myself. Analogue is simple: when i get or set navigation property in its accessor i set foreign key. It seems logical enough for model, but it broken by EDM. 

    I make playground example for investigate problem, but i cant found a bug. Please, look at example and tell, what is wrong?

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations.Schema;
    using System.Data;
    using System.Data.Entity;
    
    namespace Playground
    {
        class Base<T> where T : Base<T>
        {
            public Guid Id { get; set; }
            public static T Get(Guid id)
            {
                T result = null;
                using (DataContext dc = new DataContext())
                {
                    result = dc.Set<T>().Find(id);
                }
                return result;
            }
            public static T Add(T entity)
            {
                entity.Id = Guid.NewGuid();
                using (DataContext dc = new DataContext())
                {                
                    dc.Set<T>().Attach(entity);
                    dc.Entry(entity).State = EntityState.Added;
                    dc.SaveChanges();
                }
                return entity;
            }
    
        }
    
        class A:Base<A>
        {        
            public Guid BId { get; set; }
            [ForeignKey("BId")]
            public B B
            {
                get { return B.Get(BId); }
                set { BId = value.Id; }
            }
        }
        class B : Base<B> {}
        internal class DataContext : DbContext
        {
            public DbSet<A> A { get; set; }
            public DbSet<B> B { get; set; }
        }
        [NotMapped]
        class Program
        {
            static void Main(string[] args)
            {
                B b = new B();
                B.Add(b);
                A a = new A();
                a.B = b;
                A.Add(a);
            }        
        }
    }
     

    This code breaks on A.Add(a) with exception "Conflicting changes to the role 'A_B_Target' of the relationship 'Playground.A_B' have been detected." But no conflict founded: navigation link and id on it is similar to foreign key.

    If i remove definition of property all works well, but i must have it for my domain and validation.

    UPD: playground code changed, but nothing is change in problem.

    • Edited by PhEffetto Thursday, November 22, 2012 12:47 PM
    Thursday, November 22, 2012 12:21 PM

Answers

  • Only way i found to continue using codefirst - is implement stub private property to bind relation on it in codefirst. No other ways founded. I will use this crutch until community will make codefirst more flexible.
    • Marked as answer by PhEffetto Tuesday, November 27, 2012 8:01 AM
    Tuesday, November 27, 2012 7:52 AM

All replies

  • Sorry, I do not understand you. You are using EF Code First? So, and the your EDM? In EF Code First don't have EDM. EDM is to Database First or Model First

    Twitter: @MayogaX
    Blog: Dev Blog

    Friday, November 23, 2012 10:43 AM
  • I miss, i must write EF instead of EDM. But is not important: i think in EDM this problem also exist it partial class. The problem: i cant make custom navigation property accessors and keep using EF on this property.
    Friday, November 23, 2012 1:09 PM
  • Rage scream: why entity framework does not allow me update only one entity without walking by navigation property. I wanna simple update foreign keys and no more. But three days i looking for solution :)
    Saturday, November 24, 2012 10:45 AM
  • I wanna make my 3-tier application easy for develop interface tier. So i want not to provide a data context to interface layer, only calls to static methods of business entities. By this reason i must recreate context on every navigation properties walking. But on attach Entity Framework is trying to investigate these properties. Properties create its own context and generate queries to database. But it not needed - simply insert and update only one object, nomore :(
    Saturday, November 24, 2012 10:55 AM
  • By the way, codefirst cant ignore both navigation properties. Model first cant split namespaces. So, it is one way - write edmx manual? Entity Framework in deep view have week interfaces, whey cant realize all power of core by this two mizerable mistakes for me: namespaces and ignoring navigation properties. Sadly :(
    Saturday, November 24, 2012 12:11 PM
  • Only way i found to continue using codefirst - is implement stub private property to bind relation on it in codefirst. No other ways founded. I will use this crutch until community will make codefirst more flexible.
    • Marked as answer by PhEffetto Tuesday, November 27, 2012 8:01 AM
    Tuesday, November 27, 2012 7:52 AM
  • Hi PhEffetto,

    First, the pattern you use is not recommended. I recommend you have a class which contains the context instance (private), all entity instances (private), and all the public add, delete, update methods. You could take a look at this page: http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/implementing-the-repository-and-unit-of-work-patterns-in-an-asp-net-mvc-application

    Why the exception thrown on your current code is actually a.B and b reference different objects. You can use a.B.Equals(b) to check if they refer to the same object in memory: http://msdn.microsoft.com/en-us/library/bsc2ak47.aspx

    When you attach the a to Set<A>, Entity Framework find the B instance that foreign key BId refer to and the a.B refer to is not the same. Thus conflict occurs. Since you implement your own set method of property B in class A, so you cannot assign b to a.B directly like this: a.B = b; which ensure a.B and b refer to the same object in memory. Consequently, I recommend you use Repository and Unit of Work Patterns.

    Have a nice day.


    Alexander Sun [MSFT]
    MSDN Community Support | Feedback to us

    Tuesday, November 27, 2012 9:07 AM