0

For some of the properties in my DB I need to manually calculate their Id #, so for those properties I do .Property(p => p.Id).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None); inside OnModelCreating

Then, in the repository I have a method to calculate out the Id for a given type. I would prefer to have the system be intelligent and check to see if the DatabaseGeneratedOption.None or DatabaseGeneratedOption.Identity and either return the next Id or 0.

How can I check (from inside a repository) what the DatabaseGeneratedOption for a given type (T) is?

Anthony Nichols
  • 1,586
  • 2
  • 25
  • 50
  • 1
    look at this, maybe it can help you http://stackoverflow.com/questions/21709302/get-identity-field-out-of-keymembers/21711234 – George Mar 22 '16 at 23:44

2 Answers2

0

Thanks to George's comment I was able to come up with this solution:

bool? _sqlGeneratesID;
bool IRepository<TEntity>.IsStoreGeneratedIdentity()
{
    if (!_sqlGeneratesID.HasValue)
    {
        var items = (_context as IObjectContextAdapter)?.ObjectContext.MetadataWorkspace.GetItems(DataSpace.SSpace).OfType<EntityType>();
        var entity = items.Single(x => x.Name == typeof(TEntity).Name);
        _sqlGeneratesID = entity.KeyProperties.FirstOrDefault()?.IsStoreGeneratedIdentity ?? false;
    }

    return _sqlGeneratesID.Value;
}
Anthony Nichols
  • 1,586
  • 2
  • 25
  • 50
  • Doesn't work if table names and class names don't match. – Gert Arnold Mar 23 '16 at 19:01
  • Not all my table names match. I am getting back items with full names like `"CodeFirstDatabaseSchema.Trait"` and in all cases the name is equal to the class/type name. The only exception I am seeing is for tables that are auto-generated by the system for linking lists to entities... Maybe I'm just getting lucky. But this works for me. :D – Anthony Nichols Mar 23 '16 at 19:33
  • 1
    OK, you work code first. With database-first you'd get the table names if you query the store model. – Gert Arnold Mar 23 '16 at 20:35
  • Yes code first. Good to know though if I ever need to work with DB first (and I do sometimes). Thanks! – Anthony Nichols Mar 23 '16 at 20:55
0

As I said in a comment, your solution works by virtue of a code-first store model returning the CLR type names from EntityType. A database-first store model, however, returns the store names. These names do not necessarily match the CLR type names.

To make this method independent of EF's store model, we need to access the store-CLR mapping space (CSSpace) and find the EntitySet (by CLR name) and match its KeyMembers with the columns in the store model (Property.Column), because these columns contain the right value of IsStoreGeneratedIdentity. (CLR properties also have this property, but it's always false).

So this is what we get (as a method encapsulated in a DbContext subtype):

public bool IsStoreGeneratedIdentity<TEntity>()
{
    var entityContainerMappings = (this as IObjectContextAdapter).ObjectContext
        .MetadataWorkspace.GetItems(DataSpace.CSSpace)
        .OfType<EntityContainerMapping>();

    var entityTypeMappings = entityContainerMappings
        .SelectMany(m => m.EntitySetMappings
            .Where(esm => esm.EntitySet.ElementType.Name == typeof(TEntity).Name))
        .SelectMany(esm => esm.EntityTypeMappings).ToArray();

    var keyMappings = (from km in entityTypeMappings.SelectMany(etm => etm.EntityType.KeyMembers)
        join pm in entityTypeMappings.SelectMany(etm => etm.Fragments)
            .SelectMany(mf => mf.PropertyMappings)
            .OfType<ScalarPropertyMapping>()
            on km.Name equals pm.Property.Name
        select pm
        );

    return keyMappings.Any(pm => pm.Column.IsStoreGeneratedIdentity);
}
Gert Arnold
  • 105,341
  • 31
  • 202
  • 291