12

I know the question already has a solution (eg. this question) but I really can't afford to attach the mapping logic in the same assembly where the domain (POCO classes) is.

Is there any other way?

I found this nice blog post but I couldn't get it working. Here is the model:

public class Institute
{
    /**
        Code omitted
    **/

    protected virtual ICollection<InstituteText> InnerInstituteTexts { get; set; }

    private InstituteTextSet _TextSets;

    public InstituteTextSet Texts 
    {
        get 
        {
            if (_TextSets == null)
                _TextSets = new InstituteTextSet(InnerInstituteTexts);

            return _TextSets;
        }
    }
}

Mapping code:

var instituteTextExpression = ObjectAccessor<Institute>.CreateExpression<ICollection<InstituteText>>("InnerInstituteTexts");

institute.HasMany(instituteTextExpression)
    .WithRequired()
    .HasForeignKey(t => t.InstituteId);

where CreateExpression is defined as:

public static Expression<Func<T, TResult>> CreateExpression<TResult>(string propertyOrFieldName)
{
    ParameterExpression param = Expression.Parameter(typeof(T), "propertyOrFieldContainer");
    Expression body = Expression.PropertyOrField(param, propertyOrFieldName);
    LambdaExpression lambda = Expression.Lambda(typeof(Func<T, TResult>), body, param);

    return (Expression<Func<T, TResult>>) lambda;
}

the error I get is:

Initialization method Studentum.Core.Tests.InstituteTests.Initialize threw exception. System.TypeInitializationException: System.TypeInitializationException: The type initializer for 'Studentum.Core.FluentCoreRepositoryFactory' threw an exception. ---> System.InvalidOperationException: The configured property 'InnerInstituteTexts' is not a declared property on the entity 'Institute'. Verify that it has not been explicitly excluded from the model and that it is a valid primitive property..

Community
  • 1
  • 1
Kralizek
  • 1,999
  • 1
  • 28
  • 47
  • I *know* it's not the answer you're looking for, but do you know NHibernate supports that scenario out of the box and without hacks? – Diego Mijelshon Mar 09 '11 at 13:51
  • We used NHibernate in a test project together with the Fluent mapping "framework". Did you try this? http://wiki.fluentnhibernate.org/Fluent_mapping_private_properties – Kralizek Mar 09 '11 at 13:54
  • @Kralizek: As said, this is supported out of the box with XML mappings. It's probably harder to do with Fluent (I don't use it), but that's not what I suggested. – Diego Mijelshon Mar 09 '11 at 14:48
  • @DiegoMijelshon: sorry! i misunderstood your comment. I thought you were asking if NHibernate supported this scenario, and not suggesting it. Anyway, we took off NH because of some weird requisites on the classes (everything has to be virtual, can't use custom collections and so on). Basically we felt like NH forced us to bend our model to its need. We didn't like the idea, and, lucky, EF4 hasn't the same requirements. We're quite happy with the classic EF4 but EF4.1 is even better for the fluent mappings and for the new surface API. The only problem are this non-public properties. – Kralizek Mar 09 '11 at 16:40
  • 1
    @Kralizek: in my experience it's the other way around: NH doesn't force anything weird except the virtual properties (which are also required by EF if you want lazy loading, BTW) and it's highly extensible. EF is extremely rigid, and doesn't support the most basic things (like enums). But do whatever works for you :-) – Diego Mijelshon Mar 09 '11 at 16:58
  • I've been able to successfully map private primitive properties in EF Code First using an approach similar to the one above but haven't tried an association property. One recommendation is to see if you can do this in model-first Entity Framework (i.e. in EDMX). If you can then at least you will know that what you're trying to accomplish is actually supported by EF and that your efforts to find a way to do it in Code First are not futile. If it **does** work in EDMX you could always export your Code First model to EDMX so you can continue to make progress with your desired structure. – Dan Mork Jun 18 '11 at 22:03
  • Mapping private properties is supported indeed. The problem is the lack of a tool for mapping those properties using CF and the model builder approach. – Kralizek Jun 20 '11 at 06:29
  • Your application may not have sufficient reflection permission to access non public members. Is this a web application? – Akash Kava Sep 15 '11 at 07:37
  • it is, but we're already running a lot of reflection stuff. shouldn't i have got a security exception if i didn't? – Kralizek Sep 15 '11 at 07:58
  • @Kralizek, no you will not get security exception, you are already getting an exception that the member is not (publicly) defined, by default ASP.NET will only allow reflection of public members, you can not access internal,private or protected members, this has nothing to do with Entity Framework. Same class in same assembly may get access to non public members, but Entity Framework dll is somewhere else, and it may not be able to access your dll's non public members. – Akash Kava Sep 16 '11 at 16:22
  • @Akash Kava: The exceptions you get when reflecting on non-public members in a restricted context are very different (see `MemberAccessException`). I believe this is a different error, perhaps some code (EF or otherwise) looking for members with only `BindingFlags.Public`. – Michael Petito Sep 18 '11 at 01:03

1 Answers1

8

The first thing that came to mind is the InternalsVisibleTo assembly attribute. This allows you to name "friend" assemblies that also have access to internal members. I wasn't sure if this would work with EF CodeFirst, but I tried it and it appears to work just fine. You would have to change your model assembly slightly, but I think it is a reasonable change.

You would first need to change your property declaration to protected internal:

 protected internal virtual ICollection<InstituteText> InnerInstituteTexts { get; set; }

Then, in your model assembly, add the InternalsVisibleTo assembly attribute in your AssemblyInfo.cs file with the name of your mapping assembly.

[assembly: InternalsVisibleTo("MappingAssemblyName")]

Finally, you can define your mapping of the property in the mapping assembly like any other publicly accessible property.

institute.HasMany(i => i.InnerInstituteTexts)
    .WithRequired()
    .HasForeignKey(t => t.InstituteId);
Michael Petito
  • 12,891
  • 4
  • 40
  • 54
  • I read about this solution but isn't this breaking the "persistence unaware context" requisite since the assembly containing the entities must reference the assembly containing the mappings. – Kralizek Sep 19 '11 at 07:53
  • 2
    It is naming your mapping assembly in the assembly attribute. However, it is not referencing the assembly (no circular dependency problem), the mapping classes, entity framework, or anything persistence specific. I would argue from a practical perspective this still leaves your model "persistence unaware". If you want to get to a very technical level about "awareness", one could make the claim that your model is already exposed to details about persistence because you must make all mapped properties `virtual`. – Michael Petito Sep 19 '11 at 15:29
  • This is the way to go. I'm just a bit disappointed that a feature supported in EF4 is not supported in Code-First. – Kralizek Sep 20 '11 at 07:47