Background:
I'm using the SandboxManager format described in the documentation to export my entities to a second manager for changes, and then the entity is imported back into the main manager. I'm creating the Sandbox on page initialization from a copy of the main manger using createEmptyCopy(), giving the Sandbox the same metadata and no entities.
During the export, I pass the entity over this way:
function exportEntityToSandbox(entity) {
var exportData = STEP.EntityManager.exportEntities([entity], false);
var result = STEP.SandboxManager.importEntities(exportData,
{ mergeStrategy: breeze.MergeStrategy.OverwriteChanges });
// ImportEntities changes the service name so revert it back
STEP.SandboxManager.setProperties({
dataService: new breeze.DataService({
serviceName: 'api/Sandbox',
hasServerMetadata: false
})
});
return result.entities[0];
};
I'm currently working with this entity:
public class License
{
public int ID { get; set; }
public int LicenseTypeID { get; set; }
public virtual LicenseType LicenseType { get; set; }
public int ExternalProductID { get; set; }
public virtual ExternalProduct ExternalProduct { get; set; }
public int? LicensesPurchased { get; set; }
public int? LicensesAllocated { get; set; }
public string AllocationDescription { get; set; }
public bool DeletedFlag { get; set; }
}
And the map for this entity:
public LicenseMap()
{
this.HasKey(t => t.ID);
this.Property(t => t.ID)
.HasColumnName("ID")
.HasColumnType("int")
.IsRequired();
this.Property(t => t.LicenseTypeID)
.HasColumnName("LICENSE_TYPE_ID")
.HasColumnType("int")
.IsRequired();
this.Property(t => t.ExternalProductID)
.HasColumnName("PRODUCT_ID")
.HasColumnType("int")
.IsRequired();
this.Property(t => t.LicensesPurchased)
.HasColumnName("LICENSES_PURCHASED")
.HasColumnType("int")
.IsOptional();
this.Property(t => t.LicensesAllocated)
.HasColumnName("LICENSES_ALLOCATED")
.HasColumnType("int")
.IsOptional();
this.Property(t => t.AllocationDescription)
.HasColumnName("ALLOCATION_DESCRIPTION")
.HasColumnType("varchar")
.HasMaxLength(Int32.MaxValue)
.IsOptional();
this.Property(t => t.DeletedFlag)
.HasColumnName("DELETED_FLAG")
.HasColumnType("bit")
.IsRequired();
this.ToTable("LICENSE");
}
}
I'm only passing in the License entity during export--the ExternalProduct and LicenseType navigation entities are not passed to the Sandbox. However, ALL ExternalProducts & LicenseTypes are loaded into the Main manager on page initialization. So in the workflow, I select from a list of dropdowns the ExternalProduct and update ONLY the ExternalProductId (as the ExternalProduct itself is not on the Sandbox).
Issue:
The problem I'm facing is when the entity is exported back into the Main Manager:
function save(id, manager, tag) {
manager = manager || STEP.SandboxManager;
return manager.saveChanges()
.then(saveSucceeded)
.fail(saveFailed);
function saveSucceeded(data) {
var exportData = STEP.SandboxManager.exportEntities(data.entities, false);
STEP.EntityManager.importEntities(exportData,
{ mergeStrategy: breeze.MergeStrategy.OverwriteChanges });
// ImportEntities changes the service name
// Revert it back
STEP.EntityManager.setProperties({
dataService: new breeze.DataService({
serviceName: 'api/Datamart',
hasServerMetadata: false
})
});
// Get a reference to the same entity in the Sandbox and update observable
var entityKey = data.entities[0].entityAspect.getKey();
var type = entityKey.entityType.shortName;
var entityManagerEntity = STEP.EntityManager.getEntityByKey(type, id);
};
function saveFailed(msg) {
// Do stuff
};
};
The entity has a new ExternalProductId (changed during edit), but still has the old ExternalProduct navigation entity. The new navigation entity is not wired up to License even though I know it's in the cache. The navigation property still points to the old entity (property License.ExternalProductId does not equal License.ExternalProduct.ID).
So am I expecting too much of Breeze to do this rewiring upon import and I will need to do this manually every time?
I thought this may a problem with my EF definition and have tried adding each of these to the LicenseMap with no success:
this.HasRequired(m => m.ExternalProduct)
.WithOptional()
.Map(m => m.MapKey("ExternalProductID"));
this.HasRequired(t => t.ExternalProduct
.WithMany()
.HasForeignKey(t => t.ExternalProductID)
.WillCascadeOnDelete(false);
This is a required relationship with the navigation property on the License entity only.
I'm using Breeze v1.4.11
Edit:
I just made sure this was in the entity map:
this.HasRequired(t => t.ExternalProduct)
.WithMany()
.HasForeignKey(t => t.ExternalProductID);
And tested this simple coding snippet:
license.ExternalProductID(816);
var test = license.ExternalProduct();
And the test variable of navigation entity ExternalProduct still does not change after setting the ID directly. According to Breeze documentation the navigation entity should be updated unless I'm doing something wrong?
You have found a bug. Setting the FK to a value should cause the corresponding navigation property to be refreshed. That works properly if the corresponding entity is in cache. Unfortunately, if the corresponding entity is NOT in cache, Breeze v.1.5.1 does not null-out the navigation property as it should.
I will be back when that is fixed. We will jump on that. Stay tuned.
I got this figured out.
The problem was I didn't have all of the ExternalProducts in cache so when changing the navigation property ID, the associated ExternalProduct entity wasn't available to wire up from the new ID. When I make sure the new ExternalProduct is in cache, changing the ID on License updates the ExternalProduct properly.
I could argue that Breeze should set the ExternalProduct to null when License.ExternalProductID is changed to a value for an entity not in cache but I digress.
Related
I am trying to use Entity Framework Core / .NET 5 to interact with my databases.
When I try to query DbContent.UserClaims I get the following error:
Microsoft.Data.SqlClient.SqlException (0x80131904): Invalid column name 'UserId1'.
I am not sure where UserId1 us coming from. I have a property called UserId which is the foreign key. Here are the relation mapping
Here is what I tried to do in the DbContext class
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<User>(user =>
{
user.HasKey(r => r.Id);
user.HasMany(x => x.UserRoles).WithOne().HasForeignKey(x => x.UserId);
user.HasMany(x => x.UserClaims).WithOne().HasForeignKey(x => x.UserId);
user.HasMany(x => x.UserTokens).WithOne().HasForeignKey(x => x.UserId);
});
builder.Entity<UserClaim>(userClaim =>
{
userClaim.HasKey(r => r.Id);
userClaim.HasOne(r => r.User).WithOne().HasForeignKey<UserClaim>(x => x.UserId);
});
}
Here is the UserClaim class which is derived from IdentityUserClaim
public class UserClaim : IdentityUserClaim<string>
{
public virtual User User { get; set; }
}
Here is the User class which is derived from IdentityUser
public class User : IdentityUser<string>
{
public virtual ICollection<UserToken> UserTokens { get; set; }
public virtual ICollection<UserRole> UserRoles { get; set; }
public virtual ICollection<UserClaim> UserClaims { get; set; }
}
Here is the query that EF5 is generating
SELECT [u].[Id], [u].[ClaimType], [u].[ClaimValue],[u].[UserId], [u].[UserId1]
FROM [UserClaims] AS [u]
How can I fix this issue in Entity Framework Core?
You're using shadow properties here, and on top of that, trying to add UserId foreign key to the User itself. Since UserId is an already defined property in that class, it's adding a suffix to the property name every time you're trying to add a foreign key in the user table by the same name.
It should be something like this:
modelBuilder.Entity<UserClaim>()
.Property<int>("UserForeignKey");
modelBuilder.Entity<UserClaim>()
.HasOne(a => a.User)
.WithMany(b => b.UserClaims)
.HasForeignKey("UserForeignKey")
Read the documentation on how to configure Fluent API for shadow properties, and some other ways to use the Fluent API.
I'm trying to create a tree structure in the db where items can reference each other. I was considering that to be a simple task, however, EF Core decided to strike. I can't get it to generate the correct foreign keys, it keeps throwing an exception:
Unable to determine the relationship represented by navigation 'Item.Parent' of type 'Item'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
My class looks like this (simplified):
public class Item
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid Id { get; set; }
[ForeignKey("Parent")]
public Guid? ParentId { get; set; }
public virtual Item Parent { get; set; }
[InverseProperty("Parent")]
public virtual IEnumerable<Item> Children { get; set; }
}
I have even tried to remove the Children property, but with the same result.
Another thing I have tried was Fluent API:
entity
.HasOne(e => e.Parent)
.WithMany(e => e.Children)
.HasForeignKey(e => e.ParentId);
//or
entity
.HasOne<Item>(e => e.Parent)
.WithMany()
.HasForeignKey(e => e.ParentId);
//or
entity
.HasMany(e => e.Children)
.WithOne(e => e.Parent)
.HasForeignKey(e => e.ParentId);
//or
entity
.HasMany(e => e.Children)
.WithOne(e => e.Parent);
They all keep generating the same error, I'm kind of lost on what am I doing wrong.
I was looking at some other answers too and they look pretty much like what I'm trying to do, however, my EF will not generate the migration.
Disregard this question, I've figured out my own mistake. I will leave it here in case someone faces the same problem as the error message is very misleading.
What has happened was a copy&paste issue where I have created another configuration for an unrelated entity, but forgot to update the type:
{
public void Configure(EntityTypeBuilder<Item> entity)
{
entity
.HasNoKey();
}
}
Notice the Item type in the generics definition. This lead the EF Core to wipe out the key that was originally set on the entity and made EF Core confused about the relationship (as now I'm targeting foreign key to a property that's neither key nor index).
Hi i just start working on a project. I saw a context query line. It includes entity then includes again.
I don't understand whats what mean. I created 2 diffrent var object. when removed the self include line then debugging. I saw same thing.
This is the code
using (var context = new DbContext())
{
var entity = context.Set<Book>()
..
...
.Include(x => x.Book)
.Include(x => x.BookAuthor).ThenInclude(x => x.Author)
..
... goes on
return entity.ToList()
}
I thought this is the same thing
using (var context = new DbContext())
{
var entity = context.Set<Book>()
..
...
//remove .Include(x => x.Book)
.Include(x => x.BookAuthor).ThenInclude(x => x.Author)
..
... goes on
return entity.ToList()
}
other example code is
.Include(x => x.CategoryBranches).ThenInclude(x => x.Category)
.Include(x => x.CategoryBranches).ThenInclude(x => x.Category).ThenInclude(x => x.BookCategories);
Can anyone explain the self include again. I saw it everywhere
Thank you
If the Book has a Navigation Property called Book (that doesnt make sense with that name semantically, but technically it does), you have to include it to load it.
You can have a reference to yourself, just like to any other table via a foreign key, and have a navigation property for that.
Consider this example that makes more sense semantically:
public class Person
{
public int Id { get; set; }
public int ParentId { get; set; }
public Person Parent { get; set; } // This is a self referencing navigation property
}
So now to use the parent you'd have to:
persons.Include(p => p.Parent). ... ;
to access the parent.
Lazy loading means related entities in a model class are loaded automatically.
The parent class provided by Mafii does not use lazy loading. That's why he had to use the Include method to load (or include) the parent entity (the related entity). If he didn't do it this way, any details under the Parent navigation will not be loaded. To load a related entity automatically, simply enable lazy loading by using the virtual keyword in the navigation property of the entity. This way, you don't have to use Include anymore.
public class Person
{
public int Id { get; set; }
public int ParentId { get; set; }
public virtual Parent Parent { get; set; }
}
Note: I think there is a performance downside to this. So, if you do this, you need to be sure you actually need those entities loaded.
When I try to load child entity of parent entity it loads with default values. If i try to load explicitly it throws exception
Multiplicity constraint violated. The role 'Association_Customer_Target' of the relationship 'CodeFirstNamespace.Association_Customer' has multiplicity 1 or 0..1. This exception is thrown while retrieving the child entities of a complex graph.
I have a graph Association which has a child entity Customer with a relationship of one to zero or one and has an Independent association.*Primary key* is shared. I'm using EF6. lazy loading is enabled.
public class Association
{
public virtual long Id { get; set; }
public virtual string ExternalId { get; set; }
public virtual int OrganizationId { get; set; }
public virtual AssociationType AssociationType { get; set; }
public virtual Customer Customer {get; set;}
public Association()
{
Customer = new Customer();
}
}
Customer class.
public class Customer
{
public virtual long Id { get; set; } //Shared primary key
public virtual ICollection<Item> Items {get; set;}
public virtual ICollection<Complaint> Complaints {get; set;}
public customer()
{
Items = new List<Item>();
Complaints = new List<Complaint>();
}
}
Mapping are Uni directional:
public class AssociationMapping:EntityTypeConfiguration<Association>
{
public AssociationMapping() : base()
{
HasKey(x => x.Id);
Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(x => x.ExternalId).IsRequired();
Property(x => x.OrganizationId).IsRequired();
Property(x => x.AssociationType);
HasOptional(x => x.Customer).WithRequired().WillCascadeOnDelete();
}
}
public class CustomerMapping : EntityTypeConfiguration<Customer>
{
public CustomerMapping ():base()
{
HasKey(x => x.Id);
Property(x => x.Id);
HasMany(x => x.Items)
.WithOptional()
.HasForeignKey(key => key.CustomerId)
.WillCascadeOnDelete();
HasMany(x => x.Complaints)
.WithOptional()
.HasForeignKey(key => key.CustomerId)
.WillCascadeOnDelete();
}
}
When I Load My association entity it loads perfectly but child entity Customer is loaded with default values when i try to load Customer explicitly it throws the exception.
var dbassociation = Single<Association>(x => x.OrganizationId== asso.organizationId && x.ExternalId == asso.ExternalId && x.AssociationType == asso.AssociationType);
dbassociation.Customer = Single<Customer>(x => x.id == dbassociation.id);
[Update: Single Method]
public TEntity Single<TEntity>(System.Linq.Expressions.Expression<Func<TEntity, bool>>criteria) {
return Context.Set<TEntity>().SingleOrDefault(criteria); }
For testing purpose I have tried to eager load by removing virtual on Customer property in association class and tried following but it throws same excepetion
Context.Configuration.LazyLoadingEnabled = false;
Context.Entry<Association>(dbassociation).Reference<Customer>(pa => pa.Customer).Load();
I have also tried which throws same exception
var dbassociation = Context.Set<Association>().Include("Customer").SingleOrDefault(x => x.OrganizationId== asso.organizationId && x.ExternalId == asso.ExternalId && x.AssociationType == asso.AssociationType);
Now I came to conclusion that though I use different methods for retrieving the exception is same. The problem is with mapping I guess. I appreciate your comments and suggestions.
Try to remove
Customer = new Customer();
from the Association constructor. Instantiating navigation references is a source for known problems (in contrast to instantiating empty navigation collections which is fine). It is the reason why you get a Customer with default values. I'm not sure if it also explains the exception, but I could imagine that when the Association gets loaded and attached to the context together with the uninitialized Customer created by the default constructor EF detects related entities with invalid keys: The Association which has (I assume) a key value !=0 and the related Customer with a key ==0 (because it never has been initialized to another value). However, in a shared primary key association the two key values must match. Because they don't, it might cause the exception (however an exception that doesn't really point very well to the root of the problem).
Just a guess.
I'm transitioning from nHibernate to EF5 and am having problems with mapping relations. Working with a conventional one-to-many relation let's call it Instructor/Course:
public class Course
{
public Course()
{
Instructor = new Instructor();
CourseFiles = new List<CourseFile>();
}
public int Id { get; set; }
public string Description { get; set; }
public string Title { get; set; }
public int InstructorId { get; set; }
public virtual Instructor Instructor { get; set; }
public virtual ICollection<CourseFile> CourseFiles { get; set; }
}
public class CourseMap : EntityTypeConfiguration<Course>
{
public CourseMap()
{
// Primary Key
this.HasKey(t => t.Id);
// Properties
this.Property(t => t.Description)
.IsRequired();
this.Property(t => t.Title)
.IsRequired()
.HasMaxLength(125);
// Table & Column Mappings
this.ToTable("Course");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.Description).HasColumnName("Description");
this.Property(t => t.Title).HasColumnName("Title");
this.Property(t => t.InstructorId).HasColumnName("InstructorId");
// Relationships
this.HasRequired(t => t.Instructor)
.WithMany(t => t.Courses)
.HasForeignKey(d => d.InstructorId);
}
}
public partial class Instructor
{
public Instructor()
{
this.Courses = new List<Course>();
}
public int Id { get; set; }
public string Biography { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
public class InstructorMap : EntityTypeConfiguration<Instructor>
{
public InstructorMap()
{
// Primary Key
this.HasKey(t => t.Id);
this.Property(t => t.Biography)
.HasMaxLength(140);
// Table & Column Mappings
this.ToTable("Instructor");
this.Property(t => t.Id).HasColumnName("Id");
this.Property(t => t.Biography).HasColumnName("Biography");
//the mapping of this relation _has to be where the problem is
// really seems like this should create the required plumbing but no joy
this.HasMany(w => w.Webinars)
.WithRequired()
.HasForeignKey(w => w.PresenterId);
}
}
}
The above is the POCO generated by the Reverse Engineer tool where I've used as a starting point, the db that nHibernate's been using for the last few years. For the most part things lined up really well. After minor adjustments for PKey names I've been able to seed the new db with nHibernate's data and the same joins are working against both dbs.
E.G. this works the same returns correct values:
SELECT C.Title, C.Id, C.InstructorId, I.Id
FROM dbo.Course C
INNER JOIN dbo.Instructor I ON I.Id = C.InstructorId
...
And even within the assembly, this linq query pulls correctly against the db:
var query = from Course in _ctx.Courses
where Course.Instructor.Id == InstructorId
select Course;
So I must have lots of the ducks lined up correctly. However, when attempting to access the Instructor entity from _within the Course entity, as is typical at the View:
#foreach (var course in Model)
{
<div>#course.Title - Instructor.ID is all zeros: #course.Instructor.Id - FK is correct: #course.InstructorId</div>
}
outputs:
My Correct Course Title - Instructor.ID is all zeros: 0 - FK is correct: 555
Title From Diff Instructor - Instructor.ID is all zeros: 0 - FK is correct: 333
and so on for all courses.
I'm not taking explicit action to Dispose anything. I set a breakpoint at the start of the ForEach inside the view and don't see 'context' in the Locals window. Breaking inside the Controller the Instructor node (of context) shows a line for each and every presenter in the db all properly seeded except the first 10 (the Repository is using '.Take(10)'. Those first 10 Presenter lines (in Presenter|Local) are all zeroed out. So clearly, something's up with the Constructor in the POCO.