I am start using TDD for the following class using Entity Framework 4.1:
public class Agent
{
// Primary key
public int ID { get; set; }
[Required]
public string Name { get; set; }
public string Address { get; set; }
public string City { get; set; }
public string Country { get; set; }
public string Phone1 { get; set; }
}
My assertion will fail:
/// <summary>
///A test for Agent Constructor need have name
///</summary>
[TestMethod()]
public void AgentConstructorTest()
{
Agent target = new Agent();
Assert.IsNull(target);
}
When I look at the generated target object, it is created with ID = 0. How could I test that Name is required then?
And if the Name field is required, how could I still create an Agent object? When will the real ID been assigned? To test model itself, do I need create/mockup a DbContext to be able to assigned a ID?
Keep in mind that your are just dealing with POCO classes here - there is no magic going on that would allow the construction of the Agent class to fail just because you have put a custom attribute on one of its properties.
Entity framework is checking for custom attributes during its validation and for data mapping - in this case it will check for the Required attribute and only declare the entity as "valid" if the corresponding string property is not null and also it will map Name to a non-nullable column in the database.
To mirror that you could write a custom validation routine in your unit test that performs the same checks, i.e. makes sure that all properties that are decorated with the Required attribute indeed have a value, i.e something like this:
[TestMethod()]
public void AgentWithNoNameIsInvalid()
{
Agent target = new Agent();
Assert.IsFalse(IsValid(target));
}
This does feel like you are testing EF now though, not your code.
Since the ID is your primary key it will only be assigned when the entity has been committed to the database. So yes for full testing you will have to mock a unit of work and a context that does this for you as well. There are many pitfalls though and subtle (and not so subtle) differences between IQueryable<T> and IEnumerable<T> that makes this approach very fragile.
Personally I would recommend you do integration testing with EF based on a separate test database with known content and write your unit tests and expected results based on this test database - this might not be true TDD but I found it is the only way to be sure that I am testing the right thing.
Related
I have a parent object that sets a child property with its foreign key to prevent duplication of the child in the database.
public class Unit
{
public Unit(UnitType unitType)
{
unitTypeCode = unitType.Id;
}
public UnitType UnitType {get; private set;}
private int unitTypeCode;
}
public class UnitType
{
public static UnitType Campus => new UnitType(1, nameof(Campus));
private UnitType(int id, string name)
{ ... }
public int Id {get; private set;}
public string Name {get;private set;}
}
This is sufficient for the application as the unit will always be retrieved through the DbContext before any further operations are done on it. The issue is when writing unit tests for this object, I am not using EF Core to test which is resulting in my UnitType property not being set.
[TestMethod]
public void Can_Set_Campus()
{
var unit = new Unit(UnitType.Campus);
// this will be false because unit.UnitType is never set
Assert.IsTrue(unit.UnitType == UnitType.Campus);
}
Is there a way to set the UnitType and have EF Core ignore it when saving (using the foreign key to create the relationship) but still populate the related property when retrieving the data?
Alternatively, is there a better way to handle testing for this type of situation?
Update:
more info on UnitType structure per Felipe's comment: There are 3 'UnitTypes' that can be assigned to a Unit:
Id
Name
1
Campus
2
Building
3
Room
I am creating a new object each time, which is why I am using the foreign key to create the relationship in EF Core (to avoid creating the duplicate that EF by default creates when the object is populated). Ultimately is there a way to tell EF "Don't create a record for the UnitType when saving, just add the _unitTypeId for foreign key reference"?
I know I could retrieve the UnitType from the DB every time, so it's already being tracked by EF, but I would prefer to not to have to do this. I'd like to keep only aggregate roots exposed in the DbContext.
We are managing status indicators in a similar way, so this issue manifests itself in multiple ways.
I am using EF Core and I have a scenario where the user can create a custom field and then creates options for that custom fields.
public class CustomField : Entity<long>
{
[Required]
public string Name { get; private set; }
public bool IsRequired { get; private set; }
public List<CustomFieldOption> customFieldOptions;
public virtual IReadOnlyCollection<CustomFieldOption> CustomFieldOptions => customFieldOptions;
protected CustomField()
{
}
public CustomField(long id, string name, bool isRequired, List<CustomFieldOption> customFieldOptions)
{
Id = id;
Name = name;
IsRequired = isRequired;
this.customFieldOptions = customFieldOptions;
}
}
public class CustomFieldOption : Entity<long>
{
[Required]
[MaxLength(256)]
public string Text { get; private set; }
protected CustomFieldOption()
{
}
public CustomFieldOption(string text)
{
Text = text;
}
}
public class Client : Entity<long>
{
public Name Name { get; set; }
private List<ClientCustomFieldOptionValue> customFieldOptionValues { get; set; } = new List<ClientCustomFieldOptionValue>();
public IReadOnlyCollection<ClientCustomFieldOptionValue> CustomFieldOptionValues => customFieldOptionValues;
public Client(Name name)
{
}
public Result AddCustomFieldOptionValues(List<ClientCustomFieldOptionValue> values)
{
return Result.Success();
}
public Result RemoveCustomFieldOptionValues(List<ClientCustomFieldOptionValue> values)
{
return Result.Success();
}
}
public class ClientCustomFieldOptionValue
{
public CustomFieldOption CustomFieldOption { get; private set; }
protected CustomFieldOptionValue()
{
}
public ClientCustomFieldOptionValue(CustomFieldOption customFieldOption)
{
CustomFieldOption = customFieldOption;
}
}
CustomFieldOption seems to be a Value Object as the text it holds is something that doesn't need an Id. But then in terms of store persistency needs an Id to be stored in database on a different table where it can be queries by Id etc...
I am not sure if I shall add it as an Entity because ValueObjects do not have Id.
One other problem I have is validation. If it is an Entity how can I validate Text property. I know validation on constructor is a bad idea. If I validate it in the ApplicationLayer then wherever I create a new object I have to validate that is not empty and the length.
If I forget to add validation in one of the application services and pass null Text then I create an inconsistent state.
Update #1
A Client can select one or many options of a custom field. I suppose these needed to be stored on a separate table ClientCustomFieldOptionValue. In that case is this an entity or a valueobject? And what about CustomFieldOption. Does it become an Entity? I am quite confused when to use Entity or ValueObjects
Try not to think of persistency details while designing domain model.
According to your description, CustomFieldOption expresses an individual property with no business relations to any other structure, thus:
it should not hold a business identifier
it should encapsulate its own validations
Meaning it fits the concept of a value-object (validation inside ctor).
When it comes to persistency, your repository model should be capable of storing CustomFieldOption objects in a child table (with DB identifier) referencing the parent table (CustomField objects)
On the query side, repository should be capable of aggregating data from these two tables into a single CustomField entity.
(How exactly you implement such DB capabilities depends on the ORM you choose to work with, EF in your case)
Just one observation, if you will use Ef Core and the containing entity has a one to many relationship with the value objects, you will have this limitation:
Owned Entity types, Ef Core
Owned types need a primary key. If there are no good candidates properties on the .NET type, EF Core can try to create one. However, when owned types are defined through a collection, it isn't enough to just create a shadow property to act as both the foreign key into the owner and the primary key of the owned instance
If you are mapping your entities and value objects using DbContext, you usually define an owned entity type for a value object or use a record type.
For owned entities, this creates a column in your table like this: EntityName_ValueObject (i.e. Person_Address) but this works for a single value object not a collection when you don't know in advance the number of items in the collection.
It is correct that you should not concern with persistence when designing your domain, but is also correct to think that having a value object with an identity does not make sense.
Most important, you should be aware of this potential issue early on.
Using different languages (php, .net) and frameworks(zf2), I fetch data from a database and store it into a model class. Every property of this class maps to a column on the database.
So if I have a table: tbl_user: user_id, user_name.
I would have a class: +User: +string user_id, +string user_name.
One of the TDD principles say: "Write some code that causes the test to pass"
Do I need to test the model too? Because it looks to me to be a really redundant test.
No. If the class only contains Properties / Fields and doesn't contain any logic, there is no need to test it. If you're concerned about code coverage, these classes will be 'tested' by the tests for whichever class consumes them.
For example:
public class DomainObject
{
public int Id{ get; set; }
public string Name {get;set; }
}
public class BusinessLogic
{
public void DoSomethingBusinessLike(DomainObject do)
{
//stuff happens
}
}
It is not necessary to test DomainObject directly, it is implicilty tested when you create tests for BusinessLogic.
The following class was Auto generated from a template using the Entity Framework Model.
namespace Entities
{
using System;
using System.Collections.Generic;
public partial class Country
{
public Country()
{
this.Regions = new HashSet<Region>();
}
public long CountryId { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public bool Preferred { get; set; }
public System.DateTime LastChanged { get; set; }
public virtual ICollection<Region> Regions { get; set; }
}
}
I have a Wcf web service that returns POX (Xml) and Json only. I am wanting to return my own serialised object like;
public class MyResponseObject
{
public int RequestId {get;set;}
public List<Country> CountryList {get;set;}
//other properties
}
But I don't want to return the Regions ICollection.
The object can then be returned using something like
Newtonsoft.Json.JsonConvert.SerializeObject()
Am I best returning my own serialised POCO object in this manner ?
In projects like these, your classes can be split into two types:
Database entity objects (what Entity Framework works with)
Data contract objects (what WCF or your web-service works with)
While it is possible to use the same objects for both, it is not recommended because the database entity objects are an internal implementation concern that is separate from the external interface (your webservice). You might add or remove columns to your database table and not want your API contracts to change. But usually you'll want to hide information from service-consumers, like a database table Users ( UserId, Password ), you definitely don't want the Password property going out!
Another reason not to is that you later might want to add attributes to your webservice contract classes (e.g. to control output formatting or input validation), adding these to entity objects is painful, if not impossible in some cases.
I know it sounds like a needless duplication of work as the majority of classes will have identical members, but it makes sense from a long-term perspective.
Fortunately tools like AutoMapper can speed-up the process of copying data from your database entity objects to your data contract objects.
I am running into some errors implementing my plan as described below. I am not so interested at this point in resolving particular errors as I am in whether or not this is a good idea.
All history-capable objects descend from a common class AuditableObject with a single property public Guid ID { get; set; }.
A descendent might be:
public class Taco : AuditableObject { public string Seasoning { get; set; } }
Now, I would like to implement the save event handler to write to the following table (class)
public class AuditItem
{
public Guid ID { get; set; }
public virtual AuditableObject Object { get; set; }
public string ObjectClassName { get; set; } //ugly
public string OldObjectXMLData { get; set; }
public string NewObjectXMLData { get; set; }
public DateTime Timestamp { get; set; }
}
I am not sure if I need the ObjectClassName as I can check the type of the object at runtime but it's there just in case.
On save I would basically serialize the object before and after to the respective properties, save a timestamp, and set the object - ie map the FK.
Is this an ugly way to go about it? Are there any obvious drawbacks to descending from a single class with EF Code First as I am doing?
I think you will need the full-qualified-name of the object's type when desalinizing, so that will be mandatory.
Alternatively serializing the object will be cause you problems. Assume that we are going to audit TacoOject1 of Taco class using the approch, the serialized data will be put in data base, later due to business changes we need to add another property to Taco, after recompilation when we need to deserilazed TacoOject1 we will get TypeMissMatchException (not sure of exception name).
Another design objection is using inheritance for audit process.
First: In reality Taco is not a AuditableObject , Its a roll played by Taco, using inheritance will violate Liskov Substitution Principle.
Second: You can not use multiple inheritance, think that if we had a TacoSupperClass, how we could audit Taco then?
If I where going to design auditing process, I would use Entity–attribute–value model Making AuditItem a marker interface and rename it to IAuditableEntity.
Having an attribute called AuditableProperty would enhance our process.
Any entity needs to be audited will be marked by IAuditableEntity, any property of the entity needed to be partcipated in audit will be marked by AuditableProperty attribute.
public class Taco : IAuditableEntity
{
[AuditableProperty]
public string Seasoning { get; set; }
[AuditableProperty]
public string OtherProperty1 { get; set; }
public string OtherProperty2 { get; set; }
}
The AuditLog table will have these columns:
1. EntityFullTypeName: (String) We are going to audit different entities, the field will be used to get meaningful reports .(mandatory)
2. ObjectIdentifier: Entity identifier that is being manipulated, primary key or business key of the entity.
3. FieldName: (String) Entity field name.
4. OldValue: (String) Entity field old value.
5. NewValue: (String) Entity field new value.
6. TransactionUser: Application user that makes the change. (mandatory)
7. TransactionID: Any operation changing the entities will need to have a unique transaction ID (like GUID) (mandatory), In case of an update on an entity changing multiple fields,these column will be the key point to trace all changes in the update(transcation)
8. ChangeDate: Transaction date. (mandatory)
9. FieldType: enumeration or text showing the field type like TEXT or Double. (mandatory)
In service layer when Taco1 is going to be updated(or inserted) we will check if Taco1 type is marked by IAuditableEntity using reflection(using a lazy chash to store reflection data), if so which properties have been changed(we need a separate DB call to fetch old values).
e.g :
Taco1 = new Taco();
Taco1.Seasoning = "old Seasoning value";
Taco1.OtherProperty1 = "Old Other Property1 value";
Taco1.OtherProperty2 = "Old Other Property2 value";
Saved before,now updating:
Taco1.Seasoning = "New Seasoning value";
Taco1.OtherProperty1 = "New Other Property1 value";
Taco1.OtherProperty2 = "New Other Property2 value";
We will insert two records in AuditLog with the same TransactionID:
Having this approach Any entity (table) could be traced Reports will be readableOnly changes will be logged.