create linked entities in EF based on automatically generated id - entity-framework

Challenge in EF6:
how to check Id of resulting row in the database after running this (esentially adding an entity record):
repository.Add(myEntity1);
...and use that id to add the second entity which has property X = to the id of the first entity?
use that id to add the second entity which has property X = to the id of the first entity?
repository.Add(myEntity2);
Right now there is no linkage between entity 1 and entity 2 because i don;t know how to save the id (automatically generated by ef) after first add
... and preserve it for adding it as a fk in the second entity?
Thanks a lot

You could try this following after your call to SaveChanges:
myEntity2.X = myEntity1.Id;
Then call SaveChanges again. This doesn't really utilise the power of Entity Framework, however, which is in managing relationships between entities. If your class was defined something like this:
public class MyEntity
{
[Key]
public int Id { get; set; }
[ForeignKey(nameof(RelatedEntity))]
public int RelatedEntityId { get; set; }
public MyEntity RelatedEntity { get; set; }
}
You could add your entities something like the following, and the Id/foreign key matching would be handled for you after calling SaveChanges:
myEntity1.RelatedEntity = myEntity2;
This is a fairly general solution, so if you'd like something more specific then you will need to include more details in your question.
You can read more about configuring Entity Framework relationships here.

Related

Simple contract for use with FromSql()

With its recent improvements, I'm looking to move from Dapper back to EF (Core).
The majority of our code currently uses the standard patterns of mapping entities to tables, however we'd also like to be able to make simple ad-hoc queries that map to a simple POCO.
For example, say I have a SQL statement which returns a result set of strings. I created a class as follows...
public class SimpleStringDTO
{
public string Result { get; set; }
}
.. and called it as such.
public DbSet<SimpleStringDTO> SingleStringResults { get; set; }
public IQueryable<SimpleStringDTO> Names()
{
var sql = $"select name [result] from names";
var result = this.SingleStringResults.FromSql(sql);
return result;
}
My thoughts are that I could use the same DBSet and POCO for other simple queries to other tables.
When I execute it, EF throws an error "The entity type 'SimpleStringDTO' requires a primary key to be defined.".
Do I really need to define another field as a PK? There'll be cases where there isn't a PK defined. I just want something simple and flexible. Ideally, I'd rather not define a DBSet or POCO at all, just return the results straight to an IEnumerable<string>.
Can someone please point me towards best practises here?
While I wait for EF Core 2.1 I've ended up adding a fake key to my model
[Key]
public Guid Id { get; set; }
and then returning a fake Guid from SQL.
var sql = $"select newid(), name [result] from names";

How to have two different navigation properties of the same another entity type using attributes

I have two entities:
//The master table/entity
[TABLE("POSITIONS")]
public class Position{
[Key,Column("POSITIONID")]
public int PositionId{get;set;}
[Column("POSITIONNAME")]
public string PositionName{get;set;}
}
//The detail table/entity
[TABLE("SLAVE_POSITIONS")]
public class SlavePosition{
[Key,Column("MASTERPOSID",Order=0)]
public int MasterPosId{get;set;}
[KEY,Column("SLAVEPOSID",Order=1)]
public string SlavePosId{get;set;}
[ForeignKey("MasterPosId")]
public virtual Position MasterPosition {get;set;}
[ForeignKey("SlavePosId")]
public virtual Position SlavePosition {get;set;}
}
In the SlavePosition, as you can see, there two columns over which this entity is in FK relationship with Position. This layout works great. Now I also need to add this collection property to Position entity:
public virtual ICollection<SlavePosition> SlavePositions{get;set;}
But apparently EF gets confused and I get {"ORA-00904: \"Extent1\".\"Position_PositionId\": invalid identifier"} error.
If I declare it like this:
[ForeignKey("SlavePositionId")]
public virtual ICollection<SlavePosition> SlavePositions { get; set; }
and then fetch a Position with PositionId =1 like this:
Position pos= dbContext.Positions.SingleOrDefault(x=>x.PositionId==1);
I get no error, but I get SlavePOsitions count 0, when it should be 5 because in the database I have 5 rows in the detail table. I am able to confirm this by running the below code:
IEnumerable<SlavePositions> slavePositions= dbcontext.SlavePositions.Where(x=>x.MasterPositionId==1);
I get five SlavePosition.
What should be the correct attribute for this collection property?
I finally figured it out. My mistake was in the referenced dependent property name. Instead of SlavePositionId I should put MasterPositionId.
This makes sense, because the Position entity acts as a master table and in real world Foreign Key relationship is set up on detail tables, not master ones. As there's no property in the dependent entity that has the same name as the PK in the master entity and there're more than one properties that have Foreignkey to the same master entity, EF needs more information.By specifying ForeignKey("MasterPositionId") to the ICollection navigation property, I instruct EF that Dependent end point property should be considered MasterPositionId. So I changed this
[ForeignKey("SlavePositionId")]
public virtual ICollection<SlavePosition> SlavePositions { get; set; }
to this
[ForeignKey("MasterPositionId")]
public virtual ICollection<SlavePosition> SlavePositions { get; set; }
In fact the former one itself is not wrong either, it just does not fit in this situation. But if I wanted to have a collection for MasterPositions this would fit perfectly fine.

How can I prevent EF7 from eagerly fixing up navigation properties?

I have an issue using EF7 in a web application with which I could use some help. I'm currently using EF7 RC1.
Here are some models that illustrate my problem.
Contact
public class Contact
{
public Guid Id { get; set; }
public string Desc { get; set; }
public ContactType ContactType { get; set; }
}
ContactType
public class ContactType
{
public Guid Id { get; set; }
public string Desc { get; set; }
public ICollection<Contact> Contacts { get; set; }
}
These models are related via Fluent API like this:
modelBuilder.Entity<Contact>(entity => {
// abridged for clarity
entity
.HasOne(c => c.ContactType)
.WithMany(ct => ct.Contacts)
.IsRequired();
});
My needs are to be able to retrieve a collection of Contact entities from the database with their ContactType property loaded. EF makes this quite easy:
using(var context = new MyDbContext()) {
var contacts = await context
.Contacts
.Include(c => c.ContactTypes)
.Where(/* some search criteria */)
.ToListAsync();
}
The issue is that in loading the ContactType properties of the Contact entities (which happens due to the call to .Include() in the query), EF also helpfully loads the Contacts property of each ContactType entity, resulting in an infinite chain of Contacts pointing at ContactTypes and ContactTypes pointing at Contacts. I understand why this is the default behavior and that it's helpful in many cases, but my needs are to serialize these entities to JSON and send them down to the client - it's a read-only situation.
My desired behavior is for EF to return a collection of Contacts with loaded (non-null) ContactType properties that have their Contacts property set to null. Is this something EF can do? Is there any way to end up with the object graph I want short of manually nulling out properties I don't want populated?
Things I've tried:
Appending .AsNoTracking() to the EF query (which doesn't seem to stop
the Contacts property of the ContactType entity from being loaded)
Telling Json.NET not to serialize infinite reference loops (which is
required to avoid infinite recursion during serialization, but still
results in a lot of extra data being serialized)
You can't avoid EF to load ContactType.Contacts collection, as it's not actually loading it but filling the collection with the loaded Contact instances.
This is why using AsNoTracking has no efect, because is not a problem of lazy loading nor ChangeTracker.
You have three possible solutions:
Use Json.NET ReferenceLoopHandling = ReferenceLoopHandling.Ignore, but as you stated it will generate lot of unnecesary data, as you will get the collection of Contacts for every ContactType
Use [JsonIgnore] attribute on ContactType.Contacts so it will be ignored by the serializer. But it will ignore it always, I don't know if you need it in other situations
Define a DTO, use something like Automapper to map your data in it (without Contacts collection) and serialize it
I would prefer the 3rd option as I don't like sending domain model objects to the client, and it avoid adding attributes to domain model not related with domain.
I have same question Entity Framework 7 Core disable auto loading
I add AsNoTracking()
IQueryable<ScheduleModel> q = _db.Schedules;
q = q.AsNoTracking();
q = q.Include(x => x.ElementItem);
q = q.Include(x => x.ScheduleHours);
Properties not populate automatic now.

EF6: Table Splitting Not Working

I am trying to create an EF6 database where two tables, Addresses and Visits, share the same values as primary keys. Visits, conceptually, is an extension of Addresses. I'm splitting the tables because most of the records in Addresses don't require the fields contained in Visits.
I'm using the code first approach. Here's the relevant code for the Addresses:
public class Address
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[ForeignKey( "ID" )]
public virtual Visit Visit { get; set; }
and for Visits:
public class Visit
{
[Key]
[DatabaseGenerated( DatabaseGeneratedOption.Identity )]
public int ID { get; set; }
[ForeignKey("ID")]
public virtual Address Address { get; set; }
Based on my research, I also needed to include the following in my datacontext's OnModelCreating method:
modelBuilder.Entity<Visit>()
.HasOptional( v => v.Address )
.WithRequired();
Unfortunately, this doesn't work. I can update the database alright, after eliminating scaffolding calls to drop the primary index from Addresses (probably because the add-migration code thinks the primary key is "merely" a foreign key field). But when I run the application I get the following error:
Invalid column name 'Address_ID'.
Invalid column name 'Address_ID'.
From my limited experience with EF6 this looks like someplace deep inside the framework it's expecting there to be fields named 'Address_ID', probably in the Visits table (based on the 'table name'_'field name' naming structure I've seen for other implicitly added fields).
Is what I'm trying to do possible? If so, what am I missing in the configuration?
Additional Info
In trying out bubi's proposed solution, which unfortunately still generates the same error, that I could eliminate the OnModelCreating code and still get functional migration code generated.
Resolution
I finally did what I should've done earlier, which is examine the actual T-SQL code generated by the query which was blowing up. It turns out the problem was not in the Visit/Address linkage, but in a completely separate relationship involving another table. Apparently, somewhere along the way I did something to cause EF to think that other table (Voters) had an Address_ID foreign key field. In reality, the Address/Voter relationship should've been, and originally was, tied to a Voter.AddressID field.
Rather than try to unwind a large number of migrations I opted to blow away the database, blow away the migrations and start from scratch. After recreating the database -- but using bubi's suggestion -- I reloaded the data from backup and, voila, I was back in business.
For the sake of completeness, here's the code I ended up having to put into the OnModelCreating method call to get the Address/Visit relationship to work correctly:
modelBuilder.Entity<Visit>()
.HasRequired( v => v.Address )
.WithRequiredDependent( a => a.Visit );
modelBuilder.Entity<Address>()
.HasRequired( a => a.Visit )
.WithRequiredPrincipal( v => v.Address );
I am a little confused about why I have to use HasRequired in order to be able to use WithRequiredPrincipal/WithRequiredDependent, since not every entry in the Address table has an entry in the Visit table. That would seem to be "optional", not "required". But it appears to work, and maybe the "required" part is just internal to EF's model of the database, not the database itself.
There are 2 problems in the model:
- Only one of the Keys can be autonumbering, the other must get the same Id (this independently by EF).
- A mapping problem.
This model should work.
public class Address
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Description { get; set; }
public virtual Visit Visit { get; set; }
}
public class Visit
{
public Visit()
{
Address = new Address();
}
[Key]
[ForeignKey("Address")]
public int Id { get; set; }
public string Description { get; set; }
public virtual Address Address { get; set; }
}
Example of use
var visit = new Visit
{
Description = "Visit",
Address = {Description = "AddressDescription"}
};
db.Visits.Add(visit);
db.SaveChanges();
In addition to what bubi mentioned, your modelBuilder statement contradicts the model in that it doesn't mention Address.Visit as the inverse property. So it thinks that the property represents a separate relationship and tries to create the Address_ID column for that relationship.
You need to have
modelBuilder.Entity<Visit>()
// from your description sounds like every Visit needs an Address
.HasRequired(v => v.Address )
// need to mention the inverse property here if you have one
.WithOptional(a => a.Visit);
...or just remove the statement completely since you're already using attributes, and EF should be able to figure it out by convention.

Relations between complex type not getting updated

So with entity framework I'm trying to update two existing entities.
There I've the main object something like:
public class MainObject
{
public string Name { get; set; }
public virtual SmallObject Part { get; set;}
}
public class SmallObject
{
public string Name { get; set; }
}
In the repository I first check if the SmallObject already exists in the database by:
MainObject.Part = (from s in repoSmallObject.GetAll()
where s.name == MainObject.Part.Name
select s).FirstOrDefault();
Then finally I call the update method in my GenericRepository
repoMainObject.Update(MainObject)
which is defined as a generic repository method:
dbSet.Attach(entity)
context.Entry(entity).State = EntityState.Modified;
context.SaveChanges();
But the relationship doesn't get updated. Why is that? Both objects are attached to context not?
*Edit: The two repo's are injected with the same Context.
And strangely enough the Add method works and also updates the relationship.
When you set
context.Entry(entity).State = EntityState.Modified;
you need at least to set the state after and before updates (i.e. context.Entry(mainObject).CurrentValues and OriginalValues) so EF can build the right UPDATE query (with right WHERE clause).
It works if you set
context.Entry(entity).State = EntityState.Added;
because EF needs just to generate an INSERT query.
I don't know exactly why you need it but usually I prefer to attach the object to the DbSet and modify the properties so EF handles various states.
dbSet.Attach(MainObject)
MainObject.Part = (from s in repoSmallObject.GetAll()
where s.name == MainObject.Part.Name
select s).FirstOrDefault();
(In your case does not work because MainObject.Part.Name does not change)
The attached object should have the same values of the database otherwise you have a concurrency exception.
BTW, why you don't read the old object (MainObject) from the DB than work on it???