我试图通过使用EF代码首先创建一个SQL数据库。
假设我有以下代码:
public class Account
{
public int Id;
public ICollection<User> Users;
}
public class User
{
public int Id;
public int AccountId;
}
public class AccountContext : DbContext
{
public DbSet<Account> Accounts;
public DbSet<User> Users;
}
(注意,没有任何流利的API命令或数据注释的;我想按照惯例做到这一点。)
当创建数据库时,我得到了用户表中的以下字段:
Id
AccountId
Account_Id
为什么不EF账户拿起一个事实,即“ACCOUNTID”指的是“ID”主键(按照约定)? 我想如果可能的话,以这种手动流利的API / DA避免映射,我想,以避免用户的帐户导航属性。
只有两种方法,我知道如何执行你在找什么做的,无论是数据注解(非常快)或流利的映射。 你不能只是说public int AccountId;
并期望一切工作。
流利的API映射双向
public class Account
{
public int Id { get; set; }
public ICollection<User> Users { get; set; }
}
public class User
{
public int Id { get; set; }
public Account Account { get; set; }
}
public class AccountContext : DbContext
{
public AccountContext()
: base("DefaultConnection")
{
}
public DbSet<Account> Accounts { get; set; }
public DbSet<User> Users { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().HasRequired(u => u.Account)
.WithMany(a => a.Users)
.HasForeignKey(u => u.AccountId);
}
}
数据标注双向
public class Account
{
[Key]
public int Id { get; set; }
public ICollection<User> Users { get; set; }
}
public class User
{
[Key]
public int Id { get; set; }
[ForeignKey("Account"), DatabaseGenerated(DatabaseGeneratedOption.None)]
public int AccountId { get; set; }
public Account Account { get; set; }
}
// and of course you need your context class, but with less code
public class AccountContext : DbContext
{
public AccountContext()
: base("DefaultConnection")
{
}
public DbSet<Account> Accounts { get; set; }
public DbSet<User> Users { get; set; }
}
没有数据注释或流畅API映射双向
public class Account
{
public int Id { get; set; } //as Id in Accounts Table
public ICollection<User> Users { get; set; }
}
public class User
{
public int Id { get; set; } //as Id in Users Table
public Account Account { get; set; } // as Account_Id in Users Table
}
// and of course you need your context class, but with less code
public class AccountContext : DbContext
{
public AccountContext()
: base("DefaultConnection")
{
}
public DbSet<Account> Accounts { get; set; }
public DbSet<User> Users { get; set; }
}
我希望这可以帮助你或任何人怀疑别人
编辑
如果你想避免Bi-Directional
导航然后更改Users
喜欢这个
public class User
{
public int Id { get; set; }
//delete below from User class to avoid Bi-directional navigation
//public Account Account { get; set; }
}
注:没有测试但逻辑是合理的
您可以编写自己的自定义的公约,但它真的会变得复杂(例如如果Account
有两个集合的导航性能User
S'怎么会EF知道哪个集合导航属性是由单一的引用User.AccountId
财产?有无数种可能注意事项/陷阱,它可能无法考虑到他们所有。
我下面的例子将在你所描述的情况下工作,但可能会开始打破,如果你的模型变得更加复杂。 你应该过滤可能的实体类型那些你希望在你的模型(下面的例子检查在应用程序域中所有类型)。 我强烈建议你干脆用流利的API或数据注解在下面,但它确实工作,为您所陈述的需求,是一个自定义的公约,一个有趣的例子。
// I recommend filtering this
var possibleEntityTypes = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany( ass => ass.GetTypes() );
modelBuilder.Properties()
.Where( cp =>
IsValidForeignKeyType( cp.PropertyType ) &&
cp.Name.Length > 2 &&
( cp.Name.EndsWith( "ID" ) || cp.Name.EndsWith( "Id" ) ) &&
!cp.Name.Substring( 0, cp.Name.Length - 2 ).Equals( cp.ReflectedType.Name, StringComparison.OrdinalIgnoreCase ) )
.Configure( cppc =>
{
var sourcePropertyType = cppc.ClrPropertyInfo.PropertyType;
var sourceEntityType = cppc.ClrPropertyInfo.ReflectedType;
var targetEntityName = cppc.ClrPropertyInfo.Name.Substring( 0, cppc.ClrPropertyInfo.Name.Length - 2 );
var icollectionType = typeof( ICollection<> ).MakeGenericType( sourceEntityType );
// possible problem of multiple classes with same name but different namespaces
// for this example I simply select the first but this should be more robust
// e.g. check for ID/ClassNameID property in the class or require same
// namespace as the property's class
var targetEntityType = possibleEntityTypes.FirstOrDefault( t =>
t.Name == targetEntityName &&
// check if the type has a nav collection property of the source type
t.GetProperties().Any( pi =>
pi.PropertyType.IsGenericType &&
icollectionType.IsAssignableFrom( pi.PropertyType ) ) );
if( null != targetEntityType )
{
// find the nav property
var navPropInfos = targetEntityType.GetProperties()
.Where( pi =>
pi.PropertyType.IsGenericType &&
icollectionType.IsAssignableFrom( pi.PropertyType ) &&
pi.PropertyType.GetGenericArguments().First() == sourceEntityType );
if( 1 != navPropInfos.Count() )
{
// more than one possible nav property, no way to tell which to use; abort
return;
}
var navPropInfo = navPropInfos.First();
// get EntityTypeConfiguration for target entity
var etc = modelBuilder.GetType().GetMethod( "Entity" )
.MakeGenericMethod( targetEntityType )
.Invoke( modelBuilder, new object[] { } );
var etcType = etc.GetType();
var tetArg = Expression.Parameter( targetEntityType, "tet" );
// Invoke EntityTypeConfiguration<T>.HasMany( x => x.Col )
// returns ManyNavigationPropertyConfiguration object
var mnpc = etcType.GetMethod( "HasMany" ).MakeGenericMethod( sourceEntityType )
.Invoke( etc, new[] {
Expression.Lambda(
Expression.Convert(
Expression.Property(
tetArg,
navPropInfo ),
icollectionType ),
tetArg ) } );
string withMethodName = ( sourcePropertyType.IsPrimitive || sourcePropertyType == typeof( Guid ) )
? "WithRequired"
: "WithOptional";
// Invoke WithRequired/WithOptional method
// returns DependentNavigationPropertyConfiguration object
var dnpc = mnpc.GetType().GetMethods().Single( mi =>
mi.Name == withMethodName && !mi.GetParameters().Any() )
.Invoke( mnpc, new object[] { } );
var setArg = Expression.Parameter( sourceEntityType, "set" );
// Invoke HasForiegnKey method
var x = dnpc.GetType().GetMethod( "HasForeignKey" ).MakeGenericMethod( sourcePropertyType )
.Invoke( dnpc, new[]{
Expression.Lambda(
Expression.Property(
setArg,
cppc.ClrPropertyInfo ),
setArg ) } );
}
});
Helper方法:
public static bool IsValidForeignKeyType( Type type )
{
var retVal = type.IsPrimitive ||
type == typeof( string ) ||
type == typeof( Guid );
if( !retVal )
{
if( type.IsGenericType && type.GetGenericTypeDefinition() == typeof( Nullable<> ) )
{
var genArgType = type.GetGenericArguments().Single();
retVal = genArgType.IsPrimitive || genArgType == typeof( Guid );
}
}
return retVal;
}
文章来源: EF Code First: Duplicate foreign keys (one from name convention, one from navigation property)