2

Using C# and Entity Framework I've been playing around with some architecture ideas and running into a wall. My question here is How do I unit test the following structure? but perhaps I should be posting on the Software Engineering StackExchange and asking Is this a good architectural approach?

The Idea is that Entity Framework handles ID generation, and can create instances of classes with those IDs set, even if the setter is private

public class MyClass
{
    public int Id { get; private set; }

    public int MyValue { get; set; }
}

This way other code can never overwrite an Id value that should be database generated, new entries for the database must be created as new instantiations (rather than changing an Id to 0 or other questionable behaviours), etc.

But I'm having trouble using these classes in Unit Tests now. Previously I would mock the Database context so that requesting a table from the context just returned an in-memory collection of the expected model class, with properties like Id configured so other operations like .Where(i => i.Id == reqestedId) would work as expected:

mockContext.SetUp(c => c.MyClasses).Returns(myMockCollection);

How do I use those models to help test other parts of the application? Creating interfaces for each model data class seems excessive. Is specifying an internal setter and making the class visible to the testing project the best approach?

IronSean
  • 1,520
  • 17
  • 31
  • `private int Id { get; private set; }` doesn't make a lot of sense to have the `private` on the setter since the entire property is private. As an alternative you could use `internal` and then use the `InternalsVisibleTo` attribute to allow your test assembly to see internal properties. Another alternative is using `#if TEST` to change the modifier to public and define the `TEST` when running unit tests. – Ron Beyer Apr 05 '18 at 14:42
  • @RonBeyer that was a mistake when I typed the example, the Id property is supposed to be public. – IronSean Apr 05 '18 at 14:45
  • You can only mock virtual or interface members. So if you don´t want to extract an interface - which I can´t understand why - you should make the member `internal` in order to make it accessable in the test-project via `InternalsVisibleTo`. – MakePeaceGreatAgain Apr 05 '18 at 14:55
  • @IronSean did you ever figure out an elegant solution to this that doesn't involve exposing internals to the test project? I'm facing the same problem. I don't want Id to be settable by anything other than EF – drewob Nov 02 '18 at 14:56
  • @drewob Unfortunately I haven't, I'm still using the expose internal approach. – IronSean Nov 02 '18 at 15:00

4 Answers4

2

In EF Core 2.1 , entity types can have constructors.

public class MyClass
{
    public int Id { get; private set; }

    public int MyValue { get; set; }

    public MyClass(int id, int myValue)
    {
       this.Id = id;
       this.MyValue = myValue;
    }
}

EF Core considers properties with private setters as read-write properties. As a result of this, mapping will happen normally i.e. Id will still become a primary key and the key can also be store generated. You can read more here

Nish26
  • 969
  • 8
  • 16
  • 1
    This still exposes that constructor to misuse by other parts of the application. The only places that need to be able to set the Id are the Entity Framework (which can despite it being a private setter) and the testing application. Otherwise the goal is to prevent the Id from being set or modified within other code. – IronSean Apr 05 '18 at 17:24
  • 2
    The constructor can have any of the access modifiers - private, public, protected ,internal, protected internal ....You can define two constructors - a public parameter less constructor to be used in general and a private/internal parameterized constructor that will automatically used by EF. Now , you can make the internal constructors visible to your unit test project using [assembly:InternalsVisibleTo("UnitTestProject")] in AssemblyInfo.cs – Nish26 Apr 05 '18 at 17:35
1

The current approach I'm using is this:

public class MyClass
{
    public int Id { get; internal set; }

    public int MyValue { get; set; }
}

With [assembly:InternalsVisibleTo("my.Testing.Project")] in the AssemblyInfo.cs file for my models project.

This way my testing project is allowed to manually set the Id when needed, but other code cannot. It still exposes the Id for misuse within the project it was defined, but because that project is largely model classes, with all the business logic in a separate project it's relatively safe.

IronSean
  • 1,520
  • 17
  • 31
0

It seems MyClass represent data from the DB. Usually, I separate my domain class from DTO (Data Transfer object) and DTO has all public getters and setters. I would recommend having a separate domain object and a DTO.

For example,

  • Customer : Domain Class
  • CustomerDto : a property bag that holds data from database

CustomerRepository will know how to create Customer object from CustomerDto from database and Customer.Id will be privately set but CustomerDto.Id will be public.

Andrew Chaa
  • 6,120
  • 2
  • 45
  • 33
  • For many projects, DTO is an overload, especially for a desktop one. – Denis Kucherov Apr 05 '18 at 20:47
  • IMO, domain object is about more behaviour, rather than about data. Without DTO, domain class would be coupled to a table in DB. It would turn out a DB Driven Development, I'm afraid. – Andrew Chaa Apr 06 '18 at 08:12
  • DTO is about data and especially about data transfer. The only optional behavior appropriate for DTO object is self-serialization logic. The main purpose is to optimize cross-domain calls. DTO has nothing to do with DB Driven Development or DDD. – Denis Kucherov Apr 06 '18 at 12:23
  • Then how would you solve the coupling of the domain object to the DB table without DTO? Your domain class will have the same properties as the DB columns – Andrew Chaa Apr 09 '18 at 08:22
  • Here's a good topic about DTO. Just want to add that D in DTO stands for data (input, domain, signal) basically, any data not only database tables. https://stackoverflow.com/questions/1440952/why-are-data-transfer-objects-dtos-an-anti-pattern – Denis Kucherov Apr 09 '18 at 09:01
  • I don't think it answers my question. Also the link has an higher voted answer that DTO is not an Anti Patter ;-) – Andrew Chaa Apr 09 '18 at 13:43
  • DTO is not an anti-pattern. But it is not for decoupling domain class from DB table. – Denis Kucherov Apr 09 '18 at 13:49
0

I think that there is no point to hide Entity Id field and have this sort of pain in the neck.

First of all, this approach is not symmetric in a sense that you can have not surrogate Id (for example Guid Id, etc.). (Correction: Guid is surrogate too).

Furthermore, sometimes you actually need an ability to set Identity field and you can do this in EntityFramework by setting negative int for the entity Id. This option is really helpful is some scenarios.

Entity Framework is not handling Id generation, I think this is a wrong statement, for Identity Id key it just returns database generated value.

IMHO, this is a bad architectural approach.

Variant 2.You can use explicit interface.

public interface IEntity
{
    int Id { get; set; }
}

public class UserEntity : IEntity
{
    private int _id;

    public int Id
    {
        get { return _id; }
    }

    int IEntity.Id
    {
        get { return _id;}
        set { _id = value; }
    }

    public string Name { get; set; }
}

var user = new UserEntity();
((IEntity)user).Id = -1;
user.Name = "John";
var userId = user.Id;

There is also an option to add an internal interface to set Id value.

Denis Kucherov
  • 369
  • 3
  • 15