EF 6.1.3 Lazy loading not working - asp.net-mvc-5

I am trying to get a single employee record, but looking at the Diagnostic Tools it is showing Entity Framework performing tons of queries loading the entire database. Lazy loading is enabled and I am using the public and virtual keywords so I don't think that should be the problem. Is there anything I am missing, the navigational properties for the Employee record should not be loading.
Service:
return _employeeRepo.GetEmployee(sid);
Repository:
public Employee GetEmployee(string sid)
{
Employee employee = Context.Employees.SingleOrDefault(e => e.SID == sid);
return employee != null ? employee.ToDomain() : null;
}
Employee Model:
public class Employee
{
...
public virtual ICollection<Address> Addresses { get; set; }
public virtual ICollection<Disability> Disabilities { get; set; }
...
public virtual Bureau Bureau { get; set; }
public virtual Division Division { get; set; }
...
public Domain.Models.Employee ToDomain()
{
return Mapper.Map<Domain.Models.Employee>(this);
}
}
Context:
public class SqlContext : DbContext
{
public SqlContext() : base("SqlContext")
{
Database.SetInitializer<SqlContext>(null);
}
public virtual DbSet<EfModels.Address> Addresses { get; set; }
public virtual DbSet<EfModels.Bureau> Bureaus { get; set; }
public virtual DbSet<EfModels.Disability> Disabilities { get; set; }
public virtual DbSet<EfModels.Division> Divisions { get; set; }
public virtual DbSet<EfModels.Employee> Employees { get; set; }
}

Your Mapping Tool (AutoMapper) is the issue.
When your calling employee.ToDomain(), the navigation properties of your entity are being accessed, causing EF to lazy load the tables.

Related

EntityFramework : Invalid column name *_ID1

I am trying to implement DbContext for couple of tables called 'Employee' and 'Department'
Relationship between Employee and Department is many to one. i.e. department can have many employees.
Below are the EntityFramework classes I designed ( CodeFirst approach )
[Table("Employee")]
public class Employee
{
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Column("Name")]
public string Name { get; set; }
[Column("Department_ID")]
public int Department_ID { get; set; }
public virtual Department Department { get; set; }
}
[Table("Department")]
public class Department
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[Column("Name")]
public string Name { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
}
While adding Employee record I am getting below exception
"Invalid column name 'Department_ID1'."
I am not sure why EF is referring to Department_ID1. Do I need to add configuration in OnModelCreating method of DbContext?
I am using EF version 6.1.1
I've also gotten this problem in my EF one-many deals where the one has a List of the many property and my mapping didn't specify that property. For example take:
public class Notification
{
public long ID { get; set; }
public IList<NotificationRecipient> Recipients { get; set; }
}
then
public class NotificationRecipient
{
public long ID { get; set; }
public long NotificationID { get; set; }
public Notification Notification { get; set; }
}
Then in my mapping, the way that caused the Exception (the incorrect way):
builder.HasOne(x => x.Notification).WithMany()
.HasForeignKey(x => x.NotificationID);
What fixed it (the correct way) was specifying the WithMany property:
builder.HasOne(x => x.Notification).WithMany(x => x.Recipients)
.HasForeignKey(x => x.NotificationID);
Hi After spending some time I could fix this problem by using ForeignKey attribute on public virtual Department Department { get; set; } property of Employee class.
Please see below code.
[Table("Employee")]
public class Employee
{
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Column("Name")]
public string Name { get; set; }
[Column("Department_ID")]
public int Department_ID { get; set; }
[ForeignKey("Department_ID")]
public virtual Department Department { get; set; }
}
This fixed my problem. Are there any other solution to fix this? Using fluent API?
For me, the issue was resolved by removing a (duplicate?) virtual property.
Using the OP's example:
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int Department_ID { get; set; }
public virtual Department Department { get; set; }
}
public class Department
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
}
Turns into:
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public int Department_ID { get; set; }
}
public class Department
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
}
In my case I added a virtual property on top of the auto generated property
I fixed it by adding the NotMapped attribute to my property, or you could configure with fluent api
public partial class Control
{
[NotMapped]
public virtual ICollection<Control> Children { get => this.InverseParent; set => this.InverseParent = value; }
}
I had the same error, my issue was the FK was a long but I had it as an int in the model. EF generated a new column because it didn't match types on the FK so it assumed they weren't the same and went ahead with making another one but putting 1 at the end because there was already one with the proper name. Making sure the types matched resolved the issue for me.
This can be fixed simply by putting [NotMapped] annotation on your virtual properties.
public class Employee
{
[ForeignKey("Department")]
public int Department_ID
[NotMapped]
public virtual Department Department { get; set; }
}
And in you modelBuilder:
modelBuilder.Entity<Employee>(entity =>
{
entity.HasOne(e => e.Department);
});
Just flip this around if you want to call by Department.
We use the [NotMapped] annotation so that EF Core will disregard it when looking at your database.

how do i use AutoMapper in ICollation<> Fields

when i use AutoMapper for mapping my ViewModels and get All News, thrown error for me.
Errors...
The following property on Mosque.Core.ViewModels.CategoryViewModel cannot be mapped:
Categories
Add a custom mapping expression, ignore, add a custom resolver, or modify the destination type Mosque.Core.ViewModels.CategoryViewModel.
please help me, thank you
//Models
public class News
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<Category> Categories { get; set; }
public virtual User User { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<News> News { get; set; }
}
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<News> News { get; set; }
}
//ViewModels
public class NewsViewModel
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<CategoryViewModel> Categories { get; set; }
public virtual UserViewModel User { get; set; }
}
public class CategoryViewModel
{
public int Id { get; set; }
public string Title { get; set; }
public virtual ICollection<NewsViewModel> News { get; set; }
}
public class UserViewModel
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<NewsViewModel> News { get; set; }
}
how do i use for select All News?
--Update1--
I used onion architecture in the project and i installed AutoMapper in the Service layer and i want get all news from repository and fill into ViewModels and pass to the UI.
my code in service layer is...
public List<NewsViewModel> GetAll()
{
Mapper.CreateMap<News, NewsViewModel>()
.ForMember(dest => dest.Categories, src => src.MapFrom(p => p.Categories))
.ForMember(dest => dest.User, src => src.MapFrom(p => p.User));
Mapper.AssertConfigurationIsValid();
var viewModels = new List<NewsViewModel>();
foreach (var item in _newsRepository.GetAll())
{
var viewModel = Mapper.Map<News, NewsViewModel>(item);
viewModels.Add(viewModel);
}
return viewModels;
}
You don't seem to have created maps for Catagory and User.
Add the following maps:
Mapper.CreateMap<User, UserViewModel>();
Mapper.CreateMap<Category, CategoryViewModel>();
By the way, why are you creating the maps inside the GetAll method? You can create the maps once, usually at application startup.

Merge ApplicationDbContext and DataContext into a single context

I am having a problem merging these 2 contexts.
I deleted this line of code from IdentityModels.cs:
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
}
I implemented this inside my DataContext.cs
public class DataContext : DbContext
{
public DataContext()
: base("name=DefaultConnection")
{
}
// Account tables
public DbSet ApplicationUsers { get; set; }
public DbSet Roles { get; set; }
public DbSet Tokens { get; set; }
public DbSet UserClaims { get; set; }
public DbSet UserLogins { get; set; }
public DbSet UserManagements { get; set; }
public DbSet UserRoles { get; set; }
public DbSet UserSecrets { get; set; }
// App Models
public DbSet<Course> Courses { get; set; }
public DbSet<Student> Students { get; set; }
}
Problem:
When I "update-database" it only created the Courses and Students tables.
Question
If I successfully implement this method will I lose all of the nice methods that IdentityDbContext interface offers for example:
var rm = new RoleManager<IdentityRole>(
new RoleStore<IdentityRole>(new ApplicationDbContext()));
return rm.RoleExists(name);
Ok here is the solution that I found posted by Olav Nybo in this topic How can one put application users in the same context as the rest of the objects?.
Go to his sample project on github: https://github.com/onybo/Asp.Net-Identity-sample-app/tree/master/CustomUser/CustomUser
Download the configurations folder from the Models folder and place the folder inside your models folder.
Inside your DataContext file you will put this snippet of code which will call these configuration files to build out your database.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
if (modelBuilder == null)
throw new ArgumentNullException("modelBuilder");
modelBuilder.Configurations.AddFromAssembly(typeof(Generic_Repo_Template.Models.Configurations.ApplicationUserConfiguration).Assembly);
}
Now that your database is created you still do not have access to these tables through the datacontext object. In order to be able to access these tables through your datacontext is to include these lines of code:
public virtual IDbSet<ApplicationUser> Users { get; set; }
public virtual IDbSet<IdentityRole> Roles { get; set; }
public virtual IDbSet<IdentityUserClaim> Claims { get; set; }
So the full DataContext file will look something like this:
public class DataContext : DbContext
{
public DataContext()
: base("name=DefaultConnection")
{
}
// Account tables
public virtual IDbSet<ApplicationUser> Users { get; set; }
public virtual IDbSet<IdentityRole> Roles { get; set; }
public virtual IDbSet<IdentityUserClaim> Claims { get; set; }
// App Models
public DbSet<Course> Courses { get; set; }
public DbSet<Student> Students { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
if (modelBuilder == null)
throw new ArgumentNullException("modelBuilder");
modelBuilder.Configurations.AddFromAssembly(typeof(AspNetRoleBasedSecurity.Models.Configurations.ApplicationUserConfiguration).Assembly);
}
}
You have to use:
DbSet<Model> Name {get;set;}
Instead of
DbSet Model {get;set;}
As the answer to your question. The RoleStore needs a DbContext (IdentityDbContext inherits from DbContext). So you should be able to still use the roleManager and more...

Cascade saving children objects in Orchard CMS

Lets say I have an object called Company that is represented like so:
public class Company
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual Address Address { get; set; }
}
public class Address
{
public virtual int Id { get; set; }
public virtual string Line1 { get; set; }
public virtual string Line2 { get; set; }
public virtual string Line3 { get; set; }
public virtual string Town { get; set; }
public virtual string County { get; set; }
public virtual string Postcode { get; set; }
public virtual double Longitude { get; set; }
public virtual double Latitude { get; set; }
}
When I create a new company, I also want the new address to be created too. At the moment, I can only get this to work by creating the address record and then creating the company record separately:
[HttpPost]
public ActionResult Add(Company company)
{
_addressRepo.Create(company.Address);
_companyRepo.Create(company);
return RedirectToAction("Index");
}
(Where _addressRepo and _companyRepo are of type IRepository<Address> and IRepository<Company> respectively)
How can I get all child objects to cascade save? So that ideally, when I call _companyRepo.Create(company);, Orchard handles all the child records for me?
I tried creating a new attribute so that properties can be marked as Cascade Save Update, but If I put a breakpoint on the Apply method it never appears to get hit.
public class CascadeSaveUpdateAttribute : Attribute {
}
public class CascadeSaveUpdateConvention :
AttributeCollectionConvention<CascadeSaveUpdateAttribute>
{
protected override void Apply(CascadeSaveUpdateAttribute attribute, ICollectionInstance instance)
{
instance.Cascade.SaveUpdate();
}
}
What can I do to get this Cascade Save functionality working?

EF - Lost In Migration

Before the migration I had an 1 to many relationship between tables Work and Factory:
public class Work
{
public int WorkId { get; set; }
public string Name { get; set; }
public virtual List<Factory> MyFactory { get; set; }
}
and:
public class Factory
{
public int FactoryId { get; set; }
public string Name { get; set; }
public virtual Work ParentWork { get; set; }
}
Then I tried to change the relationship to be an 1 to 1:
public class Work
{
public int WorkId { get; set; }
public string Name { get; set; }
[Required]
public virtual Factory MyFactory { get; set; }
}
public class Factory
{
public int FactoryId { get; set; }
public string Name { get; set; }
public virtual Work ParentWork { get; set; }
}
When I tried to create the migration with:
add-migration MyMigration
I received the following error:
The ALTER TABLE statement conflicted with the FOREIGN KEY constraint "FK_dbo.Work_dbo.Factory_WorkId". The conflict occurred in database "EntityFrameworkLab.MyContext", table "dbo.Factory", column 'FactoryId'
Now, I have a few questions:
1) Why couldn't the migration be created, whereas the model is valid?
2) What changes should I implement in the code?
3) If I try to manually revert the changes, the same error message occurs, but this time when I try to do the:
update-database -verbose

Resources