8

I have the next enitities:

public class MyEntity
{
    public virtual int Id { get; set; }
    public virtual MySecondEntity SecondEntity { get; set; }
}
public class MySecondEntity
{
    public virtual int Id { get; set; }
    ...
    some properties here
    ...
}

I don't want to create MySecondEntityID property in order to have clean model.

I need to set myEntity.MySecondEntity by its Id, but I don't want to request MySecondEntity from DB.

Is it possible to do it without creating MyEntity.MySecondEntityID property and without loading the whole MySecondEntity object or even without any db requests?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
Alexander Vasilyev
  • 1,273
  • 2
  • 15
  • 22

5 Answers5

9

It is possible to create the MySecondEntity object manually, and attach it to the context as an unchanged object.

var secondEntity = new MySecondEntity();
secondEntity.Id = id;
context.MySecondEntities.Attach(secondEntity);
entity.SecondEntity = secondEntity;

To keep it simple, I have ignored the possibility that a MySecondEntity object with the same key already exists in the context. To make that work, you should check the local entities (for example by searching context.MySecondEntities.Local) and re-use an entity if you find it in there.

Note: EF won't have any way of knowing that the other properties of secondEntity, which are left at their default values, don't correspond to what's in the database. For what you're doing now, that doesn't matter, but it may matter at a later stage.

  • that's so hacky. Why not just make the SecondEntityId nullable? – NullVoxPopuli Feb 04 '16 at 12:22
  • 1
    @NullVoxPopuli The OP here doesn't want to have a `SecondEntityId` property at all, but even if the OP did want that, how would making it nullable help? Setting `SecondEntityId` should work with non-nullable FK properties just as well as with nullable ones. –  Feb 04 '16 at 12:57
  • I thought that with a nullable property, you don't need to set it at all? – NullVoxPopuli Feb 04 '16 at 13:04
  • @NullVoxPopuli That's not really wrong, but it shows that we're talking about two totally different things. Take another look at the question, and try to understand what the OP is after. If you then still feel that there is a simpler way to achieve that, you're more than welcome to post it as an answer. –  Feb 04 '16 at 13:25
2

For entity Framework 5 or later you can add an Id next to a navigational property, which will be auto-populated when the entity is loaded from the db.

public class MyEntity 
{
    public int Id { get; set;} 

    public int MySecondEntityId { get; set;} 
    public virtual MySecondEntity MySecondEntity { get; set;} 
} 

Setting MySecondEntityId is enough to create the relationship (after saving). This way you don't have to load the actual entity from db.

If you have a nullable foreign key, the making the navigational Id nullable is enough.

public int? MySecondEntityId { get; set;} 
Fredrik Ljung
  • 1,445
  • 13
  • 28
  • But I don't want to have MySecondEntityId in my model! :) – Alexander Vasilyev Feb 06 '14 at 06:14
  • @AlexanderVasilyev: You can't not to have. However you can make it internal or private so it won't be exposed outside. – abatishchev Feb 06 '14 at 06:22
  • @abatishchev: Why I cannot not to have? Today I have a model without ForeignID properties. The only problem I stuck is setting it by Id. I have to load foreign object from DB and then set. But at the same time it is an advantage because I can theck existence of foreign object in DB before exception on SaveChanges() and answer with appropriate message to a client. – Alexander Vasilyev Feb 06 '14 at 06:27
  • You can still perform that check when you feel that is necessary, but at least in my own code I come across a lot of situations where I have a known Id and there is no need for an extra trip to the db. Adding the foreign key gives you more flexibility, whether you add it as a public property or a private as suggest by abatishchev. – Fredrik Ljung Feb 06 '14 at 06:35
  • A consideration here is that supplying a raw Id is error-prone when that record doesn't exist, and so I kinda feel like (dependent on the situation) that the call to the DB to check if it exists rules out the supposed advantage of not having to retrieve a user object. That, or simply letting the key ref exception happen and catching it. – Zimano Jul 04 '22 at 13:54
2

Use a mutator method to modify a protected property for the ID:

public class MyEntity
{
    public virtual int Id { get; set; }
    public virtual MySecondEntity SecondEntity { get; set; }

    [ForeignKey( "SecondEntity" )]
    protected virtual int? MySecondEntityId { get; set; }

    public void SetSecondEntityId( int? id )
    {
        MySecondEntityId = id;
    }
}
Moho
  • 15,457
  • 1
  • 30
  • 31
0

If you want to get the MyEntity object from your database without initializing the MySecondEntity object, you can turn off lazy loading.

Method one

Turn of lazy loading on your context.

Method two

Remove the virtual keyword from your entity:

public class MyEntity
{
    public virtual int Id { get; set; }
    public MySecondEntity SecondEntity { get; set; }
}

This will disable lazy loading for SecondEntity. More info.

Community
  • 1
  • 1
Loetn
  • 3,832
  • 25
  • 41
0

Finally found a solution for this problem of keeping our models as clean as possible: Shadow foreign key.

I had a bit more complex case and got it working.

Model


// main class that is aware of the other
public sealed class Foo
{
    public int Id { get; set; }

    public IEnumerable<FooBar> FooBars { get; set; }

    // ... other props
}

// class that associates Foos and Bars with some extra props (not tied to Foos or Bars specifically). Goal here is to not have any ID props
public sealed class FooBar
{
    public Foo Foo { get; set; }

    public Bar Bar { get; set; }

    public bool Enabled { get; set; }
}

// Bar doesn't even know Foo exists
public sealed class Bar
{
    public int Id { get; set; }

    // ... other props
}

In my case I have a many to many relationship, so the primary key of FooBar is a composite key of the two main table IDs.

OnModelCreating:

modelBuilder.Entity<FooBar>(b =>
{
    const string FooId = "FooId";
    const string BarId = "BarId";
    b.Property<int>(FooId);
    b.Property<int>(BarId);

    b.HasKey(FooId, BarId);
});

Conventions that care of the rest.

André Mantas
  • 482
  • 4
  • 13