3

I have an Address object that has a City property. When creating a brand new Address to be inserted via EF6, I fill all required basic Address properties (address line 1, postal code, etc), but I don't need a fully hydrated City instance so long as it has the ID like so:

address.City = new City { Id = 1 };

When I attempt to insert my Address, it also attempts to do validation on the City's properties, but I don't want to do any CRUD on City since all I need is its ID.

I found the below question that introduced me to detaching entries from the DbContext so that EF does not attempt to do CRUD on said objects:

How do I stop Entity Framework from trying to save/insert child objects?

What seems to be happening when I detach the City instance is that it also nulls it out, so my Address has a null City property. This is a problem because City is also required, so a DbEntityValidationException is thrown saying "The City field is required".

I am new to EF, so perhaps the way I am going about all of this is just wrong to begin with.

Edit By request, here's all my code:

Building my Address entity in my client before passing it to WebApi endpoint:

var user = new AppUser { Id = 1 };
var address = new Address
{
    City = new City { Id = 277 },
    Line1 = "123 whatever ln",
    PostalCode = "01233",
    CreatedBy = user,
    ModifiedBy = user,
    CreatedOn = DateTime.Today,
    ModifiedOn = DateTime.Today
};

In my ASP.NET app, I create an array of instances I want to detach from the context:

Detached = new object[] {
    value.Principle.ModifiedBy,
    value.Principle.CreatedBy,
    value.Principle.City
};

Just before the save, I detach all instances in the array:

foreach (var d in DetachedObjects)
{
    dbContext.Entry(d).State = EntityState.Detached;
}
dbContext.SaveChanges();

What I thought was happening with detaching properties was that it was simply telling EF not to do any CRUD on them, but I didn't want it to null them out because I want the parent/principle entity to have the ID for its FK.

Here are my Address and City classes:

[DebuggerDisplay("{Line1}")]
public class Address : CisEntity
{
    [MaxLength(200)]
    [Required]
    public string Line1 { get; set; }

    [MaxLength(200)]
    public string Line2 { get; set; }

    [Required]
    public City City { get; set; }

    [MaxLength(25)]
    public string PostalCode { get; set; }
}

[DebuggerDisplay("{Name}, {Province.Name}, {Province.Country.Name}")]
public class City : CisEntity, IEntityName
{
    [Required]
    public Province Province { get; set; }

    [MaxLength(100)]
    public string Name { get; set; }
}
Community
  • 1
  • 1
oscilatingcretin
  • 10,457
  • 39
  • 119
  • 206
  • can you put all your `models` and `context` related code ? – Sampath Sep 28 '16 at 13:05
  • I'd say you are going about this wrong. If city is required, then you MUST have a city to insert an address because city is REQUIRED. If you don't feel that having a city is necessary, then don't required it. At the very least, you should post your entities so we can have a look. – Gavin Sep 28 '16 at 13:10
  • @Gavin Added code. I understand that if City is required, it should have an instance. The problem is that detaching City actually nulls it out which is not my intent. I want to keep the City instance solely for its ID, but I do not want EF to perform any CRUD on it. – oscilatingcretin Sep 28 '16 at 13:17
  • how can you generate a `City` `ID` without inserting it ? – Sampath Sep 28 '16 at 13:21
  • If my answer doesn't take care of your issue, please give is some more code we have a better idea of what you're trying to do, and I will update my answer accordingly. – Gavin Sep 28 '16 at 13:29
  • @Sampath The assumption is that I already have the City ID and I don't want to make a trip to the database to get a fully hydrated entity. – oscilatingcretin Sep 28 '16 at 13:40

2 Answers2

0

If you don't want the City to be required when performing CRUD, remove the required attribute. If you truly need a City for your Address in your program, but not in the database, then do as you are and null out the City piece before performing CRUD on Address. Otherwise it will be inserted. You should go check out what your tables look like in your database. EF will be tracking these in separate tables, and the City column on Address will be a foreign key. If you decorate your City property on Address with the required attribute, it means that the column is non-nullable. This means that in the database this column must contain a foreign key to a record in the City table, hence a City must exist.

Gavin
  • 4,365
  • 1
  • 18
  • 27
  • I understand all of this. What I ultimately want to do is provide my Address object with nothing more than the City's ID. I do not need a fully hydrated City object. – oscilatingcretin Sep 28 '16 at 13:47
  • Then I would try adding `public int CityId { get; set; }` with the required attribute to Address and removing the required attribute from the City property. Then you'll have your Id without requiring a City. – Gavin Sep 28 '16 at 13:51
  • 1
    I've seen this in some EF code examples, but I don't like it (why add a CityId field when EF should be smart enough to know that it already exists as Id on the City object?) . It seems that there would be a way to say, "Hey, EF, require a City *instance*, but only make sure it has a key value for the sole purpose of a FK for Address". – oscilatingcretin Sep 28 '16 at 13:57
  • EF is smart enough to know that some of your columns in the City table are non-nullable, and that's why it's giving you this issue. If every column except Id were nullable, you wouldn't have this issue. – Gavin Sep 28 '16 at 14:04
  • FYI, the reason why I am trying to detach the City instance is because, if I don't, EF complains that the Province property on the City is null since it is also required. I am just trying to avoid the rabbit hole of hydrating every instance in the entire object graph just to add one record – oscilatingcretin Sep 28 '16 at 14:05
  • If you can have a city instance with the province property null, then it's not required. Yet, you are telling EF that it is required. – Gavin Sep 28 '16 at 14:06
0

I believe I understand your question and I'm answering 3 years later. LOL.

For the main entity, context.Addresses.Add(address);

for the related or child entities, context.Entry(address.City).State = EntityState.Modified;

Do this for every other related entity.

  • In my case, I don't want to insert child data.Meaning wants to insert only parent entity data and ask context to ignore child entity. I have tried both lines one by one but, didn't get any success. _dbContext.Entry(entity.City).State = Microsoft.EntityFrameworkCore.EntityState.Unchanged; OR _dbContext.Entry(entity.Shift).State = Microsoft.EntityFrameworkCore.EntityState.Detached; – Rahul Aug 23 '20 at 19:46