MartinHN
MartinHN

Reputation: 19782

Entity Framework Code First, foreign key/object should not save

I have three classes, like so:

[DataContract]
public class ApplicationDto : BusinessBase<int>
{
    /// <summary>
    /// Gets or sets the name.
    /// </summary>
    /// <value>The name.</value>
    [DataMember]
    public string Name { get; set; }

    /// <summary>
    /// Gets or sets the description.
    /// </summary>
    /// <value>The description.</value>
    [DataMember]
    public string Description { get; set; }

    /// <summary>
    /// Gets or sets the development startdate.
    /// </summary>
    /// <value>The development startdate.</value>
    [DataMember]
    public DateTime DevelopmentStartdate { get; set; }

    /// <summary>
    /// Gets or sets the launch date.
    /// </summary>
    /// <value>The launch date.</value>
    [DataMember]
    public DateTime LaunchDate { get; set; }
}

[DataContract]
public class CustomerDto : BusinessBase<int>
{
    /// <summary>
    /// Gets or sets the name of the user.
    /// </summary>
    /// <value>The name of the user.</value>
    [DataMember()]
    public string UserName { get; set; }

    /// <summary>
    /// Gets or sets the first name.
    /// </summary>
    /// <value>The first name.</value>
    [DataMember()]
    public string FirstName { get; set; }

    /// <summary>
    /// Gets or sets the last name.
    /// </summary>
    /// <value>The last name.</value>
    [DataMember()]
    public string LastName { get; set; }

    /// <summary>
    /// Gets or sets the name of the company.
    /// </summary>
    /// <value>The name of the company.</value>
    [DataMember()]
    public string CompanyName { get; set; }

    /// <summary>
    /// Gets or sets the phone.
    /// </summary>
    /// <value>The phone.</value>
    [DataMember()]
    public string Phone { get; set; }

    /// <summary>
    /// Gets or sets the email.
    /// </summary>
    /// <value>The email.</value>
    [DataMember()]
    public string Email { get; set; }

    /// <summary>
    /// Gets or sets the address1.
    /// </summary>
    /// <value>The address1.</value>
    [DataMember()]
    public string Address1 { get; set; }

    /// <summary>
    /// Gets or sets the address2.
    /// </summary>
    /// <value>The address2.</value>
    [DataMember()]
    public string Address2 { get; set; }

    /// <summary>
    /// Gets or sets the city.
    /// </summary>
    /// <value>The city name.</value>
    [DataMember()]
    public string City { get; set; }

    /// <summary>
    /// Gets or sets the state region.
    /// </summary>
    /// <value>The state region.</value>
    [DataMember()]
    public string StateRegion { get; set; }

    /// <summary>
    /// Gets or sets the zip code.
    /// </summary>
    /// <value>The zip code.</value>
    [DataMember()]
    public string ZipCode { get; set; }

    /// <summary>
    /// Gets or sets the country id.
    /// </summary>
    /// <value>The country id.</value>
    [DataMember()]
    public int CountryId { get; set; }

    /// <summary>
    /// Gets or sets the ean number.
    /// </summary>
    /// <value>The ean number.</value>
    [DataMember()]
    public string EanNumber { get; set; }

    /// <summary>
    /// Gets or sets the vat number.
    /// </summary>
    /// <value>The vat number.</value>
    [DataMember()]
    public string VatNumber { get; set; }

    /// <summary>
    /// Gets or sets the time zone id.
    /// </summary>
    /// <value>The time zone id.</value>
    [DataMember()]
    public string TimeZoneId { get; set; }
}

[DataContract]
public class ApplicationInstanceDto : BusinessBase<int>
{
    /// <summary>
    /// Gets or sets the customer id.
    /// </summary>
    /// <value>The customer id.</value>
    [DataMember]
    public int CustomerId { get; set; }

    /// <summary>
    /// Gets or sets the application id.
    /// </summary>
    /// <value>The application id.</value>
    [DataMember]
    public int ApplicationId { get; set; }

    /// <summary>
    /// Gets or sets the application.
    /// </summary>
    /// <value>The application.</value>
    [DataMember]
    public ApplicationDto Application { get; set; }

    /// <summary>
    /// Gets or sets the customer.
    /// </summary>
    /// <value>The customer.</value>
    [DataMember]
    public CustomerDto Customer { get; set; }

    /// <summary>
    /// Gets or sets the initial version id.
    /// </summary>
    /// <value>The initial version id.</value>
    [DataMember]
    public int InitialVersionId { get; set; }

    /// <summary>
    /// Gets or sets the current version id.
    /// </summary>
    /// <value>The current version id.</value>
    [DataMember]
    public int CurrentVersionId { get; set; }

    /// <summary>
    /// Gets or sets the name of the unique instance.
    /// </summary>
    /// <value>The name of the unique instance.</value>
    [DataMember]
    public string UniqueInstanceName { get; set; }
}

Let's say I have 2 applications present in my database, and several customers.

In my MVC Web App, I show the list of applications and click a link called 'Create Instance'.

I select the Customer this instance is for, and clicks save.

In EF Code First, by default it always saves the related objects - in this case the Application and the Customer.

Since I'm doing this using AJAX calls I'm only sending the Id of both the application and the customer back to my Controller. Looking at the database, that's all I need to create an ApplicationInstance.

I 'cheat' by doing this:

var appInstance = new ApplicationInstanceDto();
            appInstance.InitialVersionId = 1;
            appInstance.CurrentVersionId = 2;
            appInstance.ApplicationId = 1;
            appInstance.CustomerId = 1;
            appInstance.UniqueInstanceName = "test";

db.ApplicationInstances.Add(appInstance);
db.SaveChanges();

But of course, I get an exception from the database telling me that the name column in the Applications table does not allow null.

Is there any way I can cheat even more, and avoid the entire insert of related objects?

I have foreign key relationships and such set up correctly.

Upvotes: 2

Views: 7924

Answers (1)

Morteza Manavi
Morteza Manavi

Reputation: 33206

I recommend changing your associations to Foreign Key Associations instead of being independent (which are now):

public class Application
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class ApplicationInstance
{
    public int Id { get; set; }
    public int ApplicationId { get; set; }
    public int CustomerId { get; set; }

    public Application Application { get; set; }
    public Customer Customer { get; set; }
}

And then you can save like this:

var appInstance = new ApplicationInstance();
appInstance.CustomerId = customerId;
appInstance.ApplicationId = applicationId;

db.ApplicationInstances.Add(appInstance);
db.SaveChanges();

Update:

You can use ForeignKeyAttribute data annotation to relate FK columns to their navigation properties:

public class ApplicationDto
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class CustomerDto
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class ApplicationInstanceDto
{
    public int Id { get; set; }
    public int ApplicationId { get; set; }
    public int CustomerId { get; set; }

    [ForeignKey("ApplicationId")]
    public ApplicationDto Application { get; set; }

    [ForeignKey("CustomerId")]
    public CustomerDto Customer { get; set; }
} 

Upvotes: 5

Related Questions