I work with ASP.NET MVC With Durandal/Breeze templates.
Let's say I have the following class:
public class Person
{
public int Id { get; set; }
public string Firstname { get; set; }
public string Lastname { get; set; }
public virtual List<Person> Friends { get; set; }
}
With the following EF Fluent API:
modelBuilder.Entity<Person>()
.HasMany(m => m.Friends)
.WithMany()
.Map(m => m.ToTable("Friends"));
The database is generated successfully.
The problem is when I perform a que
ry with Breeze (client side) I have no data for the Friends property.
var query = entityQuery.from('Person')
.where('id', '==', 123)
.expand("Friends");
When the query is executed I get as result the requested People entity with all the data except the Friends property is always an empty array. When I check the Json answer I see that also the data are transmitted. Even data for the Friends property. However they are not linked to the Friends property itself.
My question: what do I have to do to have my Friends property filled with values?
Thanks.
You must declare a foreign key in Person. Breeze requires the FK to correctly resolve associations.
Edit:
I just realized you are asking about a many-to-many relationship. (yeah, I should have read the post title...)
Breeze does not support many-to-many associations.
However, you could have two one-to-many relationships to work as a many-to-many. (i.e. many-to-one-to-many) In this case, you will need to define the linking table/entity and the foreign key as mentioned earlier. (see http://www.breezejs.com/documentation/navigation-properties)
Try this answer: *Note that this is incomplete because i do not see the other table that you are trying to m-2-m with Persons. ( You will only want to use Persons Table and the 2nd Table , NOT table=Friends.
db.Person
.Include(c => c.Friends)
.Where(c => c.Friends.Any(up => up.FriendVlaue == c.FirstName)) //c.from Persons
.Select(c => new
{
PersonID = c.ID,
PersonName = c.FirstName,
PersonCount = c.Person.Count()
})
{
From This answer
You should include Friends in the results. You can do this by adding Include("Friends")at Server Side API.
[HttpGet]
public IQueryable<Person> Persons()
{
return _contextProvider.Persons.Include("Friends");
}
If you don't want to return always the Friendsreference, you can create another method in the API such as PersonsWithFriends as suggested in here (Specialized query actions).
Related
I'm using the Asp Net Identity library and I've customized IdentityUser to have a custom navigation property called CompanyRole of type ApplicationRole here is rough structure bellow
public class ApplicationUser: IdentityUser
{
...
public ApplicationRole CompanyRole { get; set }
}
public class ApplicationRole : IdentityRole
{
public string Id{ get; set; }
public string Name { get; set; }
}
Fragment of ApplicationUser model configuration
builder
.HasOne(x => x.CompanyRole)
.WithOne()
.HasForeignKey<ApplicationUser>(au => au.CompanyRoleId)
.OnDelete(DeleteBehavior.Restrict);
So when I try to add multiple users with existing roles like this
var viewerRole = await _rolesService.GetViewerRole() // it queries role from dbcontex behind the scene, so it should be tracked
var usersToAdd = emails.Select(email => new ApplicationUser
{
FirstName = request.Name,
Email = email,
CompanyRole = viewerRole
}
)
_dbContext.Set<ApplicationUser>().AddRange(usersToAdd)
_dbContext.SaveChanges();
It complaints that cannot insert NULL in CompanyRoleId column, since it's a FK constraint.
The reason of this exception is that first user in a range gets CompanyRole as null, whereas others users are good. Why it's happening, since viewer role should have been tracked ?
I've tryed to play with Entities states, such as Added and Attach entity again - no luck
I've expected that all users are created with reference to existing ApplicationRole
BTW the workaround that worked, was if I split users adding one by one with slight changes and detach those immediately then it works, the drawback is that query per user...inefficient
var result = _dbContext.Set<ApplicationUser>().Add(user);
await _dbContext.SaveChangesAsync();
_dbContext.Entry(user).State = EntityState.Detached;
The issue is that you are defining the relationship between user and company role as One to One. This means any 1 company role can be assigned to only one user. So as it tries to associate the role to each user, it would de-associate it from the previous.
What it looks like you want is a Many-to-one, many users can hold the same role. Adjust your mapping to:
builder
.HasOne(x => x.CompanyRole)
.Withmany()
.HasForeignKey<ApplicationUser>(au => au.CompanyRoleId)
.OnDelete(DeleteBehavior.Restrict);
... and you should be sorted.
This question already has an answer here:
What is the correct way to do many to many entity relation insert?
(1 answer)
Closed 11 months ago.
I have a many-to-many relationship established code-first that works, with thousands of fake records generated for an API. Now I'm trying to save a new record on one side of that relationship given only the ids of the other side, as the client is passing in an array of int ids.
I've found plenty of questions with problems and answers about saving many-to-many in general, but none specifically about doing so with just a list of foreign keys. Perhaps I'm simply using the wrong terminology?
I could grab all the records for those ids up front, but it seems very heavy to wait for a database query, assign those entities to the new entity, and then go to the database again to save, when all I really need is to establish a relationship with ids I already have.
For single relationships I would just add the foreign key as a separate property and set that instead of the foreign entity itself:
public int? CarId { get; set; }
[ForeignKey("CarId")]
public CarModel? Car { get; set; }
Is there perhaps a similar paradigm for many-to-many?
Entity setup:
public class ClownModel {
public int Id { get; set; }
public List<CarModel> Cars { get; set; }
}
public class CarModel {
public int Id { get; set; }
public List<ClownModel> Clowns { get; set; }
}
DB Context OnModelCreating:
builder.Entity<ClownModel>()
.HasMany(x => x.Cars)
.WithMan(x => x.Clows);
You can use a "stub entity" to add an existing Car to a new or existing Clown without fetching the Car. Eg
var newClown = new Clown();
var car = new Car() { Id = carId };
db.Entry(car).State = EntityState.Unchanged;
newClown.Cars.Add(car);
db.Set<Clown>().Add(newClown);
db.SaveChanges();
Or include the linking entity in your model, which you can do without adding a DbSet property or changing the Many-to-Many navigation properties.
eg
builder.Entity<Clown>()
.HasMany(x => x.Cars)
.WithMany(x => x.Clowns)
.UsingEntity<ClownCar>(
c => c.HasOne(x => x.Car)
.WithMany()
.HasForeignKey(x => x.CarId),
c => c.HasOne(c => c.Clown)
.WithMany()
.HasForeignKey(c => c.ClownId)
);
then
var newClown = new Clown();
var clownCar = new ClownCar();
clownCar.CarId = carId;
clownCar.Clown = newClown;
db.Set<ClownCar>().Add(clownCar);
db.SaveChanges();
I am trying to use EF Code First on an existing database. I first tried some of the reverse-engineering tools, but I ran into problems with that, so at the moment I am trying to hand-code some of the classes. I am having some trouble getting some of the foreign key relationships set up. Consider two tables. The first is called LocaleValueLookup:
public class LocaleValueLookup
{
public int Id { get; set; }
public Guid Guid { get; set; }
}
This table provides an Id for multi-language text held in a different table (that other table is not important for the purposes of this question). The second table is called SectionType, and it has an optional FK to LocaleValueLookup:
public class SectionType
{
public int EnumId { get; set; }
public string Name { get; set; }
public int? DefaultSectionTextLocaleValueLookupId { get; set; }
// Navigation property
public LocaleValueLookup DefaultSectionTextLocaleValueLookup { get; set; }
}
I have tried various things, including adding a [ForeignKey] attribute to the SectionType.LocaleValueLookup property, and various incantations in the DbContext.OnModelCreating() override, but when I query the DbContext, I can't get the DefaultSectionTextLocaleValueLookup to be anything but null. I can retrieve other objects from the context just fine, and I have verified that DefaultSectionTextLocaleValueLookupId is not null at least some of the time.
My OnModelBuilding() contains the following:
modelBuilder.Entity<LocaleValueLookup>()
.ToTable("LocaleValueLookup")
.HasKey(lvl => lvl.Id);
modelBuilder.Entity<LocaleValueLookup>().Property(lvl => lvl.Id).IsRequired();
modelBuilder.Entity<SectionType>()
.ToTable("SectionType")
.HasKey(st => st.EnumId);
modelBuilder.Entity<SectionType>().Property(st => st.EnumId).IsRequired();
A couple of other points:
I would prefer not to have a SectionType collection on the LocaleValueLookup object. LocaleValueLookup is a low-level class that a lot of other classes depend on, so to include a collection property on LocaleValueLookup for every other class that references it will make for an unwieldy class with a lot of collections on it that I don't need from a domain perspective.
I would prefer to do the mapping setup in DbContext.OnModelCreating() rather than using attributes on my model objects
Any help would be greatly appreciated!
It looks like your foreign key is nullable so that means an optional -> many relationship.
Could you try something like this:
modelBuilder.Entity<SectionType>()
.HasOptional(opt => opt.DefaultSectionTextLocaleValueLookup)
.WithMany() // no navigation on the other side
.HasForeignKey(fk => fk.DefaultSectionTextLocaleValueLookupId);
If you were to write a query like this you should get a value back:
var query =
from st in db.SectionTypes
where st.EnumId == 12345
select new
{
SectionType = st,
LocaleValue = st.DefaultSectionTextLocaleValueLookup
};
It will only be non-null if the foreign key has a value, obviously.
I have the following model in my model:
Patient
Vendor
Organization
each of these entities needs Addresses.
The Address basically looks like the following
Address
AddressTypeId // with Navigation Property/Association to AddressType
EntityKey // indicates the PK Id of the entity this address is for
AddressType
EntityId // indicates the entity type this address type corresponds to (Patient or Vendor)
// This should be on the AddressType, not the Address, since we need a way of knowing what kind of AddressTypes are available to create for new addresses for Patients, Vendors, and Organizations
//...that is Patients support AddressType X, Vendors support AddressType Y, etc.
I want to create an association for Patient, Vendor, and Organization on the EntityKey property on Address - each with a filter constraint that the Address's AddressType.EntityId is the matching EntityId for that entity (1 for Patient, 2 for Vendor, 3 for Address).
What is the best way of doing this? Most ORM's on the market support this kind of scenario....and it's certainly a very common one.
NOTE: I don't want to create PatientAddress/PatientAddressType, VendorAddress/VendorAddressType, and OrganizationAddress/OrganizationAddress type derived entities. It severely clutters the model and makes it basically incomprehensible.
Right now I'm solving this by doing explicit joins in my LINQ queries:
const int patientTypeEntityId = 1;
var query = from p in repository.Patients
let addresses = repository.Addresses.Where(a =>
a.EntityKey == p.Id & a.AddressType.EntityId == patientTypeEntityId)
select new { Patient = p, Addresses = a }
but I don't want to continue having to do this.
If I understand correctly you want to have an address collection in your Patient, Vendor, etc...
public class Patient
{
public int Id { get; set; }
public ICollection<Address> Addresses { get; set; }
}
public class Vendor
{
public int Id { get; set; }
public ICollection<Address> Addresses { get; set; }
}
public class Address
{
public int Id { get; set; }
//public int EntityKey { get; set; }
public AddressType AddressType { get; set; }
}
... and somehow tell EF that Patient.Addresses only gets populated with addresses of address type "Patient".
I think that is not possible for several reasons:
If you don't expose the foreign key in Address (no EntityKey property there) you have to tell EF the key in the mapping (otherwise it would create/assume two different FK columns):
modelBuilder.Entity<Patient>()
.HasMany(p => p.PVAddresses)
.WithRequired()
.Map(a => a.MapKey("EntityKey"));
modelBuilder.Entity<Vendor>()
.HasMany(p => p.PVAddresses)
.WithRequired()
.Map(a => a.MapKey("EntityKey"));
This throws an exception due to the duplicate "EntityKey" column for two different relationships.
Next thing we could try is to expose the foreign key as property in Address (EntityKey property is there) and then use this mapping:
modelBuilder.Entity<Patient>()
.HasMany(p => p.PVAddresses)
.WithRequired()
.HasForeignKey(a => a.EntityKey);
modelBuilder.Entity<Vendor>()
.HasMany(p => p.PVAddresses)
.WithRequired()
.HasForeignKey(a => a.EntityKey);
This (surprisingly) doesn't throw an exception but creates two FK constraints in the database between Patient-Address and Vendor-Address with the same FK column EntityKey. For your model, I think, this doesn't make sense because it would require that always a Patient and a Vendor with the same PK exists if you have an address with some EntityKey. So, you would have to remove these FK constraints in the DB manually (which feels very hacky to me).
And the last thing is that you cannot specify a filter for lazy and eager loading of navigation properties. The Addresses collection would always get populated with the addresses which have the same EntityKey as the PK of Patient or Vendor respectively. You can apply a filter though with explicite loading:
var patient = context.Patients.Single(p => p.Id == 1);
context.Entry(patient).Collection(p => p.Addresses).Query()
.Where(a => a.Addresstype.EntityId == patientTypeEntityId)
.Load();
But you would have to ensure that you never use lazy or eager loading for the Addresses collection. So, this is not really a solution and we should forget it immediately.
The ugliest point for me is that you cannot have FK constraints on the EntityKey. In other words: The DB allows to have an EntityKey = 1 with no referenced Patient or Vendor with that PK (because somehow the patient 1 and vendor 1 have been deleted, for example).
For this reason alone I would prefer the solution shown by #Akash - aside from the fact that it is probably the only working and clean solution with EF at all.
I'm pretty sure it's something regarding hidden conventions, but I always get an error when trying to map a many-to-many relation to an existing database.
Here is the simplest example:
[Table("ALRole", SchemaName = "AL")]
public class Role
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
[Table("ALUser", SchemaName = "AL")]
public class User
{
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<Role> Roles { get; set; }
}
I got the usual three tables in the db: the first two are obvious, and the third is created with this script:
CREATE TABLE AL.ALUsersRoles
(
RoleID int NOT NULL,
UserID int NOT NULL,
CONSTRAINT PK_ALUserRole PRIMARY KEY(RoleID, UserID),
CONSTRAINT FK_ALUserRole_RoleID FOREIGN KEY(RoleID) REFERENCES AL.ALRole(ID),
CONSTRAINT FK_ALUserRole_UserID FOREIGN KEY(UserID) REFERENCES AL.ALUser(ID)
)
Now I try to map the many-to-many relation, with code like this:
// ...I'm in the EntityTypeConfiguration-derived class (User)
HasMany(u => u.Roles)
.WithMany(r => r.Users)
.Map(m =>
{
m.MapLeftKey(u => u.ID, "UserID");
m.MapRightKey(r => r.ID, "RoleID");
ToTable("ALUsersRoles", "AL");
});
I tried all the possibile combinations and variations in this code, but I always get the error:
{"Invalid column name 'Name'.\r\nInvalid ...and so on...
So I think it must be the table that is not created correctly.
Any ideas?
Thanks in advance
Andrea
P.S.: I stripped down some of my code, so maybe there can be some small typo...
well, this works for me same as OP.
//many-to-many between *Users -> Web_User_Rol <- Web_Rol*
modelBuilder.Entity<Users>()
.HasMany(u => u.Web_Rols).WithMany(r => r.Users)
.Map(t=>t.MapLeftKey("user_id")
.MapRightKey("roleID")
.ToTable("Web_User_Rol"));
There is nothing wrong with your object model or fluent API code. I've used them and they perfectly created the desired schema without any exception. I think your problem comes from another entity (perhaps one with a "Name" property) unrelated to what you've shown here. To find that, drop (or rename) your existing database and let Code First create one for you and then compare the 2 databases and see what is different.