I thought this was going to be easy... I have a situation where I have a table Module, which can contain "base" modules, and "compound" modules (that are made up from 1-n base modules).
So I have these two tables in SQL Server 2014:
CREATE TABLE Module
(
ModuleId INT NOT NULL IDENTITY(1,1)
CONSTRAINT PK_Module PRIMARY KEY CLUSTERED,
ModuleName VARCHAR(100)
)
CREATE TABLE CompoundModule
(
CompoundModuleId INT NOT NULL
CONSTRAINT FK_CompoundModule_MainModule
FOREIGN KEY REFERENCES dbo.Module(ModuleId),
BaseModuleId INT NOT NULL
CONSTRAINT FK_CompoundModule_BaseModules
FOREIGN KEY REFERENCES dbo.Module(ModuleId),
CONSTRAINT PK_CompoundModule
PRIMARY KEY CLUSTERED(CompoundModuleId, BaseModuleId)
)
and I filled in a few base modules:
INSERT INTO dbo.Module (ModuleName)
VALUES ('Base Module #1'), ('Base Module #2'), ('Base Module #3')
Now I created an EF 6 "code-first, reverse-engineer from database" model and get this Module class:
[Table("Module")]
public partial class Module
{
public Module()
{
Module1 = new HashSet<Module>();
Module2 = new HashSet<Module>();
}
public int ModuleId { get; set; }
public string ModuleName { get; set; }
public virtual ICollection<Module> Module1 { get; set; }
public virtual ICollection<Module> Module2 { get; set; }
}
and this context class:
public partial class ModuleCtx : DbContext
{
public ModuleCtx() : base("name=ModuleCtx")
{ }
public virtual DbSet<Module> Module { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Module>()
.Property(e => e.ModuleName)
.IsUnicode(false);
modelBuilder.Entity<Module>()
.HasMany(e => e.Module1)
.WithMany(e => e.Module2)
.Map(m => m.ToTable("CompoundModule").MapLeftKey("BaseModuleId").MapRightKey("CompoundModuleId"));
}
}
When I'm now trying to create a new compound module with this code, it turns out things aren't quite as easy as I thought.....
using (ModuleCtx ctx = new ModuleCtx())
{
Module newCompound = new Module();
Module baseModule1 = ctx.Module.FirstOrDefault(m => m.ModuleId == 1);
Module baseModule3 = ctx.Module.FirstOrDefault(m => m.ModuleId == 3);
newCompound.BaseModules.Add(baseModule1);
newCompound.BaseModules.Add(baseModule3);
ctx.Module.Add(newCompound);
ctx.SaveChanges();
}
This code causes an error (on the line trying to fetch the base module #1):
System.Data.Entity.Core.EntityCommandExecutionException was unhandled
HResult=-2146232004
Message=An error occurred while executing the command definition. See the inner exception for details.
Source=EntityFramework
InnerException: System.Data.SqlClient.SqlException
HResult=-2146232060
Message=Invalid column name 'Module_ModuleId'.
What am I missing here?? And why isn't the EF6 reverse-engineering code smart enough to create a model that works in this case??
I've been using EF4 with database-first approach so far, so all this fluent code-first configuration is still a bit of a mystery (and problem) to me...... does anyone see my (most likely very) obvious rookie mistake??
PS: this is the code that the "Code-first from existing database" reverse-engineering produces - not my own. So why does the reverse engineering output code that doesn't work in the end??
Try my generator EntityFramework Reverse POCO Generator and see if that does a better job for you.
It generated the following code (interesting stuff at the bottom):
public interface IMyDbContext : System.IDisposable
{
System.Data.Entity.DbSet<Module> Modules { get; set; } // Module
int SaveChanges();
System.Threading.Tasks.Task<int> SaveChangesAsync();
System.Threading.Tasks.Task<int> SaveChangesAsync(System.Threading.CancellationToken cancellationToken);
}
public class MyDbContext : System.Data.Entity.DbContext, IMyDbContext
{
public System.Data.Entity.DbSet<Module> Modules { get; set; } // Module
static MyDbContext()
{
System.Data.Entity.Database.SetInitializer<MyDbContext>(null);
}
public MyDbContext()
: base("Name=MyDbContext")
{
}
public MyDbContext(string connectionString)
: base(connectionString)
{
}
public MyDbContext(string connectionString, System.Data.Entity.Infrastructure.DbCompiledModel model)
: base(connectionString, model)
{
}
public MyDbContext(System.Data.Common.DbConnection existingConnection, bool contextOwnsConnection)
: base(existingConnection, contextOwnsConnection)
{
}
public MyDbContext(System.Data.Common.DbConnection existingConnection, System.Data.Entity.Infrastructure.DbCompiledModel model, bool contextOwnsConnection)
: base(existingConnection, model, contextOwnsConnection)
{
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
}
protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Configurations.Add(new ModuleConfiguration());
}
public static System.Data.Entity.DbModelBuilder CreateModel(System.Data.Entity.DbModelBuilder modelBuilder, string schema)
{
modelBuilder.Configurations.Add(new ModuleConfiguration(schema));
return modelBuilder;
}
}
public class Module
{
public int ModuleId { get; set; } // ModuleId (Primary key)
public string ModuleName { get; set; } // ModuleName (length: 100)
// Reverse navigation
public virtual System.Collections.Generic.ICollection<Module> BaseModule { get; set; } // Many to many mapping
public virtual System.Collections.Generic.ICollection<Module> CompoundModule { get; set; } // Many to many mapping
public Module()
{
BaseModule = new System.Collections.Generic.List<Module>();
CompoundModule = new System.Collections.Generic.List<Module>();
}
}
// Module
public class ModuleConfiguration : System.Data.Entity.ModelConfiguration.EntityTypeConfiguration<Module>
{
public ModuleConfiguration()
: this("dbo")
{
}
public ModuleConfiguration(string schema)
{
ToTable("Module", schema);
HasKey(x => x.ModuleId);
Property(x => x.ModuleId).HasColumnName(#"ModuleId").IsRequired().HasColumnType("int").HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity);
Property(x => x.ModuleName).HasColumnName(#"ModuleName").IsOptional().IsUnicode(false).HasColumnType("varchar").HasMaxLength(100);
HasMany(t => t.CompoundModule).WithMany(t => t.BaseModule).Map(m =>
{
m.ToTable("CompoundModule", "dbo");
m.MapLeftKey("BaseModuleId");
m.MapRightKey("CompoundModuleId");
});
}
}
Related
I have a very simple example I am trying to set up with the following schema...
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public string SomethingVeryBig { get; set; }
public List<Bar> Bars { get; set; }
}
public class Bar
{
public int Id { get; set; }
public int FooId { get; set; }
public string Name { get; set; }
}
What I am trying to test is using Breeze in a way disconnected from my data repository, so I am hand coding from the Fluent API my DBContext. Context code below, "FoosDb" is just an sdf file deployed with the project for the Breeze Metadata and is not a real database we are saving data into.
public class FoosDbContext : DbContext
{
public FoosDbContext() : base(nameOrConnectionString: "FoosDb")
{
Database.SetInitializer<FoosDbContext>(null);
}
public DbSet<Foo> Foos { get; set; }
public DbSet<Bar> Bars { get; set; }
public DbSet<Link> Links { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Foo>().HasKey(f => f.Id);
modelBuilder.Entity<Bar>().HasKey(b => b.Id);
modelBuilder.Entity<Foo>().HasMany(f => f.Bars).WithRequired().HasForeignKey(b => b.FooId);
}
}
public class FoosContextProvider : EFContextProvider<FoosDbContext>
{
public FoosContextProvider() : base() { }
protected override List<KeyMapping> SaveChangesCore(Dictionary<Type, List<EntityInfo>> saveMap)
{
return new List<KeyMapping>();
}
protected override bool BeforeSaveEntity(EntityInfo entityInfo)
{
return true;
}
protected override Dictionary<Type, List<EntityInfo>> BeforeSaveEntities(Dictionary<Type, List<EntityInfo>> saveMap)
{
// return a map of those entities we want saved.
return saveMap;
}
}
Everything works great and I am testing all CRUD operations via a project from the Hot Towel Template, but when I query Foos from my controller the json data looks perfect, but when it gets transferred to Breeze/Knockout Observables the data in each "Foo.Bars" list is wrong. It is taking Bar.Id = 1 and always putting that on Foo.Id = 1, Bar.Id = 2 and putting that on Foo.Id = 2, and so on. Even though in my example Bar.Id = 2 should be on Foo.Id = 1.
I find thousands examples for codefirst relations, but i can't do work my sample !
Many errors like this:
The ForeignKeyAttribute on property 'LanguageID' on type 'BL.Objects.User' is not valid. The navigation property 'Language' was not found on the dependent type 'BL.Objects.User'. The Name value should be a valid navigation property name.
and same same same...
I really want to load language association with user. (en, ru, es)
public abstract class BaseUser : FinanceBase<int>, IUser
{
[ForeignKey("Language")]
public int LanguageID { get; set; }
[ForeignKey("LanguageID")]
public virtual Language Language { get; private set; }
}
public class User : BaseUser
{
public override void GenerateID()
{
...
}
}
public abstract class BaseLanguage : FinanceBase<int>, ILanguage
{
#region Implementation of ILanguage
public string Code { get; set; }
public string Fullname { get; set; }
public string ImagePath { get; set; }
#endregion
}
public class Language : BaseLanguage
{
public override void GenerateID()
{
}
}
public class FinanceDatabaseContext : DbContext
{
public FinanceDatabaseContext()
{
Database.SetInitializer(new FinanceContextInitializer());
}
public DbSet<User> Users { get; set; }
public DbSet<Language> Languages { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<User>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Users");
}).HasKey(x => x.ID).HasRequired(x => x.Language).WithMany().HasForeignKey(x => x.LanguageID);
modelBuilder.Entity<Language>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Languages");
}).HasKey(x => x.ID);
base.OnModelCreating(modelBuilder);
}
}
public class FinanceContextInitializer : DropCreateDatabaseIfModelChanges<FinanceDatabaseContext>
{
protected override void Seed(FinanceDatabaseContext context)
{
context.Database.ExecuteSqlCommand("ALTER TABLE Users ADD CONSTRAINT uc_Language UNIQUE(LanguageID)");
}
}
Thanks !
You don't need to use foreign key for LanguageID
public int LanguageID { get; set; }
[ForeignKey("LanguageID ")]
public virtual Language Language { get; private set; }
Add a virtual navigation field to your User table for language if it isnt already there.
Ie you have LanguageId and Language in the POCO class User.
The alternative is a navigation field in Language Class back to User,
ie Public virtual List Users
But lets stick to Users having BOTH virtual navigation property and Foreign Key Id field.
... the rest of User.....
public int LanguageId
// nav relationship
public virtual Language Language { set; get; }
// Now the FK declaration as you described should work in fluent API...
// Has required NAVIGATION property, its 1 to many and the I have a field for this FK value called X
modelBuilder.Entity<User>().Map(m =>
{
m.MapInheritedProperties();
m.ToTable("Users");
}).HasKey(x => x.ID).HasRequired(x => x.Language).WithMany().HasForeignKey(x => x.LanguageID);
Solved. My Navigation property Language has private setter
[ForeignKey("LanguageID")]
public virtual Language Language { get; private set; }
I using "Entity Framework DbContext" at the moment I have got exception towars.dbo was not found. This is very strange because in my website I all the time ask about towar.dbo but no towars.dbo Do you know where is a problem?
- InnerException {"Invalid object name 'dbo.Towars'."} System.Exception {System.Data.SqlClient.SqlException}
My all things about Towar (of course different place in my program):
public class ProductController : Controller
{
//
// GET: /Product/
public ITowarRepository repository;
public ProductController(ITowarRepository productRepository)
{
repository = productRepository;
}
public ViewResult List()
{
return View(repository.Towar);
}
}
public interface ITowarRepository
{
IQueryable<Towar> Towar { get; }
}
public DbSet<Towar> Towar { get; set; }
public class EFTowarRepository : ITowarRepository
{
public EFDbContext context = new EFDbContext();
public IQueryable<Towar> Towar
{
get { return context.Towar; }
}
}
public class Towar
{
[Key]
public int Id_tow { get; set; }
public string Nazwa { get; set; }
public string Opis { get; set; }
public decimal Cena { get; set; }
public int Id_kat { get; set; }
}
Add the following line to your context:
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
You can tell EF to map to the table Towar by overriding the OnModelCreating method in your DBContext class with fluent API like this:
public class EFDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Towar>().ToTable("Towar");
}
}
Now EF will look for Towar table instead of Towars. If you do not have these tables created, there is some other problem you are having.
EF Code First automatically pluralizes the table names. Use a [Table] attribute to explicitly map the entity to a table name:
[Table("Towary")]
public class Towary
{
// Whatever properties
}
It looks like there's a way to disable pluralization gobally too, see Entity Framework Code First naming conventions - back to plural table names?.
using System.Data.Entity.ModelConfiguration.Conventions;
namespace MVCDemo.Models
{
public class EmployeeContext : DbContext
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
For the sake of completeness #forty-two
I have a problem with Automapper when I try use custom resolver which uses dependency injection.
I have the following model:
public class User : Entity
{
public virtual string Name { get; set; }
public virtual Country Country { get; set; }
}
public class Country : Entity
{
public virtual string Name { get; set; }
}
and the following view model:
public class RegistrationViewModel
{
[Required]
public string Name { get; set; }
public int CountryId { get; set; }
public IEnumerable<Country> Countries { get; set; }
}
in order to map I use the following code:
Mapper.Map(registrationViewModel, user);
earlier I register the following:
Mapper.Reset();
container = new WindsorContainer();
container.AddFacility<FactorySupportFacility>();
container.Register(Component.For<ISession>().
UsingFactoryMethod(() => NHibernateSessionFactory.RetrieveSession()).
LifeStyle.Is(LifestyleType.Transient));
container.Register(Component.For(typeof(LoadingEntityResolver<>)).ImplementedBy(typeof(LoadingEntityResolver<>)).LifeStyle.Transient);
Mapper.Initialize(x =>
{
x.AddProfile<BasicProfile>();
x.ConstructServicesUsing(container.Resolve);
});
My BasicProfile is the following:
public class BasicProfile : Profile
{
public const string VIEW_MODEL = "MyBasicProfile";
public override string ProfileName
{
get { return VIEW_MODEL; }
}
protected override void Configure()
{
CreateMaps();
}
private void CreateMaps()
{
CreateMap<RegistrationViewModel, User>()
.ForMember(dest => dest.Country, _ => _.ResolveUsing<LoadingEntityResolver<Country>>().FromMember(src => src.CountryId))
);
}
}
The custom resolver is done in the following way:
public class LoadingEntityResolver<TEntity> : ValueResolver<int, TEntity>
where TEntity: Entity
{
private readonly ISession _session;
public LoadingEntityResolver(ISession session)
{
_session = session;
}
protected override TEntity ResolveCore(int source)
{
return _session.Load<TEntity>(source);
}
}
When the mapping code is being run I get the following exception:
AutoMapper.AutoMapperMappingException : Trying to map ViewModels.RegistrationViewModel to Models.User.
Using mapping configuration for ViewModels.RegistrationViewModel to Models.User
Exception of type 'AutoMapper.AutoMapperMappingException' was thrown.
----> AutoMapper.AutoMapperMappingException : Trying to map ViewModels.RegistrationViewModel to LModels.Country.
Using mapping configuration for ViewModels.RegistrationViewModel to Models.User
Destination property: Country
Exception of type 'AutoMapper.AutoMapperMappingException' was thrown.
----> System.ArgumentException : Type 'Mapping.LoadingEntityResolver`1[Models.Country]' does not have a default constructor
I have no idea what may be wrong. It is probably something with constructing the resolver. When I try the following there is no problem:
var resolver = container.Resolve<LoadingEntityResolver<Country>>();
Assert.IsInstanceOf<LoadingEntityResolver<Country>>(resolver);
I would be greatfull for any help.
Best regards
Lukasz
You have some pretty hefty DI stuff going on here :-) I would avoid having AutoMapper resolve entities from a database or whatever. Makes the code difficult to understand and following objects lifetime could become a nightmare.
Anyway, to fix your problem simply swap the order from (wrong):
Mapper.Initialize(x =>
{
x.AddProfile<BasicProfile>();
x.ConstructServicesUsing(container.Resolve);
});
to (correct):
Mapper.Initialize(x =>
{
x.ConstructServicesUsing(container.Resolve);
x.AddProfile<BasicProfile>();
});
I have an entity named Tour which can have many Agents. I am able to add agents, but I cannot remove them.
// _repo is injected....
var tour = _repo.GetById(tourId);
tour.AddAgent(new Agent(tour.TourId));
When I attempt to call the Tour.RemoveAgent() method nothing is actually removed. I set a breakpoint inside the Tour.RemoveAgent() method I see that the _agents property has a count of 0.
tour.RemoveAgent(agentId); // This doesn't work because _agents is empty
Do I have to do something special for EF to populate the _agents property when I retrieve the Tour from my repository?
UPDATE: PROBLEM SOLVED (thanks to Paul's answer)
I decided to just create a Repository unique to each aggregate, that way it is easy to define exactly what needs to be included using the Include() function. This is an example where I inherit from the GenericRepository<T> class (which is also included at the bottom of this question).
public class TourRepository : GenericRepository<Tour>
{
public TourRepository(IDatabaseFactory databaseFactory) : base (databaseFactory)
{
}
public override Tour GetById(Guid id)
{
return dataContext.Tours
.Include(x => x.Agents)
.Single(x => x.TourId == id);
}
}
Tour Class
public partial class Tour
{
public Guid TourId { get; private set; }
protected virtual List<Agent> _agents { get; set; }
public Tour()
{
TourId = Guid.NewGuid();
_agents = new List<Agent>();
}
public void AddAgent(Agent agent)
{
_agents.Add(agent);
}
public void RemoveAgent(Guid agentId)
{
_agents.RemoveAll(a => a.AgentId == agentId);
}
}
Agent Class
public partial class Agent
{
public Guid AgentId { get; private set; }
public Guid TourId { get; private set; }
public Tour Tour { get; private set; }
public Agent(Guid tourId)
{
TourId = tourId;
AgentId = Guid.NewGuid();
}
}
OnModelCreating
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// AGENTS ============================
modelBuilder.Entity<Agent>()
.HasKey(x => x.AgentId)
.Property(p => p.AgentId);
modelBuilder.Entity<Agent>()
.HasRequired(p => p.Tour)
.WithMany(t => t.Agents);
// TOURS =============================
modelBuilder.Entity<Tour>()
.HasKey(x => x.TourId)
.Property(x => x.TourId);
}
Repository Class
public class GenericRepository<T> : IRepository<T> where T : class {
private MyContext dataContext;
private readonly IDbSet<T> dbset;
public GenericRepository(IDatabaseFactory databaseFactory)
{
DatabaseFactory = databaseFactory;
dbset = DataContext.Set<T>();
}
protected IDatabaseFactory DatabaseFactory
{
get;
private set;
}
protected MyContext DataContext
{
get { return dataContext ?? (dataContext = DatabaseFactory.Get()); }
}
// ... stuff removed for brevity ...
public T GetById(Guid id)
{
return dbset.Find(id);
}
}
Try making protected virtual List _agents { get; set; } public
public virtual List<Agent> _agents { get; set; }
You can also eager load by doing something like this:
_databaseContext.Tours.Include(x => x.Agents).Single(x => x.TourId == tourId)
you can read more here: http://blogs.msdn.com/b/adonet/archive/2011/01/31/using-dbcontext-in-ef-feature-ctp5-part-6-loading-related-entities.aspx