Reputation: 1420
I have multiple classes used in another VS project that are interconnected, e.g.:
namespace MyOtherProject
{
public class A {
public string Name {get;set;}
}
public class B {
public int Value {get;set;}
public A Child {get;set;}
}
}
Now I want to use these classes as POCO for code first in EF. However I don't need all classes in the database but only some of them. So I created a context:
public class MyContext : DbContext
{
public MyContext () : base("MyContext ")
{
}
public DbSet<B> Bs { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
When I try to run the project I get this error:
System.Data.Entity.ModelConfiguration.ModelValidationException: One or more validation errors were detected during model generation: MyProject.A: Name: Each type name in a schema must be unique. Type name 'A' is already defined.
Note that the namespace in the error is not the same as the definition of the class A because my class A is defined in another (shared) project. Instead the namespace is the one of the context.
You could have omitted the DbSet and DbSet statements and it would work the same. The Entity Framework would include them implicitly because the Student entity references the Enrollment entity and the Enrollment entity references the Course entity.
My assumption (not confirmed) is that EF sees that B references A but there is no dataset of type A, so it generates internally a class A and then suddenly finds out there are two such classes.
Anyone know what the exact problem is and what I can do to fix it? I have too many classes that I don't really need, so I don't want to create datasets for each class I have.
Upvotes: 1
Views: 165
Reputation: 13498
First, get all generic type parameters(Type1
, Type2
...) from DbSet<Type1>
. Then you should check for each of this types their internal properties types, i.e. references to another types (as direct link or via ICollection
set), - if this internal types not exist at first tablesTypes set we will ignore them.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//other stuff.....
Func<Type, bool> genericFilter = x => typeof(IEnumerable).IsAssignableFrom(x) && x.GenericTypeArguments.Length == 1;
//All types of your context tables
var tablesTypes = GetType().GetProperties()
.Where(x => genericFilter(x.PropertyType))
.Select(x => x.PropertyType.GenericTypeArguments.First());
var namespaces2ignore = new List<string> { "Namespace2Ignore1", "Namespace2Ignore2" };
var toIgnore = new List<Type>();
foreach (var type in tablesTypes)
//Ignore internal types, which not exist at tablesTypes
toIgnore.AddRange(type.GetProperties()
.Select(x => genericFilter(x.PropertyType) ? x.PropertyType.GenericTypeArguments.First() : x.PropertyType)
.Where(x => !tablesTypes.Contains(x) && namespaces2ignore.Contains(x.Namespace)
/*or as you suggested: && typeof(A).Assembly == x.Assembly*/
).ToList());
modelBuilder.Ignore(toIgnore);
}
If you want to add condition on specific pairs type-property you can do something like that:
foreach (var type in tablesTypes)
foreach(var prop in type.GetProperties()
.Select(x => new { type = genericFilter(x.PropertyType) ? x.PropertyType.GenericTypeArguments.First() : x.PropertyType, prop = x })
.Where(x => !tablesTypes.Contains(x.type) && namespaces2ignore.Contains(x.type.Namespace)
).ToList())
if(!(type == typeof(TestType) && prop.prop.Name == "Test"))
toIgnore.Add(prop.type);
Now won't be excluded property with name "Test" of type TestType
.
Upvotes: 2