Why my EF collection are not lazy? - lazy-loading

I'm using EF 4.1 Code first. I have a model of user and a model of setting.
Each time the repository returns a user the Setting is also loaded. I've marked the Setting as virtual all my access modifiers are public LazyLoadingEnabled and ProxyCreationEnabled are enabled by default.
What am I missing?
public class User : BaseEntity
{
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public virtual ICollection<Setting> Settings { get; set; }
}
public class Setting
{
public int UserID { get; set; }
public int SettingID { get; set; }
public string Value { get; set; }
}
The User might have several setting, so there is a one to many relationship with a foreign key in setting.
The user configuration is
public class UserConfiguration : EntityTypeConfiguration<User>
{
public UserConfiguration()
{
HasKey(u => u.ID);
HasMany(u => u.Settings).WithOptional().HasForeignKey(u => u.UserID);
}
}
and the Setting configuration is:
public class SettingsConfiguration : EntityTypeConfiguration<Setting>
{
public SettingsConfiguration()
{
ToTable("UserSettings");
HasKey(s => new { s.UserID, s.SettingID });
}
}

Lazy loading means the opposite of what you think it means.
With lazy loading (virtual property and defaults)
Settings is not retrieved immediately when querying User
Settings is retrieved when it's accessed for the first time. The DbContext must be open at that time for this to happen; otherwise you'll get an exception
Without lazy loading (non-virtual property and/or explicitly disabled)
Settings is not retrieved immediately when querying User
Settings will never be retrieved automatically; it will return null (which, in my opinion, is a terrible design decision: null is a wrong value and you shouldn't be able to get it)
In both cases, you can load Settings eagerly by using .Include(x => x.Settings), or when needed, by calling context.Entry(user).Collection(x => x.Settings).Load()

Related

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();
}
}

LazyLoad in NHibernate and ASP.NET web api Service

I have the following data model
public class Profile : Entity
{
public virtual string Name { get; set; }
public virtual int Sequence { get; set; }
public virtual string Description { get; set; }
public virtual IList<MapService> MapServices { get; set; }
}
public class MapService : Entity
{
public virtual string Name { get; set; }
public virtual string Url { get; set; }
public virtual int MaximumResolution { get; set; }
}
As you can see , a profile has many MapService(s).
And the relation is many to many.
I am building an ASP.NET Web API rest service that return profile data.
I have two calls, one to return all the profiles, and the second filtered by ID, with the following URL
http://myapp/api/profiles
http://myapp/api/profiles/:id
I am using NHibernate for data access in the API Controller.
the Web API controller looks like this
public class ProfilesController : ApiController
{
public ProfilesController()
{
}
public IEnumerator<Profile> GetAllProfiles()
{
using (Session = .. create nhibernate session )
{
return Session.Query<Profile>().GetEnumerator();
}
}
}
When I return the whole list, I don't want the details of the MapService(s), just the Name and Id
so, I thought I will do lazy loading.
So, I configured the nhibernate mapping using fluent nhibernate as follows
public class ProfileMapping : ClassMap<Profile>
{
public ProfileMapping()
{
Table("PROFILE");
Id(x => x.Id, "OBJECT_ID");
Map(x => x.Name, "PROFILE_NAME");
Map(x => x.Sequence, "SEQUENCE_NO");
Map(x => x.Description, "DESCR");
HasManyToMany<MapService>(x => x.MapServices).LazyLoad().
Table("PROFILE_MAP_SERVICE").ParentKeyColumn("PROFILE_ID").ChildKeyColumn("MAP_SERVICE_ID");
}
}
I thought by doing this, I will return only the profile data without the details of MapService List
But when I call the rest service to return the whole data like this
http://myapp/api/profiles
I get this error
"Message":"An error has occurred.","ExceptionMessage":"The 'ObjectContent`1' type failed to serialize the response body for content type 'application/json
"Message":"An error has occurred.","ExceptionMessage":"Initializing[Domain.Profile#2]-failed to lazily initialize a collection of role: Domain.Profile.MapServices, no session or session was closed","ExceptionType":"NHibernate.LazyInitializationException","StackTrace":"
It seems that the nhibernate is returning the list of profiles without mapservices, and close the session.
But them somehow the web api service is trying to access the list of map service during serialization.
how to tell the web api service to ignore the map service list?
The simple approach here would be to introduce the DTO object:
public class ProfileDto
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual string Sequence { get; set; } // or Guid...
public virtual string Description { get; set; }
... // if more needed
}
and then adjust the API Controller method
public IEnumerable<ProfileDto> GetAllProfiles()
{
using (var session = ...)
{
return session.Query<Profile>()
.Select(entity => new ProfileDto
{
Id = entity.ID,
Name = entity.Name,
Sequence = entity.Sequence,
Description = entity.Description,
})
.ToList();
}
}
The most important here is the call .ToList() which will assure, that all the loads from DB server are done during the session life time (using clause). Automapper maybe could be next step to make it more easy (less code)...

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

How to map and reference entities from other data sources with NHibernate

I'm currently working on and ASP.NET MVC application in which I have a User entity like follows:
public class User
{
public virtual int Id { get; protected set; }
public virtual string Name { get; protected set; }
public virtual string Role { get; protected set; }
public virtual Location Location { get; protected set; }
}
Where location is just as straightforward:
public class Location
{
public virtual string Id { get; protected set; }
public virtual string Building { get; protected set; }
public virtual string City { get; protected set; }
public virtual string Region { get; protected set; }
}
My complication arises because I want to populate the User from Active Directory and not the database. Additionally, several classes persisted to the database reference a user as a property. I've got an ADUserRepository for retrieval, but I don't know how to integrate these Users into my object graph when the rest is managed by NHibernate.
Is there a way for NHibernate to persist just an id for a User without it being a foreign key to a Users table? Can I map it as a component to accomplish this? I've also looked at implementing IUserType to make the translation. That way it would map to a simple field and ADUserRepository could be put in the chain to resolve the stored Id. Or am I trying to hack something that's not really feasible? This is my first time around with NHibernate so I appreciate any insight or solutions you can give. Thanks.
Update
It appears my best solution on this will be to map the User with an IUserType and inject (preferably with StructureMap) a service for populating the object before its returned. Framed in that light there are a couple of questions here that deal with the topic mostly suggesting the need for a custom ByteCodeProvider. Will I still need to do this in order for IUserType to take a parameterized constructor or do the comments here: NHibernate.ByteCode.LinFu.dll For NHibernate 3.2 make a difference?
using a Usertype to convert user to id and back
public class SomeClass
{
public virtual string Id { get; protected set; }
public virtual User User { get; protected set; }
}
// in FluentMapping (you have to translate if you want to use mapping by code)
public SomeClassMap()
{
Map(x => x.User).Column("user_id").CustomType<UserType>();
}
public class UserType : IUserType
{
void NullSafeSet(...)
{
NHibernateUtil.Int32.NullSafeSet(cmd, ((User)value).Id, index);
}
void NullSafeGet(...)
{
int id = (int)NHibernateUtil.Int32.NullSafeGet(cmd, ((User)value).Id, index);
var userrepository = GetItFromSomeWhere();
return userrepository.FindById(id);
}
}

Automapper and NHibernate lazy loading

I am struggling with this issue:
I have a list of NHibernate objects called "Project". These objects contain a lazy - loaded list of "Branches". I am trying to pass a list of Projects to a WCF service so I am using AutoMapper to transform them to flat objects.
The problem is that even though the destination objects called "ProjectContract" does not contain a list of Branches, Automapper still invokes this collection and a lot of queries are made to the database because NHibernate fires the lazy - loading and loads the Branches collection for each project.
Here are the classes and the mapping:
public class Project
{
public virtual int ID
{
get;
set;
}
public virtual string Name { get; set; }
public virtual string Description { get; set; }
public virtual IList<Branch> Branches { get; set; }
}
[DataContract]
public class ProjectContract
{
[DataMember]
public virtual int ID
{
get;
set;
}
[DataMember]
public virtual string Name { get; set; }
[DataMember]
public virtual string Description { get; set; }
}
public class ProjectMappings : Profile
{
protected override void Configure()
{
Mapper.CreateMap<Project, ProjectContract>();
}
}
My question is: Is there a way to tell AutoMapper to not touch the "Branches" collection because I don't care about it and that is a proxy that will trigger many database calls?
I temporarily fixed this with MaxDepth(0), but there are other entities where I have collections that I want to transfer, and collections that I don't want to be touched, like this one. In that case, MaxDepth(0) will not work.
Thank you,
Cosmin
Yes, The AutoMapper Ignore function.
Mapper.CreateMap<Source, Destination>()
.ForMember(dest => dest.SomeValuefff, opt => opt.Ignore());