1

Consider an ORM that maps tables to classes, and columns to properties. How should it handle a property that maps to an identity column for non-inserted objects?

For example,

[Table("MyTable")]
public class MyMappedType
{
    [Column("Id")]
    public int Id 
    { 
        get
        { 
            // ...
        }
    }
}

MyMappedType myObj = new MyMappedType();
int id = myObj.Id; // Object not inserted into database so value is missing

There are a few different options,

  1. Have the property getter throw an exception. This seems the most proper because if Id has not been assigned, any value returned is meaningless. However, it goes against guidelines stating property get methods should not throw exceptions.

  2. Have the property return a special value, i.e. 0 or -1. I believe in most ORM's the key properties are initialized with the default (0). However, this could be misleading if there is a row in the table with identity column value of 0.

  3. Force Id properties to be type Nullable<int> so it can return null until the value gets assigned.

Which option is best?

2

You answered yourself already. There is a reason most other ORMs go with option #2.

#1 should not even be considered, as throwing an exception from a property is extremely counterintuitive for the final user.

#3 is better, and mostly mirrors #2, in the sense that it assigns a default value (null in this case) to an unassigned property. However, Nullable<T> types are tricky to use, and would force your users to always check for HasValue before comparing, and other annoying complexity.

In the end, the only downside to #2 is that you can't have a row with an id of default(T), but that doesn't even makes any sense most of the time.
Also note that you aren't forced to have int as your primary key. OWIN default ApplicationUser implementation, for example, uses Guid as its primary key type (uniqueidentifier in SQL Server).

  • Properties are typically evaluated in debugger watch windows, where exceptions not only look bad, they are confusing. – Frank Hileman Mar 3 '17 at 14:23
  • In general I agree with you assertions, but there are cases where throwing an exception from a property is acceptable. One very common example is Nullable<T>.Value { get; } which throws an exception when HasValue = false. – Mr Anderson Mar 6 '17 at 2:05
0

One solution could be to avoid working directly with ORM entities before they have been inserted into the database, and not to use ORM entities in your business logic.

If you need to work with the data before inserting it, then a common solution would be to create a separate model for that data which is not part of your ORM structure, using a mapper (e.g. AutoMapper in C#), or even just some hand-rolled mapping code to switch between the two.

There are quite a few advantages of mapping from your ORM entity to a separate model; the main advantage being that it helps to decouple your Business Logic Layer away from your Data Access Layer, but also you can include other properties in your mapped 'Business Layer model' which don't need to be reflected in your database, as well as having the ability to map on to different types without affecting your underlying database schema.

For example:

[Table("Employee")]
public class Employee
{
    [Column("Id")]
    public int Id { get; set; }

    [Column("Name")]
    public string Name { get; set; }
}

public class EmployeeModel
{
    // OK - a Nullable<int> which isn't going to affect the DB schema
    // but it might be useful for the code which talks to the DB mapper
    public int? Id { get; set; }
    public string Name { get; set; }
}

// Assuming hand-rolled mapping code
public static class EmployeeModelExtensions
{
    public static Employee AsEmployee(this EmployeeModel model)
    {
        return new Employee
        {
            Id = model.Id ?? default(int),
            Name = model.Name,
        };
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.