Entity Framework - Determine the HasDatabaseGenera

2019-09-12 03:24发布

问题:

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?

回答1:

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;
}


回答2:

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);
}