How can I go about changing the naming convention of the auto-generated many-to-many table?
Assume I have two classes:
public class User
{
public int UserId { get; set; }
public virtual List<Role> Roles { get; set; }
}
public class Role
{
public int RoleId { get; set; }
public virtual List<User> Users { get; set; }
}
By Default, this will create a table called UserRoles.
I can change the name of that one table to UsersInRoles, for example, by using the following in the OnModelCreating override of my DbContext:
modelBuilder.Entity<User>()
.HasMany(p => p.Roles)
.WithMany(p => p.Users)
.Map(mc =>
{
mc.MapLeftKey("UserId");
mc.MapRightKey("RoleId");
mc.ToTable("UsersInRoles");
});
However, what I really want to do is change the naming convention so that by default, all auto-generated many-to-many tables use this new convention. I cannot figure out how to do that, or if it's even possible. I do not like having to specify 9 lines of extra code every time I specify one of these relationships.
I am currently using EF version 6.0.0-rc1.
The ability to control relationships was removed from the basic conventions API before release because it wasn't in a usable state. You can access all of the properties and tables in the model through model based conventions. An overview of model based conventions is available here: http://msdn.microsoft.com/en-US/data/dn469439
This solution involves a little more digging around in the metadata API, EntitySet is the correct type for this scenario
This convention should rename the generated relation table:
public class MyConvention : IStoreModelConvention<EntitySet>
{
public void Apply(EntitySet set, DbModel model)
{
var properties = set.ElementType.Properties;
if (properties.Count == 2)
{
var relationEnds = new List<string>();
int i = 0;
foreach (var metadataProperty in properties)
{
if (metadataProperty.Name.EndsWith("_ID"))
{
var name = metadataProperty.Name;
relationEnds.Add(name.Substring(0, name.Length - 3));
i++;
}
}
if (relationEnds.Count == 2)
{
set.Table = relationEnds.ElementAt(0) + "_" + relationEnds.ElementAt(1) + "_RelationTable";
}
}
}
Related
I'm using the latest version of ABP from abp.io and have two entities with a many-many relationship. These are:
public class GroupDto : AuditedEntityDto<Guid>
{
public GroupDto()
{
this.Students = new HashSet<Students.StudentDto>();
}
public string Name { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<Students.StudentDto> Students { get; set; }
}
and
public class StudentDto : AuditedEntityDto<Guid>
{
public StudentDto()
{
this.Groups = new HashSet<Groups.GroupDto>();
}
public string Name { get; set; }
public bool IsActive { get; set; }
public virtual ICollection<Groups.GroupDto> Groups { get; set; }
}
I set up the following test to check that I am retrieving the related entities, and unfortunately the Students property is always empty.
public async Task Should_Get_List_Of_Groups()
{
//Act
var result = await _groupAppService.GetListAsync(
new PagedAndSortedResultRequestDto()
);
//Assert
result.TotalCount.ShouldBeGreaterThan(0);
result.Items.ShouldContain(g => g.Name == "13Ck" && g.Students.Any(s => s.Name == "Michael Studentman"));
}
The same is true of the equivalent test for a List of Students, the Groups property is always empty.
I found one single related answer for abp.io (which is not the same as ABP, it's a newer/different framework) https://stackoverflow.com/a/62913782/7801941 but unfortunately when I add an equivalent to my StudentAppService I get the error -
CS1061 'IRepository<Student, Guid>' does not contain a definition for
'Include' and no accessible extension method 'Include' accepting a
first argument of type 'IRepository<Student, Guid>' could be found
(are you missing a using directive or an assembly reference?)
The code for this is below, and the error is being thrown on the line that begins .Include
public class StudentAppService :
CrudAppService<
Student, //The Student entity
StudentDto, //Used to show students
Guid, //Primary key of the student entity
PagedAndSortedResultRequestDto, //Used for paging/sorting
CreateUpdateStudentDto>, //Used to create/update a student
IStudentAppService //implement the IStudentAppService
{
private readonly IRepository<Students.Student, Guid> _studentRepository;
public StudentAppService(IRepository<Student, Guid> repository)
: base(repository)
{
_studentRepository = repository;
}
protected override IQueryable<Student> CreateFilteredQuery(PagedAndSortedResultRequestDto input)
{
return _studentRepository
.Include(s => s.Groups);
}
}
This implements this interface
public interface IStudentAppService :
ICrudAppService< // Defines CRUD methods
StudentDto, // Used to show students
Guid, // Primary key of the student entity
PagedAndSortedResultRequestDto, // Used for paging/sorting
CreateUpdateStudentDto> // Used to create/update a student
{
//
}
Can anyone shed any light on how I should be accessing the related entities using the AppServices?
Edit: Thank you to those who have responded. To clarify, I am looking for a solution/explanation for how to access entities that have a many-many relationship using the AppService, not the repository.
To aid with this, I have uploaded a zip file of my whole source code, along with many of the changes I've tried in order to get this to work, here.
You can lazy load, eagerly load or configure default behaviour for the entity for sub-collections.
Default configuration:
Configure<AbpEntityOptions>(options =>
{
options.Entity<Student>(studentOptions =>
{
studentOptions.DefaultWithDetailsFunc = query => query.Include(o => o.Groups);
});
});
Eager Load:
//Get a IQueryable<T> by including sub collections
var queryable = await _studentRepository.WithDetailsAsync(x => x.Groups);
//Apply additional LINQ extension methods
var query = queryable.Where(x => x.Id == id);
//Execute the query and get the result
var student = await AsyncExecuter.FirstOrDefaultAsync(query);
Or Lazy Load:
var student = await _studentRepository.GetAsync(id, includeDetails: false);
//student.Groups is empty on this stage
await _studentRepository.EnsureCollectionLoadedAsync(student, x => x.Groups);
//student.Groups is filled now
You can check docs for more information.
Edit:
You may have forgotten to add default repositories like:
services.AddAbpDbContext<MyDbContext>(options =>
{
options.AddDefaultRepositories();
});
Though I would like to suggest you to use custom repositories like
IStudentRepository:IRepository<Student,Guid>
So that you can scale your repository much better.
UPDATED: The sample code listed below is now complete and sufficient
to generate the shadow alternate key in Conference. When the Meeting
entity inherits from a base entity containing a RowVersion attribute
the shadow alternate key is generated in the Conference entity.
If that attribute is included directly in the Meeting entity,
without inheritance, the shadow alternate key is not generated.
My model worked as expected in EF Core 3.1. I upgraded to .Net 5 and EF Core 5, and EF adds shadow alternate key attribute(s) named TempId to several entities. EF can't load those entities unless I add those attributes to the database. The shadow alternate key properties are NOT used in any relationships that I can find in the model. Virtually all discussion of shadow properties is either for foreign keys or hidden attributes. I can't find any explanation for why EF would add a shadow alternate key, especially if it doesn't use the attribute. Any suggestions?
One of the entities that gets a shadow alternate key is Conference, which is the child in one relationship and the parent in another. I have many similar entities which do NOT get a shadow alternate key, and I cannot see any difference between them.
I loop through the model entities identifying all shadow properties and all relationships using an alternate key for the principal key. None of the shadow alternate keys are used in a relationship. I do see the two defined relationships where I specifically use an alternate key, so I believe my code is correct.
Here is a complete simplified EF context and its two entities which demonstrates the problem.
using Microsoft.EntityFrameworkCore;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace EFShadow
{
public partial class Conference
{
public Conference()
{
Meetings = new HashSet<Meeting>();
}
[Key]
public string ConferenceCode { get; set; }
[Required]
public string ConferenceName { get; set; }
public ICollection<Meeting> Meetings { get; }
}
public partial class Meeting : BaseEntity
{
public Meeting() { }
[Key]
public int MeetingId { get; set; }
[Required]
public string ConferenceCode { get; set; }
[Required]
public string Title { get; set; }
public Conference Conference { get; set; }
}
[NotMapped]
public abstract partial class BaseEntity
{
[Timestamp]
public byte[] RowVersion { get; set; }
}
public class EFShadowContext : DbContext
{
public EFShadowContext(DbContextOptions<EFShadowContext> options)
: base(options)
{
ChangeTracker.LazyLoadingEnabled = false;
}
public DbSet<Conference> Conferences { get; set; }
public DbSet<Meeting> Meetings { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<Conference>(entity =>
{
entity.HasKey(e => e.ConferenceCode);
entity.ToTable("Conferences", "Settings");
entity.Property(e => e.ConferenceCode)
.IsRequired()
.HasMaxLength(25)
.IsUnicode(false)
.ValueGeneratedNever();
entity.Property(e => e.ConferenceName)
.IsRequired()
.HasMaxLength(100);
});
builder.Entity<Meeting>(entity =>
{
entity.HasKey(e => e.MeetingId);
entity.ToTable("Meetings", "Offerings");
entity.Property(e => e.ConferenceCode).HasMaxLength(25).IsUnicode(false).IsRequired();
entity.Property(e => e.Title).HasMaxLength(255).IsRequired();
//Inherited properties from BaseEntityWithUpdatedAndRowVersion
entity.Property(e => e.RowVersion)
.IsRequired()
.IsRowVersion();
entity.HasOne(p => p.Conference)
.WithMany(d => d.Meetings)
.HasForeignKey(d => d.ConferenceCode)
.HasPrincipalKey(p => p.ConferenceCode)
.OnDelete(DeleteBehavior.Restrict)
.HasConstraintName("Meetings_FK_IsAnOccurrenceOf_Conference");
});
}
}
}
Here is the code I use to identify the shadow key.
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
namespace ConferenceEF.Code
{
public class EFModelAnalysis
{
readonly DbContext _context;
public EFModelAnalysis(DbContext context)
{
Contract.Requires(context != null);
_context = context;
}
public List<string> ShadowProperties()
{
List<string> results = new List<string>();
var entityTypes = _context.Model.GetEntityTypes();
foreach (var entityType in entityTypes)
{
var entityProperties = entityType.GetProperties();
foreach (var entityProperty in entityProperties)
{
if (entityProperty.IsShadowProperty())
{
string output = $"{entityType.Name}.{entityProperty.Name}: {entityProperty}.";
results.Add(output);
}
}
}
return results;
}
public List<string> AlternateKeyRelationships()
{
List<string> results = new List<string>();
var entityTypes = _context.Model.GetEntityTypes();
foreach (var entityType in entityTypes)
{
foreach (var fk in entityType.GetForeignKeys())
{
if (!fk.PrincipalKey.IsPrimaryKey())
{
string output = $"{entityType.DisplayName()} Foreign Key {fk.GetConstraintName()} " +
$"references principal ALTERNATE key {fk.PrincipalKey} " +
$"in table {fk.PrincipalEntityType}.";
results.Add(output);
}
}
}
return results;
}
}
}
Here is the context initialization and processing code.
var connectionSettings = ((LoadDataConferencesSqlServer)this).SqlConnectionSettings;
DbContextOptionsBuilder builderShadow = new DbContextOptionsBuilder<EFShadowContext>()
.UseSqlServer(connectionSettings.ConnectionString);
var optionsShadow = (DbContextOptions<EFShadowContext>)builderShadow.Options;
using EFShadowContext contextShadow = new EFShadowContext(optionsShadow);
EFModelAnalysis efModelShadow = new EFModelAnalysis(contextShadow);
var shadowPropertiesShadow = efModelShadow.ShadowProperties();
foreach (var shadow in shadowPropertiesShadow)
progressReport?.Report(shadow); //List the shadow properties
var alternateKeysShadow = efModelShadow.AlternateKeyRelationships();
foreach (var ak in alternateKeysShadow)
progressReport?.Report(ak); //List relationships using alternate key
The output I get is:
EFShadow.Conference.TempId: Property: Conference.TempId (no field, int) Shadow Required AlternateKey AfterSave:Throw.
No relationship uses this alternate key.
If I eliminate the Meeting entity's inheritance from BaseEntity and include the RowVersion timestamp property directly in Meeting, no shadow key is generated. That's the only change required to make the difference.
Tricky confusing issue, worth reporting it to EF Core GitHub issue tracker.
Using trial and error approach, looks like the strange behavior is caused by the [NotMapped] data annotation applied to the base class.
Remove it from there (and all other similar places) and the problem is solved. In general don't apply that attribute on model classes. Normally you don't need to explicitly mark a class as "non entity" if its is not referenced by navigation property, DbSet or Entity<>() fluent call. And if you really want to make sure explicitly it isn't used as entity, use Ignore fluent API instead, because the attribute breaks the default conventions which are applied before OnModelCreating.
e.g.
//[NotMapped] <-- remove
public abstract partial class BaseEntity
{
[Timestamp]
public byte[] RowVersion { get; set; }
}
and optionally
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Ignore<BaseEntity>(); // <-- add this
// the rest...
}
I have the following Entity class definition:
[Table("Users")]
public class WebUser
{
public virtual int Id { get; set; }
public virtual ICollection<Client> Clients { get; set; }
// more properties...
}
Notice that table name is different than the class name. I also have a ClientUsers table which is a many-to-many mapping for clients and users. Problem is, when I try to access the webUser.Clients property I get the following exception:
"Invalid object name 'dbo.ClientWebUsers'."
Looks like Entity Framework is trying to guess the name of the third table, but it apparently was not smart enough to take into account the table attribute that I have there. How can I tell EF that it is ClientUsers and not ClientWebUsers? Also what rule does it follow to know which table name comes first and which one comes second in the new table name? I think it's not alphabetical order.
I'm using EF 5.0. Thanks!
From the looks of things you're using Code First, so I'll answer accordingly. If this is incorrect, please let me know.
I believe the convention being used to determine the name of the many-to-many table is determined by the order in which they occur as DbSet properties in your SomeContext : DbContext class.
As for forcing EntityFramework to name your table whatever you like, you can use the Fluent API in the OnModelCreating method of your SomeContext : DbContext class as follows:
public class DatabaseContext : DbContext
{
public DatabaseContext()
: base("SomeDB")
{
}
public DbSet<WebUser> Users { get; set; }
public DbSet<Client> Clients { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<WebUser>().HasMany(c => c.Clients)
.WithMany(p => p.WebUsers).Map(
m =>
{
m.MapLeftKey("ClientId");
m.MapRightKey("UserId");
m.ToTable("ClientUsers");
});
}
}
This assumes your classes are something like the following:
[Table("Users")]
public class WebUser
{
public virtual int Id { get; set; }
public virtual ICollection<Client> Clients { get; set; }
// more properties...
}
public class Client
{
public int Id { get; set; }
public ICollection<WebUser> WebUsers { get; set; }
// more properties
}
Finally, here's an integration test (NUnit) demonstrating the functionality working. You may need to drop your database before running it as Code First should want to update/migrate/recreate it.
[TestFixture]
public class Test
{
[Test]
public void UseDB()
{
var db = new DatabaseContext();
db.Users.Add(new WebUser { Clients = new List<Client> { new Client() } });
db.SaveChanges();
var webUser = db.Users.First();
var client = webUser.Clients.FirstOrDefault();
Assert.NotNull(client);
}
}
Edit: Link to relevant documentation for the Fluent API
Rowan's answer (adding here for reference):
Here is the information on how to configure a many-to-many table (including specifying the table name). The code you are after is something like:
modelBuilder.Entity<WebUser>()
.HasMany(u => u.Clients)
.WithMany(c => c.WebUsers)
.Map(m => m.ToTable("ClientUsers");
~Rowan
I am in the process of creating a domain model and would like to have a "BaseEntity" class with an "Id" property (and some other audit tracking stuff). The Id property is the primary key and each Entity in my Domain Model will inherit from the BaseEntity class. Pretty straightforward stuff.....
public class BaseEntity
{
[Key]
public int Id { get; set; }
public DateTime LastUpdate { get; set; }
public string LastUpdateBy { get; set; }
}
public class Location : BaseEntity
{
[Required]
public string Name { get; set; }
public string Description { get; set; }
}
Using the example above, I would like to map the "Id" field to a "LocationId" column. I understand that I can use the modelBuilder to do this for each entity explicitly by doing something like this:
modelBuilder.Entity<Location>().Property(s => s.Id).HasColumnName("LocationId");
But I would like to do this for every Entity in my domain model and it would be ugly.
I tried the following bit of reflection but did not have any luck. For whatever reason, the compiler "cannot resolve symbol type":
foreach (var type in GetTypesInNamespace(Assembly.Load("Domain.Model"),"Domain.Model"))
{
modelBuilder.Entity<type>().Property(x=>x.Id).....
}
Is there a way to define a convention to override the default PrimaryKey convention to map my "Id" property to a "ClassNameId" property in the database? I am using Entity Framework 6.
You should take a look at Custom Code First Conventions. You need EF6 for it to work, but it looks like you're already using it.
Just to give you an overview, take a look at the following convention I've used to convert PascalCase names to underscore names. It includes a convention for id properties... It also includes an optional table name prefix.
public class UnderscoreNamingConvention : IConfigurationConvention<PropertyInfo, PrimitivePropertyConfiguration>,
IConfigurationConvention<Type, ModelConfiguration>
{
public UnderscoreNamingConvention()
{
IdFieldName = "Id";
}
public string TableNamePrefix { get; set; }
public string IdFieldName { get; set; }
public void Apply(PropertyInfo propertyInfo, Func<PrimitivePropertyConfiguration> configuration)
{
var columnName = propertyInfo.Name;
if (propertyInfo.Name == IdFieldName)
columnName = propertyInfo.ReflectedType.Name + IdFieldName;
configuration().ColumnName = ToUnderscore(columnName);
}
public void Apply(Type type, Func<ModelConfiguration> configuration)
{
var entityTypeConfiguration = configuration().Entity(type);
if (entityTypeConfiguration.IsTableNameConfigured) return;
var tableName = ToUnderscore(type.Name);
if (!string.IsNullOrEmpty(TableNamePrefix))
{
tableName = string.Format("{0}_{1}", TableNamePrefix, tableName);
}
entityTypeConfiguration.ToTable(tableName);
}
public static string ToUnderscore(string value)
{
return Regex.Replace(value, "(\\B[A-Z])", "_$1").ToLowerInvariant();
}
}
You use it like this
modelBuilder.Conventions.Add(new UnderscoreNamingConvention { TableNamePrefix = "app" });
EDIT: In your case, the Apply method should be something like this:
public void Apply(PropertyInfo propertyInfo, Func<PrimitivePropertyConfiguration> configuration)
{
if (propertyInfo.Name == "Id")
{
configuration().ColumnName = propertyInfo.ReflectedType.Name + "Id";
}
}
Try this out in your DbContext class;
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Properties<int>()
.Where(p => p.Name.Equals("Id"))
.Configure(c => c.HasColumnName(c.ClrPropertyInfo.ReflectedType.Name + "Id"));
}
int is the CLR Type of my Primary Key fields. I want to refer to all keys in code as Id but DBA's require keys to be Id with Table entity name prefix. Above gives me exactly what I want in my created database.
Entity Framework 6.x is required.
In Entity Framework 6 Code First:
modelBuilder.Entity<roles>().Property(b => b.id).HasColumnName("role_id");
and update-database...
Change in model
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long id { get; set; }
to:
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long role_id { get; set; }
Then remove this:
//modelBuilder.Entity<roles>().Property(b => b.id).HasColumnName("role_id");
A start to the Dynamic approach if NOT using custom conventions
modelBuilder.Entity<Location>().Property(s => s.Id).HasColumnName("LocationId");
You can do this using reflection on the context. Pseudo Code as explanation:
Reflect Context to get a list of POCO names
For each POCO in a dbcontext.
Map Property Id -> string PocoName+Id
Here are the extensions I use for this type of solution.
// DBSet Types is the Generic Types POCO name used for a DBSet
public static List<string> GetModelTypes(this DbContext context) {
var propList = context.GetType().GetProperties();
return GetDbSetTypes(propList);
}
// DBSet Types POCO types as IEnumerable List
public static IEnumerable<Type> GetDbSetPropertyList<T>() where T : DbContext {
return typeof (T).GetProperties().Where(p => p.PropertyType.GetTypeInfo()
.Name.StartsWith("DbSet"))
.Select(propertyInfo => propertyInfo.PropertyType.GetGenericArguments()[0]).ToList();
}
private static List<string> GetDbSetTypes(IEnumerable<PropertyInfo> propList) {
var modelTypeNames = propList.Where(p => p.PropertyType.GetTypeInfo().Name.StartsWith("DbSet"))
.Select(p => p.PropertyType.GenericTypeArguments[0].Name)
.ToList();
return modelTypeNames;
}
private static List<string> GetDbSetNames(IEnumerable<PropertyInfo> propList) {
var modelNames = propList.Where(p => p.PropertyType.GetTypeInfo().Name.StartsWith("DbSet"))
.Select(p => p.Name)
.ToList();
return modelNames;
}
However, you will still need to employee dynamic lambda to finish.
Continue that topic here: Dynamic lambda example with EF scenario
EDIT:
Add link to another question that address the common BAse Config class approach
Abstract domain model base class when using EntityTypeConfiguration<T>
Piggybacking on #Monty0018 's answer but this just need to be updated a little if, like me, you're using Entity Framework 7 and/or SQLite.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
try
{
_builder = modelBuilder;
var typeName = typeof(T).Name;
_builder
.Entity(typeof(T))
.Property<int>("Id")
.ForSqliteHasColumnName(typeName + "Id");
}
catch (Exception e)
{
throw e;
}
}
I've got the following requirement that works well in the OO space but I can't seem to get it to map back to the DB using ADO EF code first.
I have numrous products each will have different aspects (attributes but not in the sense of code attributes). For instance ring would have aspects such as mineral type = gold etc whilst a diamond would have an aspec of clarity = VVSI1.
As you can see the products very greatly in thier composition and I want a dynamic way of growing my system.
As such I've created a product class:
public class Product
{
public int id { get; set; }
public string Name { get; set; }
private List<ProductAspect> aspects = new List<ProductAspect>();
public List<ProductAspect> Aspects { get { return aspects; } set { aspects = value; } }
}
It has a list of ProductAspect which is the base class for all aspects moving forward:
public class ProductAspect
{
public int id { get; set; }
public string AspectName { get; set; }
}
I then inherit from the ProductAspect using a generic which alows me to be specific (strongly typed) about my Aspect Value:
public abstract class ProductAspect<T> : ProductAspect
{
public T AspectValue { get; set; }
}
I then create some Aspects that will allow me to decorate my product:
public class StringAspect : ProductAspect<string> { };
public class DecimalAspect : ProductAspect<decimal> { };
public class ImageAspect : ProductAspect<byte[]> { };
I then give the DbContext a try and have tried both TPH and TPC inheritance mappings.
Neither seem to work. The DB model that get's generated doesn't create a foriegn key to the StringAspect or DecimalAspect tables from the Aspect Table.
public class IxamDataContext : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<ProductAspect> Aspects { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
AspectMapping(modelBuilder);
}
private void AspectMapping(DbModelBuilder mb)
{
//TPH
//mb.Entity<ProductAspect>()
// .Map<StringAspect>(m => m.Requires("type").HasValue("sa"))
// .Map<DecimalAspect>(m => m.Requires("type").HasValue("da"));
//TPC
//mb.Entity<StringAspect>().ToTable("StringAspect");
//mb.Entity<DecimalAspect>().ToTable("DecimalAspect");
}
}
Resulting in the following exception for this Seeding code:
Product p = new Product();
p.Name = "Diamond";
p.Aspects.Add(new StringAspect() { AspectName = "History", AspectValue = "Old and long" });
p.Aspects.Add(new DecimalAspect() { AspectName = "Weight", AspectValue= 96.5M });
context.Products.Add(p);
context.SaveChanges();
Excpetion:
EntityType 'StringAspect' does not
exist in the EntitySet
'IxamDataContext.Aspects'. Parameter
name: entity
Any ideas from the EF code first pros out there?
Entity framework doesn't support intermediate non mapped types in inheritance hierarchy. It means that you can't have this inheritance: A (mapped) -> B (not mapped) -> C (mapped). EF also doesn't support mapping generic types. It means that you must remove your generic intermediate class from the hierarchy and move AspectValue to derived types with correct type.
Maybe it's to late, but I would offer you using ComplexType attribute it will allows you to extend your types as you wish.