How To Change Navigation Property Names Into Meaningful Names
Asked Answered
M

4

7

I'm using Database First with Entity Framework 5. We have two tables (massively simplified):

  • Addresses
    • Street
    • Town (etc.)
  • Customers
    • Name
    • BillingAddress
    • DeliveryAddress
    • AltDeliveryAddress

When we use Visual Studio to import the database into EF ("Update Model from Database"), we end up with code like this:

Customer myCustomer;
var a = myCustomer.Address;
var b = myCustomer.Address1;
var c = myCustomer.Address2;

What I want obviously is something like this:

var a = myCustomer.BillingAddress;
var z = myCustomer.BillingAddress.Street; // etc.

I could just edit the model in the designer, changing the Navigation Property to give me the correct name. However this isn't a viable solution because we rebuild the model every time we make changes to the database.

One option I've tried is creating a partial class like this (code copied from existing MyModel.Designer.cs with just the property name changed):

public partial class Customer : EntityObject
{
    [EdmRelationshipNavigationPropertyAttribute("MyModel", "FK_Customers_Addresses_BillingAddress", "Address")]
    public Address BillingAddress
    {
        get {
            return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<LookupItem>("MyModel.FK_Customers_Addresses_BillingAddress", "Address").Value;
        }
        set {
            ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference<LookupItem>("MyModel.FK_Customers_Addresses_BillingAddress", "Address").Value = value;
        }
    }
}

However when I run this, I get the following error:

The number of members in the conceptual type 'MyModel.Customer' does not match with the number of members on the object side type 'MyNamespace.DataModel.Customer'. Make sure the number of members are the same.

I've tried using the [NotMapped()] attribute, but that hasn't made any difference. If I remove the [EdmRelationshipNavigationPropertyAttribute...] attribute then the Linq complains with the following error:

The specified type member 'BillingAddress' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.

Is there any other way to achieve meaningful names in the Customers object?

Here's what I want at the end:

var j = myCustomer.BillingAddress;
var k = myCustomer.BillingAddress.Street;
var l = myCustomer.BillingAddress.Town; // etc.
Misconstruction answered 23/11, 2012 at 12:14 Comment(0)
S
2

You can add properties to your partial class that will be accessors to properties Address1, Address2 etc.

Example:

public partial class Customer : EntityObject
{
    public Address BillingAddress
    {
         get 
         {
             return this.Address;
         }
         set 
         {
              this.Address = value;
         }
    }
}

Update: It will not work with linq to entities. I'm afraid this is limitation of database first approach. It is one of the causes for us to use EF Code First. In Code First you have full control on entity mapping. (Code First means mapping in code, it is not mean that your db will be generated from this code if you don't want it).

Sluiter answered 23/11, 2012 at 12:21 Comment(4)
That works if you can be sure of the mapping between EF's generated names and the real database names. i.e. if .Address2 is always AltDeliveryAddress then that's ok, but it doesn't seem very safe.Misconstruction
Actually no, that doesn't work when I try to use it in LINQ. Here's my code: var customersInSeattle = cs.Where(c => c.DeliveryAddress.Town == "Seattle"); I get this error: The specified type member 'DeliveryAddress' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.Misconstruction
Yes, it's true... I'm afraid this is limitation of database first approach. It is one of the causes for us to use EF Code First. In Code First you have full control on entity mapping. (Code First means mapping in code, it is not mean that your db will be generated from this code if you don't want it).Sluiter
I'll award you the correct answer, since you've come closest to helping. We'll look at doing Code First in the next major version. Many thanks.Misconstruction
O
6

You probably don't have to worry about this. After changing the property name in the model designer EF will remember the custom naming. It won't be overwritten by subsequent updates.

Ocular answered 23/11, 2012 at 12:19 Comment(3)
Well it only works as long as you don't remove the entity and readd it to your model what can happen if you want to syncronize your model with the database. f.e. deleting a column in the database doesn't do the same in your model when refreshingNerta
Unfortunately the EF designer isn't that reliable. Often we just delete the entire model and recreate it, because trying to do updates breaks it. Therefore this option isn't available to us.Misconstruction
if I make changes to model, will it do the corresponding changes to existing database and code classes too? ... I really want that.Aerodyne
S
2

You can add properties to your partial class that will be accessors to properties Address1, Address2 etc.

Example:

public partial class Customer : EntityObject
{
    public Address BillingAddress
    {
         get 
         {
             return this.Address;
         }
         set 
         {
              this.Address = value;
         }
    }
}

Update: It will not work with linq to entities. I'm afraid this is limitation of database first approach. It is one of the causes for us to use EF Code First. In Code First you have full control on entity mapping. (Code First means mapping in code, it is not mean that your db will be generated from this code if you don't want it).

Sluiter answered 23/11, 2012 at 12:21 Comment(4)
That works if you can be sure of the mapping between EF's generated names and the real database names. i.e. if .Address2 is always AltDeliveryAddress then that's ok, but it doesn't seem very safe.Misconstruction
Actually no, that doesn't work when I try to use it in LINQ. Here's my code: var customersInSeattle = cs.Where(c => c.DeliveryAddress.Town == "Seattle"); I get this error: The specified type member 'DeliveryAddress' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.Misconstruction
Yes, it's true... I'm afraid this is limitation of database first approach. It is one of the causes for us to use EF Code First. In Code First you have full control on entity mapping. (Code First means mapping in code, it is not mean that your db will be generated from this code if you don't want it).Sluiter
I'll award you the correct answer, since you've come closest to helping. We'll look at doing Code First in the next major version. Many thanks.Misconstruction
L
0

I know this is a bit old, but it seems the easiest/best way to handle this situation is to leverage the EF5 power tool "reverse engineer code first."

http://msdn.microsoft.com/en-us/data/jj200620

It gives you all the advantages of code first mappings with the simplicity/convenience of database first object creation.

Lankford answered 4/6, 2013 at 13:48 Comment(0)
E
0

there is a solution described here:

Improve navigation property names when reverse engineering a database

You can include code generation templates(*.tt files) into your project. Modify them as it is described on link. Sample is here: https://github.com/markuspeter/EFPowerToolsTemplates (check branches for changes)

Then your naming can be generated with better names.

Emileeemili answered 12/8, 2015 at 6:57 Comment(1)
It's always recommended to include code snippets/most appropriate paragraph or two from the linked resource, even if only to prevent link-rotting.Calorimeter

© 2022 - 2024 — McMap. All rights reserved.