NotMapped & Explicit Loading Or LazyLoading EF Core - lazy-loading

I'm working on a EF Core API where few properties are to be Explicitly / Lazy loaded. But it is throwing me couple of exceptions when i try,
Model:
public int NationalityId { get; set; }
[NotMapped]
public Country Nationality { get; set; }
Repo:
RepositoryContext.Entry(user).Reference(x => x.Nationality).Load();
this throws me "Property Nationality cannot be found on the entity User"
Also tried virtual instead of [NotMapped] and this,
modelBuilder.Entity<User>().Ignore(x => x.Nationality);
when i remove the "[NotMapped]" attribute and try, i get the below error,
"Column name NationalityId1 cannot be found" ==> SQLException
The same happens for Lazy loading too - I use ILazyLoader and i tried like,
Model:
[NotMapped]
public Country Nationality
{
get => LazyLoader?.Load(this, ref _nationality);
set => _nationality = value;
}
Can someone help me to fix the issues and use both the data loading techniques ?
Note: I'm using EFCore 2.2.4

Instead of ignore/NotMap the related entity you should teach EF how this entities are related. As you are using DataAnottation Attributes. You can use ForeignKeyAttribute
[ForeignKey("Nationality")]
public int NationalityId { get; set; }
public Country Nationality { get; set; }

Related

Lazy loading in EFCore loads all the data even if not accessed

I have been trying to use lazy loading in ASP.NET core 6, I have followed the documentation to do so. However, the behavior of the lazy loading is not the same as described in the docs
Lazy loading means that the related data is transparently loaded from the database when the navigation property is accessed. Here
builder.Services.AddDbContext<AppDbContext>(
options => options.UseLazyLoadingProxies()
.UseSqlServer(builder.Configuration.GetConnectionString("Default")));
Currently I have the following entities
public class Book
{
public long Id { get; set; }
public string Name { get; set; }
public long AuthorId { get; set; }
public virtual Author Author { get; set; }
}
public class Author
{
public long Id { get; set; }
public string Name { get; set; }
}
And I have this endpoint
[HttpGet]
public IEnumerable<Book> GetBooks()
{
var list = _appDbContext.Books.ToList();
return list;
}
And the resulted SQL quires are
SELECT [b].[Id], [b].[AuthorId], [b].[Name]
FROM [Books] AS [b]
SELECT [a].[Id], [a].[Name]
FROM [Author] AS [a]
WHERE [a].[Id] = #__p_0
So in the endpoint I am not accessing the property Author inside Book entity, then why ef core is loading data that I dont need it kink of like eager loading but with two queries.
After investigation I realized that AutoMapper was accessing the Author so that is why its always retrieving the Author data.
So, lazyloading in EFCore is not convenient currently.

Automapper not mapping between two objects (which are virtually the same for all intents and purposes)

Yes, this is ANOTHER "Automapper not mapping" question. Either something broke or I'm going the stupid way about it. I'm building a webapp with ASP.NET Core 2.1 using AutoMapper 3.2.0 (latest stable release at the time) though I have tested with 3.1.0 with no luck either.
Question
Simple object to be mapped to another. For the sake of testing and trials, these are now EXACTLY the same, yet still automapper gives:
AutoMapperMappingException: Missing type map configuration or unsupported mapping.
Mapping types:
NotificationModel -> NotificationViewModel
ProjectName.Models.Dashboard.NotificationModel -> ProjectName.Models.Dashboard.NotificationViewModel
The strange thing is, I have previously mapped this model set 7 ways to sunday in the Startup.cs file with the only thing changing is my facial expression. Other maps work as indicated using similar, if not the same code for them.
The Models
NotificationModel.cs
public class NotificationModel
{
public int Id { get; set; }
public string Content { get; set; }
public DateTime CreateTS { get; set; }
public bool FlagRead { get; set; }
public bool FlagSticky { get; set; }
public bool FlagReceipt { get; set; }
public string ReceiptContact { get; set; }
public string UserId { get; set; }
public bool CANCELLED { get; set; }
}
NotificationViewModel.cs
public class NotificationViewModel
{
public int Id { get; set; }
//Reminder, this model has been amended to exactly represent that of the original model for testing purposes.
public string Content { get; set; }
public DateTime CreateTS { get; set; }
public bool FlagRead { get; set; }
public bool FlagSticky { get; set; }
public bool FlagReceipt { get; set; }
public string ReceiptContact { get; set; }
public string UserId { get; set; }
public bool CANCELLED { get; set; }
}
Startup & Automapper Config
Mapper.Initialize(cfg =>
{
// Some other mappings removed for clarity.
cfg.CreateMap<GroupViewModel, GroupModel>().ReverseMap();
//cfg.CreateMap<EntityViewModel, EntityModel>().ReverseMap().ForAllOtherMembers(opt => opt.Ignore());
cfg.CreateMap<NotificationModel, NotificationViewModel>().ForAllMembers(opt => opt.Ignore());
cfg.CreateMap(typeof(NotificationViewModel), typeof(NotificationModel));
//I even left out the .ReverseMap, for testing purposes.
});
Mapper.AssertConfigurationIsValid();
Usage
NotificationViewModel test = _mapper.Map<NotificationViewModel>(item); << Which is where I receive the exception.
Other Attempts
Ok, so I've been through some more articles explaining different things and subsequently tried the following respectively:
cfg.CreateMap(typeof(NotificationModel), typeof(NotificationViewModel));
cfg.CreateMap<NotificationModel, NotificationViewModel>().ReverseMap().ForAllMembers(opt => opt.Ignore());
cfg.CreateMap<NotificationModel, NotificationViewModel>().ForAllOtherMembers(opt => opt.Ignore());
Along with:
NotificationViewModel test = _mapper.Map<NotificationViewModel>(item);
_mapper.Map(item, typeof(NotificationViewModel), typeof(NotificationModel));
NotificationViewModel existingDestinationObject = new NotificationViewModel();
_mapper.Map<NotificationModel, NotificationViewModel>(item, existingDestinationObject);
I've tried amending the .Map()/.Map<> usage several ways, none of which seemed to yield anything but an exception about not having been configured.
So short of manually writing a conversion for this object (which is simple enough for its purpose), I am in dire need of a solution here. If not to use, then atleast to learn from and help others facing the same.
UPDATE
IT WORKS!
Scanning through the project, I noticed that somewhere in previous documentation - I read about creating a type of "config" class that just inherits from an abstract class called Profile. In this class you will also be able to define your maps, yet what is strange is that I am not able to drop this class and simply use the config maps setup in my Startup.cs file. Automapper will refuse to hold any maps that are not defined in this separate class. The below seems to get me what I need, however I still need an explanation as to why Automapper doesn't function as desired without it:
public class AMConfig : Profile
{
public AMConfig()
{
CreateMap<ManageUserModel, IndexViewModel>();
CreateMap<IndexViewModel, ManageUserModel>();
CreateMap<NotificationViewModel, NotificationModel>().ReverseMap();
CreateMap<List<NotificationViewModel>, List<NotificationModel>>().ReverseMap();
CreateMap<TaskViewModel, TaskModel>().ReverseMap();
}
}
Thanks!
Scanning through the project, I noticed that somewhere in previous documentation - I read about creating a type of "config" class that just inherits from an abstract class called Profile. In this class you will also be able to define your maps, yet what is strange is that I am not able to drop this class and simply use the config maps setup in my Startup.cs file. Automapper will refuse to hold any maps that are not defined in this separate class. The below seems to get me what I need, however I still need an explanation as to why Automapper doesn't function as desired without it:
public class AMConfig : Profile
{
public AMConfig()
{
CreateMap<ManageUserModel, IndexViewModel>();
CreateMap<IndexViewModel, ManageUserModel>();
CreateMap<NotificationViewModel, NotificationModel>().ReverseMap();
CreateMap<List<NotificationViewModel>, List<NotificationModel>>().ReverseMap();
CreateMap<TaskViewModel, TaskModel>().ReverseMap();
}
}

EF Core no .Include() method on DBset

I'm currently completely unable to call .Include() and intellisense (in vscode) doesn't seem to think it exists.
Now after a long time searching the web I've found this:
Not finding .Include() method in my EF implementing Generic repository
which seems to suggest that .Include exists only in System.Data.Entities, which is only available for EF 5 and 6.
So how do i eager load my list property for an entity in EF core?
heres my context
public class Database : DbContext
{
//Set new datasources like this: public DbSet<class> name { get; set; }
public DbSet<Domain.Resource> Resources { get; set; }
public DbSet<Domain.ResourceType> ResourceTypes { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite("Filename=./something.db");
}
}
Heres the data classes:
public class Resource
{
public int ResourceId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int ResourceTypeId { get; set; }
public ResourceType ResourceType { get; set; }
}
public class ResourceType
{
public int ResourceTypeId { get; set; }
public string Name { get; set; }
public List<Resource> Resources { get; set; }
}
Then I do something like:
public List<ResourceType> GetAll()
{
var router = new Database();
var result = router.ResourceTypes.Include(rt => rt.Resources); //It's here there's absolutely no .Include method
return result.ToList();
}
Does .Include not exist in EF Core?
It's a direct consequence of a missing reference in the file where I'm making a call to the method (though i'm not quite sure i understand how...)
Anyways, adding:
using Microsoft.EntityFrameworkCore;
like Tseng and Smit suggested, did the trick. (in the file in which i define the function)
Though why that works i have no idea. I thought .include would automatically be available through the DbSet.
Thanks though! :)
Small, late EDIT: as Christian Johansen pointed out in his comment, the reason it needs the import to see the method signature, is that it is an extension method, which is a topic I strongly encourage any up-and-coming C# developer to learn about as it is immensely useful.
If you end up here, a user of EF 6 or below and happen to miss that OP actually mentioned this like I did, you want to add
using System.Data.Entity;
to your class.
Here is a previous answer that is tracking this issue in EF7. It appears it is now 'included'.

Entity Framework 5 Cyclic Lazy Loading Cause OutOfMemoryException

I have problem with EF 5 and Lazy loading with cyclic references.
The below picture represents my model.
The main problem is between Model and ModelProperties classes, because Model contains IEnumerable navigation property and ModelProperty contains Model navigation property.
So this design cause the situation below
You can access fullsize image http://tinypic.com/r/2vskuxl/6
As you can imagine this cause very big problem, OutOfMemory exception.
Only solution that i could find is disabling lazy loading and using other methods. But lazy loading is very simplifying our work.
I hope there is a configuration or an attribute to help me loading only two levels of relations with lazy loading.
Is there any way to achieve this?
UPDATE:
Regarding the request from Julie Lerman, here is the visual model of EF.
I highlighted the main relation that cause problem.
Also you can access full-size at http://tinypic.com/r/30v15pg/6
UPDATE 2:
Here is the model definitions.
public class Model {
public int ModelID { get; set; }
public int BrandID {
get;
set;
}
public virtual Brand Brand { get; set; }
public string Logo { get; set; }
public string Name { get; set; }
public virtual ICollection<ModelProperty> ModelProperties {
get;
set;
}
}
public class ModelProperty {
public int ModelPropertyID {
get;
set;
}
public virtual int PropertyDefinitionID {
get;
set;
}
public virtual PropertyDefinition PropertyDefinition {
get;
set;
}
public virtual int ModelID {
get;
set;
}
public virtual Model Model {
get;
set;
}
public bool IsContainable {
get;
set;
}
public bool HasFilterDefinition {
get;
set;
}
public virtual ICollection<ModelPropertyValue> ModelPropertyValues {
get;
set;
}
public virtual ICollection<ModelPropertyMatchingFilter> ModelPropertyMatchingFilter {
get;
set;
}
}
Also there is an entity configuration for ModelProperty.
public class ModelPropertyEntityTypeConfiguration : EntityTypeConfiguration<ModelProperty> {
public ModelPropertyEntityTypeConfiguration() {
HasKey(p => p.ModelPropertyID);
HasRequired(p => p.PropertyDefinition).WithMany(s => s.ModelProperties).HasForeignKey(s => s.PropertyDefinitionID).WillCascadeOnDelete(false);
HasRequired(p => p.Model).WithMany(s => s.ModelProperties).HasForeignKey(s => s.ModelID).WillCascadeOnDelete(false);
HasMany(p => p.ModelPropertyValues).WithRequired(s => s.ModelProperty).HasForeignKey(s => s.ModelPropertyID).WillCascadeOnDelete(true);
HasMany(p => p.ModelPropertyMatchingFilter).WithRequired(s => s.ContainerModelProperty).HasForeignKey(s => s.ContainerModelPropertyID).WillCascadeOnDelete(false);
ToTable("dbo.ModelProperties");
}
}
UPDATE 3:
I am not sure but Automapper can cause this also. Because Entity Framework Profile tells thousands of Autommaper methods called while running.
UPDATE 4:
Here is the EFProf stacktrace:
You access bigger version http://tinypic.com/r/21cazv4/6
UPDATE 5
You can see sample project here: https://github.com/bahadirarslan/AutomapperCircularReference
In sample, you can see easily endless loops via Quick watch.
Thanks for the update. You're model looks fine. It definitely knows this is just a 1:* relationship. Ladislav (as usual) is correct. LL doesn't cause the problem...except in ONE place which is during serialization. Is there a chance that you have your code in a service? With regular lazy loading, only the property you explicitly mention will get lazy loaded. But during serialization, the serialization "mentions" every property so it just keeps loading and loading properties throughout the graph AND causes circular dependency issues. With services we have to turn lazy loading off (using context.configuration.lazyloadingenabled=false) before you return the data. So in the service method, you can eager load, or lazy load or explicitly load to get your graph but then disable lazy loading before you return the results.
you should disable lazy loading in order to bypass proxy objects or you should return what you need as a DTO. using DTO is preferred because of hiding details of your domain

Is there a plural issue for models database context y to ies in mvc4 EF

I keep getting error when I try to access a model from an edit or details action.
The model backing the 'InjuriesContext' context has changed since the
database was created. Consider using Code First Migrations to update
the database (http://go.microsoft.com/fwlink/?LinkId=238269).
First I tried adding a migration even though I was sure I hadn't changed anything. Still recieved the same error after an update-database.
Then I removed all the migrations and the database and started a clean database with an inital migration and update. Same error. Nothing was changed.
Model is:
public class InjuriesContext : DbContext
{
public InjuriesContext()
: base("DBCon")
{
}
public DbSet<Patient> Patients { get; set; }
public DbSet<Injury> Injuries { get; set; }
}
public class Injury
{
public int Id { get; set; }
public string Type { get; set; }
public int PatientId { get; set; }
}
Here is controller --
public ActionResult Edit(int id = 0)
{
Injury injury = db.Injuries.Find(id);
if (injury == null)
{
return HttpNotFound();
}
return View(injury);
}
It errors on the injuries.find. I do not have any injuries entered so I expect it to return a 404 like my other controllers but it doesn't like something about this. The only difference between this and my other models is the y to ies for plural. Does Entity Framework not handle this?
There should not be any plural restriction, as you defined everything clearly in your classes anyway.
Have you created the Injuries table?
I belive the table Injury will get created automatically. the variable injury might be a bit close, but I have to test this myself.
Rather try:
public class Injury
{
[Key]
public int Id { get; set; }
[Required]
public string Type { get; set; }
[Required]
public int PatientId { get; set; }
}
private InjuriesContext db = new InjuriesContext();
Injury objInjury = db.Injuries.Find(id);
if (objInjury == null)
{
return HttpNotFound();
}
return View(objInjury);
Hope this helps
It turns out my issue was with multiple contexts. I thought you had to create a separate context for each model class. Apparently Entity Framework needs one context. I went through and created a class for my context and put all my DBsets in that class.
public class ProjContexts : DbContext
{
public ProjContexts()
: base("ProjDBCon")
{
}
public DbSet<Patient> Patients { get; set; }
public DbSet<PreHosp> PreHosps { get; set; }
public DbSet<UserProfile> UserProfiles { get; set; }
public DbSet<Injury> Injuries { get; set; }
}
}
Then I removed all the migrations as per this post and enabled the migrations again did an add migration and update then I got the expected result.
Bottom Line--- Don't have multiple context classes in your project. Not sure if this is possible but after changing the above everything is working as expected. Not sure why it was working when I had two separate contexts and added the third? Maybe because they had foreign keys with one another?