jleach
jleach

Reputation: 7792

Deny read/write on EF6 DbSet

I have an entity that I want managed only through it's parent entity. Consider purchased Items, a list of Vendors, and Approved Vendors for an Item:

// DOMAIN
public class Item {
    public string Name { get; set; }
    public virtual ICollection<ApprovedVendor> ApprovedVendors { get; set; }
}

public class Vendor {
    public string Name {get; set; }
}

public class ApprovedVendor {
    public int ItemID {get;set;}
    public int VendorID {get;set;}
    public decimal? Cost {get;set;}

    public virtual Item Item {get;set;}
    public virtual Vendor Vendor {get;set;}
}

// DATA (DbContext mappings)
public DbSet<ApprovedVendor> ApprovedVendors {get;set;}
public DbSet<Item> Items {get;set;}
public DbSet<Vendor> Vendors {get;set;}

// fluent entity mappings as per usual

What I'm trying to do is remove access to the context.ApprovedVendors from external assemblies, thus allowing approved vendors to only be managed through an Item. However, I still need the EF6 mappings as appropriate. Additionally, for integration tests to ensure the model builds from the connected database, I must access the DbSet<ApprovedVendor> from a test project. Thus, I made the following changes:

// in PDB.Data AssemblyInfo.cs
[assembly: InternalsVisibleTo("PDB.Data.Tests.Integration")]

// in dbcontext
internal DbSet<ApprovedVendor> ApprovedVendors {get;set;}   

// in PDB.Data.Tests.Integration
[TestMethod]
public void BuildsApprovedVendor() {
    var sut = _context.ApprovedVendors.FirstOrDefault();
    if (sut == null) {
        Assert.Inconclusive();
    }
    Assert.IsInstanceOfType(sut, typeof(Domain.Items.ApprovedVendor));
}

I thought this would do, but it appears that the DbSet<ApprovedVendor> must be public, as I get the following error running the test:

PDB.Data.Tests.Integration.ModelBuilding.BuildsApprovedVendor threw exception: System.ArgumentNullException: Value cannot be null. Parameter name: source

If I change internal back to public, everything works fine (except now my DbSet is public again...)

Can I do this (am I missing something), or am I stuck with throwing an Obsolete attribute on a public DbSet and hoping future devs pay attention?

Upvotes: 1

Views: 150

Answers (1)

Evk
Evk

Reputation: 101443

You can define dbset like this:

public DbSet<ApprovedVendor> ApprovedVendors {internal get;set;}

This will prevent doing anything with it from another assemblies (because getter is internal), except setting it, which usually doesn't make sense anyway. At the same time, because setter is still public - EF will be able to map that set correctly.

Upvotes: 2

Related Questions