New Poco Entity with null navigation property EF Code first - c#

im using entity framework code first. Here are my poco classes:
public class BusinessPartner
{
[Key]
public int BusinessPartnerID { get; set; }
[Required]
[StringLength(255)]
public string Title { get; set; }
[StringLength(255)]
public string Address { get; set; }
public virtual ICollection<BusinessPartnerLanguage> BusinessPartnerLanguage { get; set; }
}
public class Language
{
[Key]
public int LanguageID { get; set; }
[Required]
[StringLength(255)]
public string Title { get; set; }
[Required]
public bool Active { get; set; }
public virtual ICollection<BusinessPartnerLanguage> BusinessPartnerLanguage { get; set; }
}
public class BusinessPartnerLanguage
{
[Key, Column(Order = 0)]
public int BusinessPartnerID { get; set; }
[Key, Column(Order = 1)]
public int LanguageID { get; set; }
[Required]
public bool Default { get; set; }
public virtual BusinessPartner BusinessPartner { get; set; }
public virtual Language Language { get; set; }
}
when i create new entity in my controller like:
BusinessPartner bp = new BusinessPartner();
its navigation property - bp.BusinessPartnerLanguage is null,
so if i do something like :
bp.BusinessPartnerLanguage.Add(someEntity);
im getting null exception. I managed to work around this by changing virtual ICollection properties to :
private ICollection<BusinessPartnerLanguage> _BusinessPartnerLanguage;
public virtual ICollection<BusinessPartnerLanguage> BusinessPartnerLanguage
{
get { return this._BusinessPartnerLanguage ?? (this._BusinessPartnerLanguage = new HashSet<BusinessPartnerLanguage>()); }
set { _BusinessPartnerLanguage = value; }
}
But i wanna know why isnt this working in normal way as it should, and is there a better way to work around this problem. Thanks in advance.

It's because It is just a POCO entity, if the property never been set, it will be null.
When retrieving an object from db context, if you enable the proxy, EF will create a proxy entity. And if you mark the property as virtual, any access to the virtual property will load the relationship and initialize the collection (if it's null). And it's the same thing with eager loading.
But you can't load the relationship if the entity doesn't exist in the database (a new one), it has to be an existing entity.
If you have existing id, you can create the proxy programmatically and lazy loading will load the relationship and initialize the collection. But you have to use Create method instead of new operator.
A proxy instance will not be created if you create an instance of an
entity using the new operator. - MSDN
var context = ...
int existingBPID = ...
var bp = context.Set<BusinessPartner>().Create(); // BusinessPartnerLanguage = null
bp.BusinessPartnerID = existingBPID; // BusinessPartnerLanguage = null
context.Entry(bp).State = EntityState.Unchanged; // BusinessPartnerLanguage = null
var collection = bp.BusinessPartnerLanguage; // trigger lazy loading, BusinessPartnerLanguage != null
And you can also simplify the property as follow.
private readonly ICollection<BusinessPartnerLanguage> _BusinessPartnerLanguage
= new HashSet<BusinessPartnerLanguage>();
public virtual ICollection<BusinessPartnerLanguage> BusinessPartnerLanguage
{
get { return _BusinessPartnerLanguage; }
}

Related

C# EF6 mapping not applied on select

I am facing an issue with the Fluent API mapping in EF6. It is all set up, but for some reason, anytime I select an object, it is missing the child objects.
Lets start with the DbContext:
public partial class PMSContext : DbContext
{
public PMSContext() : base(nameOrConnectionString: "PmsDb")
{
this.Configuration.ProxyCreationEnabled = false;
this.Configuration.LazyLoadingEnabled = false;
}
public DbSet<Employee> employees { get; set; }
public DbSet<Project> projects { get; set; }
public DbSet<ProjectStep> projectSteps { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Entity<Employee>().HasMany(e => e.Projects).WithMany(t => t.EmployeesWorkingOnProject).Map(m =>
{
m.MapLeftKey("EmployeeId");
m.MapRightKey("ProjectId");
m.ToTable("employee_project");
});
modelBuilder.Entity<ProjectStep>().HasRequired(p => p.Project).WithMany(s => s.ProjectSteps).Map(m => m.MapKey("Project")).WillCascadeOnDelete(true);
//modelBuilder.Entity<Project>().HasMany(p => p.ProjectSteps).WithRequired(ps => ps.Project).WillCascadeOnDelete(true);
modelBuilder.Entity<Employee>().HasOptional<Project>(e => e.LeaderOfProject).WithOptionalPrincipal(p => p.ProjectLeader).Map(m => m.MapKey("ProjectLeader"));
}
public Project FindProjectById(int id)
{
return this.projects.Find(id);
}
}
This is pretty much everything needed which plays into my issue.
I have set up a total of 3 model classes:
[DataContract(Namespace = "Shared")]
public class Employee
{
public Employee()
{
this.Projects = new List<Project>();
}
[DataMember]
public int ID { get; set; }
[DataMember]
public String Name { get; set; }
[DataMember]
public String JobDescription { get; set; }
[DataMember]
public String Department { get; set; }
[DataMember]
public String DirectDialing { get; set; }
[DataMember]
public bool Status { get; set; }
public virtual Project LeaderOfProject { get; set; }
[DataMember]
public virtual List<Project> Projects { get; set; }
}
[DataContract(Namespace = "Shared")]
public class Project
{
public Project()
{
this.EmployeesWorkingOnProject = new List<Employee>();
this.ProjectSteps = new List<ProjectStep>();
}
[DataMember]
public int ID { get; set; }
[DataMember]
public String Titel { get; set; }
[DataMember]
public DateTime StartDate { get; set; }
[DataMember]
public DateTime EndDate { get; set; }
[DataMember]
public String Description { get; set; }
[DataMember]
public Employee ProjectLeader { get; set; }
[DataMember]
public bool Status { get; set; }
[DataMember]
public virtual List<Employee> EmployeesWorkingOnProject { get; set; }
[DataMember]
public virtual List<ProjectStep> ProjectSteps { get; set; }
}
[DataContract(Namespace = "Shared")]
[Table("project_step")]
public class ProjectStep
{
[DataMember]
public int ID { get; set; }
[DataMember]
public String Description { get; set; }
[DataMember]
public DateTime StartDate { get; set; }
[DataMember]
public DateTime EndDate { get; set; }
[DataMember]
public Project Project { get; set; }
}
And the corresponding database setup:
Now to my problem. Whenever I execute the FindProjectById method it does return the proper object, but it is missing any reference to the childs. this means that
ProjectSteps
EmployeesWorkingOnProject
ProjectLeader
are not set. This also causes issues on my delete methods. I assume that this is an error in my OnModelCreating method, but I am not 100% sure.
Can anyone tell me what I am missing to fetch the child objects as well?
You have disabled lazy loading and proxies, but if you do not .Include() child entities then EF doesn't know to load them. To use .Include() you will need to use .SingleOrDefault() rather than Find. Otherwise you will need to go to the context to Load child collections/references.
Your FindProjectById() would look something like:
var project = this.Projects.Include(x=>x.ProjectLeader)
.Include(x=>x.EmployeesWorkingOnProject)
.Include(x=>x.ProjectSteps)
.SingleOrDefault(x=>x.ID == id);
return project;
A caveat around using SingleOrDefault vs. Find is that where Find will search local store then go to DB, Single/First/ etc. will go to DB. This means that a query is executed each time where Find may find an entity in the local memory store. if you're inserting records into the DB Context (and prior to save changes) searching for that entity will not return an entity that is in the local store, but hasn't been committed to the DB yet. (prior to SaveChanges())
Typically you do not want to return entities outside of the scope of the DbContext as lazy load proxies won't work so anything you don't pre-load will be #null. I generally rely on deferred execution, returning IQueryable then .Select() the various bits I care about into DTO/ViewModel POCO classes.
You can try change the lazy loading into true if you want to automatically load the child objects.
this.Configuration.LazyLoadingEnabled = true;
if the lazy loading is false, then you need to load the reference before accessing it.

How to load specific objects in entity framework

I have an auto generated class (Orders class) which references other objects. What i'm trying to accomplish is get
an order using the order id and the user related to that order.
However when loading the object it is taking 2 seconds to load it. I looked at the object and EF is populating the list
of all the dependent objects (Language, country, customer user).
How do i get only specific object like only the order properties and the user object ? I tried to accomplish that using
the below code but it is loading all the objects.
//Code for getting order id and user related to it
using (var _storeEntities = new StoreEntities())
{
var test = _storeEntities.Orders.Where(r => r.Id.ToString() == orderId).Include(x => x.User).ToList();
}
// Parent entity
public partial class StoreEntities: DbContext
{
public virtual DbSet<Orders> Orders { get; set; }
}
// Order object
public partial class Orders
{
public System.Guid Id { get; set; }
public bool IsActive { get; set; }
public System.DateTime OrderDate{ get; set; }
public Nullable<int> CustomerId { get; set; }
public string LanguageId { get; set; }
public Nullable<System.Guid> UserId { get; set; }
public string CountryId { get; set; }
public virtual Customer Customer{ get; set; }
public virtual User User{ get; set; }
public virtual Country Country { get; set; }
public virtual Language Language { get; set; }
}
You should disable Lazy loading in configuration
public class YourContext : DbContext
{
public YourContext ()
{
this.Configuration.LazyLoadingEnabled = false;
}
}
Or only during your call
using (var _storeEntities = new StoreEntities())
{
_storeEntities .Configuration.LazyLoadingEnabled = false;
var test = _storeEntities.Orders.Where(r => r.Id.ToString() == orderId)
.Include(x => x.User).ToList();
}
Here is the MSDN article for every type of loading
https://msdn.microsoft.com/en-nz/data/jj574232.aspx
Disable lazy loading:
_storeEntities.Configuration.LazyLoadingEnabled = false;
In constructor or before the query you want to run.
Or just remove virtual keyword.
Also you can disable proxy creation which is used for lazy loading:
_storeEntities.Configuration.ProxyCreationEnabled = false;

LINQPad Hiding Entities with POCO Connection

When I connect LINQPad to my custom assembly using POCO connection, I'm missing some of my entities. This is a project was handed off to me, so I'm not sure what exactly causes this.
This is what I see in LINQPad for my FooContext,
This is my project model,
This is a table that does not show up in LINQPad,
[Table("People")]
public class Person : DomainEntity
{
[Required, StringLength(50)]
public String GivenName { get; set; }
[Required, StringLength(50)]
public String Surname { get; set; }
public virtual ICollection<EmailAddress> EmailAddresses { get; set; }
public virtual ICollection<Phone> Phones { get; set; }
public virtual ICollection<PhysicalAddress> PhysicalAddresses { get; set; }
public virtual ICollection<Login> Logins { get; set; }
public virtual ICollection<CompanyContact> CompanyContacts { get; set; }
}
Here's a table that does,
[Table("Tags")]
public class Tag
{
public int Id { get; set; }
public String Value { get; set; }
public virtual DomainEntity Entity { get; set; }
}
What determines what LINQPad shows for FooContext?
LINQPad uses the following logic to determine which entities to display in the Schema Explorer:
System.Data.Entity.Infrastructure.IObjectContextAdapter adapter = this;
var items = adapter.ObjectContext.MetadataWorkspace.GetItems (DataSpace.CSpace);
var container = (EntityContainer) items.First (i => i.BuiltInTypeKind == BuiltInTypeKind.EntityContainer);
var entities = container.BaseEntitySets.Where (b => b.BuiltInTypeKind == BuiltInTypeKind.EntitySet && b.ElementType != null && b.ElementType is EntityType);
entities.Dump(1);
Does your table show up when you run this code in LINQPad with your custom POCO data context selected?

Entity Framework 6.1 - code first - reference properties not loading correctly

I've noticed an issue with EF 6.1 code first. I have the following classes -
namespace Domain
{
public interface ISupportsOptimisticConcurrency
{
byte[] RowVersion { get; set; }
}
public class Entity : ISupportsOptimisticConcurrency
{
public int Id { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
}
public class Lookup : Entity
{
public Lookup()
{
Description = string.Empty;
}
[Required]
[MaxLength(100)]
public string Name { get; set; }
[MaxLength(300)]
public string Description { get; set; }
}
public class GroupType : Lookup
{
}
public class Group:Entity
{
public Group()
{
GroupType = new GroupType();
}
[Required]
public string Name { get; set; }
[Required]
public Guid ExternalId { get; set; }
[Required]
public string Password { get; set; }
[Required]
public string MonitorEmail { get; set; }
public string UrlRequestEmail { get; set; }
public bool UsesDefaultOptions { get; set; }
[ForeignKey("GroupType")]
public int GroupTypeId { get; set; }
public virtual GroupType GroupType { get; set; }
}
}
I've written a typical Repository class for accessing data from DB. Now, when I try to find a Group by Id, and include the GroupType, the GroupType doesn't load properly, and the Name property of GroupType comes as null.
Interestingly, when I removed the Group constructor which initializes a new GroupType, things start working fine.
Could you please explain this behavior?
Note: This same scenario works fine with NHibernate as it is.
Thanks for the replies.
I think you have to remove the initialization logic in the Group constructor:
GroupType = new GroupType();
This probably overwrites the loaded data or does not even load it (because it already was instantiated), causing the GroupType property to be the instance that you initialized it with instead of the one in the database.
It may be the same issue as explained here.

Reflection To Update Source Entity with Children To Target With Children

For all you Reflection experts I need some help. I am working on a gneric method to be able to use hierarchical data assignments in an MVC application using CodeFirst and EF5. A user would apply changes to the parent object (source) and this method would populate the child object(s) (target). It's not working as I would expect. Any pointers would be greatly appreciated.
The below method "CopyEntity" is intended to take a source entity that has child entities and update a target entity of the same type and structure. We are only looking to update those entities that inherit a base type of 'Association' and also do not have a 'NoInherit' attribute on the members. Not every property would be updated from the 'Association'
public class ClassCode : Inheritable<ClassCode>
{
// Base Properties ----------------------------------
public int LobId { get; set; }
public virtual LOB LOB { get; set; }
public bool IsVirtual { get; set; }
//public string Name { get; set; }
public string Description { get; set; }
[Required(ErrorMessage = "Select Code")]
public string Code { get; set; }
//enable to force recreation of the database
public string IID { get; set; }
public virtual ICollection<ClassSubjectivityAssoc> Subjectivities{ get; set; }
}
public class ClassSubjectivityAssoc : Association
{
[Column("SubjectivityId")]
public override int AssociationId { get; set; }
[ForeignKey("AssociationId")]
public virtual Subjectivity Subjectivity { get; set; }
public int ClassCodeId { get; set; }
[ForeignKey("ClassCodeId")]
public virtual ClassCode ClassCode { get; set; }
}
public abstract class Association : BaseEntity
{
public DateTime Start { get; set; }
public DateTime? End { get; set; }
public bool IsCurrent { get; set; }
public int Order { get; set; }
public bool BreakInheritance { get; set; }
public int? InhId { get; set; }
public virtual ClassCode InhClass { get; set; }
public int CodeId { get; set; }
[ForeignKey("ClassCodeId")]
public virtual Code ClassCode { get; set; }
public abstract int AssociationId {get; set;}
}
public class BaseEntity
{
private static DateTime CREATE_DATE
{
get { return DateTime.Now; }
}
[NoInherit]
public int Id { get; set; }
[NoInherit]
public string CreatedBy { get; set; }
[NoInherit]
public DateTime Created { get; set; }
[NoInherit]
public string LastModifiedBy { get; set; }
[NoInherit]
public DateTime LastModified { get; set; }
public bool IsActive { get; set; }
}
protected void CopyEntity(Inheritable<T> source, Inheritable<T> target)
{
Type sourceType = source.GetType();
Type targetType = target.GetType();
// ensure that the types can be assigned to one another
//if (!targetType.IsAssignableFrom(sourceType)) return;
// loop through the properties of the source system
foreach (PropertyInfo sourceMember in sourceType.GetProperties().Where(sourceMember => sourceMember.GetCustomAttribute(typeof(NoInheritAttribute)) == null))
{
var targetMember = targetType.GetProperty(sourceMember.Name);
// if the property doesn't exist in the target, let's get out
if (targetMember == null) continue;
// check if this property is a Collection
bool isCollection = (sourceMember.PropertyType.IsGenericType && sourceMember.PropertyType.GetInterfaces().Any(x =>
x.IsGenericType &&
x.GetGenericTypeDefinition() == typeof(IEnumerable<>)));
// if we are not dealing with associations, let's get outta herre
if (!(isCollection
? sourceMember.PropertyType.GetGenericArguments().Single()
: sourceMember.PropertyType)
.IsSubclassOf(typeof(Association))) continue;
if (isCollection)
{
IEnumerable<Association> targetCollection = (IEnumerable<Association>) targetMember.GetValue(target);
// Loop through the collection and evaluate
foreach (Association sourceAssociation in (IEnumerable) sourceMember.GetValue(source))
{
Association targetAssociation =
targetCollection.SingleOrDefault(t => t.AssociationId == sourceAssociation.AssociationId);
CopyAssociation(sourceAssociation, targetAssociation);
}
}
else
{
Association sourceAssociation = (Association) sourceMember.GetValue(source);
Association targetAssociation = (Association) targetMember.GetValue(target);
CopyAssociation(sourceAssociation, targetAssociation);
}
}
}
The problem is: It does not seem to be traversing the child objects properly and does not update the target as I would have expected. Looking for input and suggections as I am not as proficient in Reflection as I would like.
UPDATE: 12/06/2012
Ok, So I believe I've found the problem but still looking for a solution. This problem came to me from another dev on my team with the assumption that the object model was sound. However, it seems that the entity that we are currently testing does not retrieve the seeded values from the db.
There is nothing defined in the OnModelCreating() method defining this association although the tables are being properly created from the entity model and the data is being loaded properly from the dbSeed() method. I can run a select statement from the db and get the proper data. It seems that the entity model foreign key associations might be invalid and thus preventing the correct data retrieval. Any help would be greatly appreciated.
UPDATE: Ok, finally retrieving the correct data. This finally did it.
modelBuilder.Entity<ClassSubjectivityAssoc>()
.HasRequired(c => c.ClassCode)
.WithMany(u => u.Subjectivities)
.HasForeignKey(f => f.ClassCodeId);
I'm not really sure if I understand the question, but it seems that you just need to make sure that sourceMember is either an Association (or descendant) or a collection of Association.
You could do that in a quick and dirty way with casting the resulting value to Association, and checking if the cast succeeded, like this for simple properties:
Association sourceAssociation = sourceMember.GetValue(source) as Association;
if (sourceAssociation != null) // the source is a valid Association
{
Association targetAssociation = targetMember.GetValue(target) as Association;
if (targetAssociation != null) // the destionation too
{
CopyAssociation(sourceAssociation, targetAssociation);
}
}
and with similar code for the collections as well
The method you have shown will not descend a object tree, so it will only work on Association objects that are directly attached to the Entity.
See original post "UPDATES" for solution.

Categories

Resources