Home » Ado.Net » Entity Framework » Complex Type cannot have reference to entities

Complex Type cannot have reference to entities

If you have a complex type, you cannot have within this one any reference to other entities. Let’s imagine that I have a money class.

    public class Money
    {
        public decimal Value { get; set; }
        public CurrencyType Currency { get; set; }
    }

Has you can see, it has a reference to another class, named CurrencyType. This type is abstract and must be set to a Currency class like USD or CND. However, this bring Entity Framework with a ModelValidationException.

System.Data.Entity.ModelConfiguration.ModelValidationException: One or more validation errors were detected during model generation:

DataAccess.Contexts.Implementations.CurrencyType: Name: Each type name in a schema must be unique. Type name ‘CurrencyType’ is already defined.

It is not possible to have this configuration. It would be very practical because you can have the Money in any of you class and the table would have the Money_Value and Money_CurrencyId.

One option is to remove the currency from the Money or to have both property of the Currency class inside the Money class. However, the strong positive that we had with CurrencyType was that this one was using Value Object pattern. This mean that it was strongly typed in the code and had a reference to a Currency table which had USD and CDN currency.

The question is how to use Entity Framework and still have an association between the amount of the money (value) and the currency without losing the strongly association with the table that has all currencies? This lead us to a second option that is to remove the money from the entity that has the money. Instead of having the money directly into the class, you can have all money into a separated table. This remove the use if complex type on the money table. Of course, this cause some overhead by having more tables and more joins to do to get all the information. It also has the problem of having the ID of the Money inside your Entity.

A possible solution that I have used is to loss the strong Foreign Key but still have the data directly into the entity that use the money. This way, less tables, more speed, and in the code we use the value object pattern. Here is the money class and the value object class for currency.

public class Money
{
	public decimal Value { get; set; }
	public int CurrencyTypeId {
		get { return this.Currency.Id; }
		set { this.Currency = CurrencyType.GetCurrencyFromId(value); }
	}
	///
/// This is ignored by EntityFramework
	///
	public CurrencyType Currency { get; set; }
}

public abstract class CurrencyType : ValueObject
{

	public static readonly CurrencyType Canada = new CanadianCurrency();
	public static readonly CurrencyType UnitedStatesOfAmerica = new USACurrency();

	public static CurrencyType GetCurrencyFromId(int value)
	{
		Type type = typeof(CurrencyType);

		var fields =  type.GetFields(BindingFlags.Public | BindingFlags.Static);
		foreach (var field in fields)
		{
			var fieldValue = field.GetValue(null) as CurrencyType;
			if (fieldValue != null)
			{
				if (fieldValue.Id == value)
				{
					return fieldValue;
				}
			}
		}
		throw new KeyNotFoundException(string.Concat(value, " cannot be found in any static fields."));
	}
}

As you can see, the money class does have the value and the CurrencyType property. This property is ignored by Entity Framework so Money can be as Complex Type. However, we have an integer field that can be used but should not since we have a strongly typed currency property. Here is how to make it works with Entity configuration.

public class MoneyConfiguration : ComplexTypeConfiguration
{
	public MoneyConfiguration()
	{
		this.Ignore(d => d.Currency);
	}
}

How to use this money class? The best way is to check the unit tests.

[TestMethod]
public void GivenAnInteger_WhenIntegerIsAKeyOfACurrency_ThenReturnConcreteCurrency()
{
    // Arrange
    var idOfCanada = CurrencyType.Canada.Id;
    var expected = CurrencyType.Canada;

    // Act
    var found = CurrencyType.GetCurrencyFromId(idOfCanada);

    //Assert
    Assert.IsInstanceOfType(found,typeof(CanadianCurrency));
    Assert.AreEqual(expected.Id,found.Id);
}

This is required because when we will load from Entity Framework, we will only get the integer. But, in the business logic the code will use the property. This is why the property of CurrencyTypeId is tightly bound with Currency with the reflection code. In the case that the Id is not legit (does not exist), an exception is thrown. This should never occur but we should still test the case.

[TestMethod]
public void GivenAnInteger_WhenIntegerIsNotAnExistingKeyOfACurrency_ThenThrowException()
{
    // Arrange
    const int idNonExisting = -1;

    // Act & Assert
    TestExtensions.Thrown(() => CurrencyType.GetCurrencyFromId(idNonExisting));
}

UnitTestPassed
To conclude, this might not be the best solution since it would be cleaner not to have this integer in the class. It would also be better not to have this integer without a foreign key to the CurrencyType table. However, since the value will be filled up and handled by Entity Framework only, we still control the integrity with the code. It would be possible for us to add manually the foreign key too.

If you like my article, think to buy my annual book, professionally edited by a proofreader. directly from me or on Amazon. I also wrote a TypeScript book called Holistic TypeScript

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.