0

I want the db set to be internal in order to ensure external packages only have access to and program against the interface not the concrete class

e.g.

namespace Domain
{
    public interface IProduct
    {
        string Description { get; }
        int Id { get; }
        decimal Price { get;  }
    }
}



//Separate Person.cs file for custom logic
namespace Domain
{
    internal partial class Product :IProduct
    {
    }
}

internal partial class POS : DbContext
{
    public POS()
        : base("name=POS")
    {
    }   

    internal DbSet<Product> Products { get; set; }
}

//The other Person.cs file is generated by the .tt file 

//_context.People is null which caused the dreaded null pointer exception :(
var people = _context.People.ToList();  

As soon as I set the access to the Person class and People entity set to public via the Model Browser it works again, but I want to restrict the access to internal for package encapsulation.

It worked with Context in VS2010 EF but not with DbContext in VS2012.

Any help is much appreciated :}

P.S.

For now I have just edited the .tt file as below

public <#=code.Escape(container)#>()
    : base("name=<#=container.Name#>")
{
    Products = Set<Product>(); 

This generates the context class as below which instantiates the set, it would be nice to not have to add this to the .tt file for every entity set in the model.

internal partial class POS : DbContext
{
    public POS()
        : base("name=POS")
    {
         Products = Set<Product>(); 
    }

1 Answers1

0

I know this question is old but I just ran into this issue as well. According to a number of other StackOverflow posts, this is still the behavior of EntityFramework and the solution is still to explicitly Set<> the entity sets.

That said, instead of having to manually add each entity name to the .tt file, I created some code that will cause the TT file to automatically generate this code for each entity.

In the *.Context.tt file, you should spot the code for the constructor that looks something like this:

    public <#=code.Escape(container)#>()
        : base("name=<#=container.Name#>")
    {
<#
if (!loader.IsLazyLoadingEnabled(container))
{
#>
        this.Configuration.LazyLoadingEnabled = false;
<#
}
#>
    }

Modify this so it now looks like:

   public <#=code.Escape(container)#>()
        : base("name=<#=container.Name#>")
    {
<#
if (!loader.IsLazyLoadingEnabled(container))
{
#>
        this.Configuration.LazyLoadingEnabled = false;
<#
}
#>

<#
    foreach (var entitySet in container.BaseEntitySets.OfType<EntitySet>())
    {
#>
        <#=codeStringGenerator.SetStatement(entitySet)#>

<#
}
#>
    }

Further down in the file you should see a class definition for the CodeStringGenerator class, add a new method (I added mine directly under the DbSet method definition around line 307):

public string SetStatement(EntitySet entitySet)
{
    return string.Format(
        CultureInfo.InvariantCulture,
        "{0} = Set<{1}>();",
        _code.Escape(entitySet),
        _typeMapper.GetTypeName(entitySet.ElementType));
}

When you save the template it should regenerate the DbContext class with the Set<> statements for each entity in your model. New entities that are added will re-trigger the template generation and those new entities will also be included in the constructor.

Andrew Keller
  • 3,198
  • 5
  • 36
  • 51