How to use my domain classes with a standard EF context?

Jun 26, 2012 at 12:20 PM
Edited Nov 17, 2012 at 7:52 PM

Hi.

Update - 17 Nov 2012: See the following post for a sample implementation of achieving the below: http://nakedobjects.codeplex.com/discussions/403614

I believe that I could use my entity classes, implement and inject my own container, and use them with a standard EF context.

However I cannot find a way to instantiate a standard EF Context. I was thinking it is because you use a different T4Template.

In my edmx I have SmdCoreModelContainer in the Entity Container Name property. But there is no matching class by that name I can instantiate.

Am I making sense?

Coordinator
Jun 26, 2012 at 1:19 PM

You can simply add another standard T4 template that does generate them for you:

- Add > New Item  >  Online Templates > EF 4.x DbContextGenerator for C#

- Edit the added .tt file, to replace $edmxInputFile$  with the name of your .edmx file.

- Run the template. 


This can co-exist with the Naked Objects template.  You should find that this generates the object context for with the methods for accessing the entity sets.

Coordinator
Jun 26, 2012 at 1:38 PM
Edited Jun 26, 2012 at 2:34 PM

The two other things you will then have to do will be:

1. Take responsibility to inject any services that are needed into any object that is created and/or retrieved.

2. Provide an implementation of the IDomainObjectContainer.

The latter is not too difficult. The key container methods that you use are, typically, Instances<T>, NewTransientInstance<T>, and Persist.  In all cases, you need to delegate to broadly equivalent methods on the EF Context.

If all your object creation and retrieval is delegated to type-specific repositories and/or factories, then the most elegant solution would be for each of those to delegate to a type-specific injected 'container'  defined by your own interface.  The following shows an example (for the Instances method only):

 

    public class CustomerRepository {
        //Injected
        public ICustomerContainer CustomerContainer { set; protected get; }

        public IQueryable<Customer> FindCustomers(string name)  {
            return from obj in CustomerContainer.Instances()
                   where obj.Name == name
                   select obj;
        }
    }

    public interface ICustomerContainer {
        IQueryable<Customer> Instances();
    }

    public class NakedObjectsCustomerContainer : ICustomerContainer
    {
        //Injected
        public IDomainObjectContainer Container { set; protected get; }

        public IQueryable<Customer> Instances()  {
            return Container.Instances<Customer>();
        }
    }

    public class EFCustomerContainer : ICustomerContainer
    {
        //Injected (EF context)
        public Model1Container Model1Container { set; protected get; }

        public IQueryable<Customer> Instances() {
            return Model1Container.Customers;
        }
    }

 

Alternatively, you can write a completely generic implementation of IDomainObjectContainer, that generates the appropriate templated method calls dynamically (which is what we do within the Naked Objects code, incidentally). 

Coordinator
Jun 27, 2012 at 8:22 AM

I wrote, above: "Alternatively, you can write a completely generic implementation of IDomainObjectContainer, that generates the appropriate templated method calls dynamically (which is what we do within the Naked Objects code, incidentally). "

Thinking about this further, there's no need for the dynamic method creation as I had suggested  -  because you can delegate directly to the templated CreateObjectSet<T>()  method on the context.   (We only need to do the dynamic templated method creation in Naked Objects because of the way we handle the type meta-data) .

So the simplest approach would be merely to make the context (e.g. Model1Container in my example above) implement IDomainObjectContainer directly -  then you can just inject that into each object that needs the container.  You could either write this by hand, or modify the T4 template (referred to above) to generate it e.g.:

    public class MyEntityContainer : ObjectContext, IDomainObjectContainer
    {

        public IQueryable<T> Instances<T>() where T : class
        {
            return CreateObjectSet<T>();
        }

      //etc
    }

That way it doesn't matter whether all object creation/retrieval is delegated to repositories/factories or not.

 

Jun 27, 2012 at 8:28 AM
Great. Thank you. Hope I get to do that soon. :)
Jul 10, 2012 at 1:26 PM

Thinking about this further, there's no need for the dynamic method creation as I had suggested  -  because you can delegate directly to the templated CreateObjectSet<T>()  method on the context.   (We only need to do the dynamic templated method creation in Naked Objects because of the way we handle the type meta-data) .

I am missing something about the CreateObjectSet<T>(). Where is that method and where does it come from. It isn't on the EF Context that gets generated.

Coordinator
Jul 10, 2012 at 3:01 PM

CreateObjectSet<T>() is a method on System.Data.Objects.ObjectContext  (which I had MyEntityContainer inheriting from above).  That is also the root class of the Model1Container that is generated by the T4 template that I added (2nd posting in this thread).

I think that Microsoft now favours the use of System.Data.Entity.DbContext rather than ObjectContext, though they appear to fulfil broadly similar roles.  DbContext has a Set<TEntity>() method, which, I assume, is the equivalent.

Jul 10, 2012 at 7:19 PM

>- Add > New Item  >  Online Templates > EF 4.x DbContextGenerator for C#

You are right. The above online template that I found used DbContext, not ObjectContext.

Looks like Set<>() should work.

Thank you much.

Jul 12, 2012 at 5:38 AM
Edited Jul 12, 2012 at 5:39 AM

Ok, I have got 1 property and 3 methods remaining of IDomainObjectContainer that I don't quite know how to implement. Not sure what the idea behind them is.

Any tips?

 

        // Should I just fetch this off the HttpContext?
public System.Security.Principal.IPrincipal Principal { get { throw new NotImplementedException (); } } public void ObjectChanged (object obj) { throw new NotImplementedException (); } public void Resolve (object obj, object field) { throw new NotImplementedException (); } public void Resolve (object obj) { throw new NotImplementedException (); }
Coordinator
Jul 12, 2012 at 7:55 AM

The Principal can just be extracted from the Session as you suggest.

Are you actually calling Resolve or ObjectChanged in your domain code?   If not, there's no need to implement these methods.

Resolve just ensures that the object/field has already been loaded into memory; objectchanged notifies NakedObjects that a changed has been made -  and propogates this to the UI and/or persistor.  Some years ago, it was necessary to insert calls to these methods all over your domain code. That now all happens under the covers (in the proxy)  -  so it is rare that you need to make an explicit call to them.

Jul 12, 2012 at 6:34 PM

Ok. No I don't call them directly.

Nov 17, 2012 at 7:53 PM

The implementation as promised.

http://nakedobjects.codeplex.com/discussions/403614