While I’ve been working with Version 1 of the Entity Framework I, amongst others, became very frustrated when trying to update foreign keys. I realise that this article is probably out of date now as EF 4.0 was released a couple of weeks ago, which allows you access to foreign keys. Never the less, there might be some struggling out there with version 1.
Scenario : I was trying to update or insert a record using the entity framework but there were two references pointing to the same entity.
I was aware that you can get the entity reference property set it to null then re-attach it as seen in figure 1.
- /// <summary>
- /// Saves the non network device.
- /// </summary>
- /// <param name=”nonNetworkDeviceDto”>The non network device dto.</param>
- public void SaveNonNetworkDevice(NonNetworkDeviceDto nonNetworkDeviceDto)
- {
- using (var context = new AssetNetworkEntities2())
- {
- var changedDevice = TransformationHelper.ConvertNonNetworkDeviceDtoToEntity(nonNetworkDeviceDto);
- if (!nonNetworkDeviceDto.DeviceId.Equals(-1))
- {
- var originalDevice =
- context.NonNetworkDevices.Include(“Status”).Include(“NonNetworkType”).FirstOrDefault(
- d => d.DeviceId.Equals(nonNetworkDeviceDto.DeviceId));
- context.ApplyAllReferencedPropertyChanges(originalDevice, changedDevice);
- context.ApplyCurrentValues(originalDevice.EntityKey.EntitySetName, changedDevice);
- }
- else
- {
- var maxNetworkDevice = context.NonNetworkDevices.OrderBy(“it.DeviceId DESC”).First();
- changedDevice.DeviceId = maxNetworkDevice.DeviceId + 1;
- var status = changedDevice.Status;
- var nonNetworkType = changedDevice.NonNetworkType;
- changedDevice.Status = null;
- changedDevice.NonNetworkType = null;
- context.AttachTo(“DeviceStatuses”, status);
- if (nonNetworkType != null)
- {
- context.AttachTo(“NonNetworkTypes”, nonNetworkType);
- }
- changedDevice.Status = status;
- changedDevice.NonNetworkType = nonNetworkType;
- context.AddToNonNetworkDevices(changedDevice);
- }
- context.SaveChanges();
- }
- }
However, if you have two fields referencing the same lookup for the foreign key this method fails.
In figure 1 on line 9 I call a static method of the my TransformationHelper class ConvertNonNetworkDeviceDtoToEntity. This class’s purpose is to convert DTO’s to an entity and can be seen in the code below in Figure 2
- /// <summary>
- /// Converts the non network device dto to entity.
- /// </summary>
- /// <param name=”nonNetworkDeviceDto”>The non network device dto.</param>
- /// <returns>An entity of type <see cref=”NonNetworkDevice”/></returns>
- internal static NonNetworkDevice ConvertNonNetworkDeviceDtoToEntity(NonNetworkDeviceDto nonNetworkDeviceDto)
- {
- var nonNetworkDevice = new NonNetworkDevice
- {
- DeviceId = nonNetworkDeviceDto.DeviceId,
- Description = nonNetworkDeviceDto.Description ?? string.Empty,
- LocationDescription = nonNetworkDeviceDto.LocationDescription ?? string.Empty,
- IPOctet1 = nonNetworkDeviceDto.IPOctet1 ?? string.Empty,
- IPOctet2 = nonNetworkDeviceDto.IPOctet2 ?? string.Empty,
- IPOctet3 = nonNetworkDeviceDto.IPOctet3 ?? string.Empty,
- IPOctet4 = nonNetworkDeviceDto.IPOctet4 ?? string.Empty,
- AssetNumber = nonNetworkDeviceDto.AssetNumber ?? string.Empty,
- Owner = nonNetworkDeviceDto.Owner ?? string.Empty,
- Status = new DeviceStatus { Status = nonNetworkDeviceDto.Status.Status },
- LocationCode = nonNetworkDeviceDto.LocationCode
- };
- if (nonNetworkDeviceDto.NetworkType != null)
- {
- nonNetworkDevice.NonNetworkType = new NonNetworkType
- {
- TypeID = nonNetworkDeviceDto.NetworkType.TypeId,
- TypeName = nonNetworkDeviceDto.NetworkType.TypeName
- };
- }
- return nonNetworkDevice;
- }
This conversion is fine for an update, but if you try to insert you have to go through the procedure shown in Figure 1 of assigning the reference properties to null, attaching then reassigning, because if you don’t EF tries to add new records to the reference tables, resulting in a Duplicate Primary key error.
So, I created some extension methods to help me with this task. Figure 3 shows the reworked static method,with Figure 4 and 5 showing the reference properties
- /// <summary>
- /// Converts to non network device entity.
- /// </summary>
- /// <param name=”context”>The context of the entity .</param>
- /// <param name=”nonNetworkDeviceDto”>The non network device dto.</param>
- /// <returns>A converted <see cref=”NonNetworkDevice”/></returns>
- internal static NonNetworkDevice ConvertToNonNetworkDeviceEntity(this AssetNetworkEntities2 context, NonNetworkDeviceDto nonNetworkDeviceDto)
- {
- var isNew = nonNetworkDeviceDto.DeviceId.Equals(-1);
- var nonNetworkDevice = new NonNetworkDevice
- {
- DeviceId = nonNetworkDeviceDto.DeviceId,
- Description = nonNetworkDeviceDto.Description ?? string.Empty,
- LocationDescription = nonNetworkDeviceDto.LocationDescription ?? string.Empty,
- IPOctet1 = nonNetworkDeviceDto.IPOctet1 ?? string.Empty,
- IPOctet2 = nonNetworkDeviceDto.IPOctet2 ?? string.Empty,
- IPOctet3 = nonNetworkDeviceDto.IPOctet3 ?? string.Empty,
- IPOctet4 = nonNetworkDeviceDto.IPOctet4 ?? string.Empty,
- AssetNumber = nonNetworkDeviceDto.AssetNumber ?? string.Empty,
- Owner = nonNetworkDeviceDto.Owner ?? string.Empty,
- LocationCode = nonNetworkDeviceDto.LocationCode,
- Status =
- context.ConvertToDeviceStatusEntity(nonNetworkDeviceDto.Status, isNew),
- NonNetworkType =
- context.ConvertToNonNetworkTypeEntity(nonNetworkDeviceDto.NetworkType, isNew)
- };
- return nonNetworkDevice;
- }
- /// <summary>
- /// Converts to device status entity.
- /// </summary>
- /// <param name=”context”>The context.</param>
- /// <param name=”deviceStatusDto”>The device status dto.</param>
- /// <param name=”isNew”>if set to <c>true</c> [is new].</param>
- /// <returns>A minimal converted <see cref=”DeviceStatus”/> for foreign key reference</returns>
- private static DeviceStatus ConvertToDeviceStatusEntity(this AssetNetworkEntities2 context, DeviceStatusDto deviceStatusDto, bool isNew)
- {
- var status = isNew
- ? context.DeviceStatuses.First(ds => ds.Status == deviceStatusDto.Status)
- : new DeviceStatus { Status = deviceStatusDto.Status };
- return status;
- }
- /// <summary>
- /// Converts to non network type entity.
- /// </summary>
- /// <param name=”context”>The context.</param>
- /// <param name=”nonNetworkTypeDto”>The non network type dto.</param>
- /// <param name=”isNew”>if set to <c>true</c> [is new].</param>
- /// <returns>A minimal converted <see cref=”NonNetworkType”/> for foreign key reference</returns>
- private static NonNetworkType ConvertToNonNetworkTypeEntity(this AssetNetworkEntities2 context, NonNetworkTypeDto nonNetworkTypeDto, bool isNew)
- {
- if (nonNetworkTypeDto == null)
- {
- return null;
- }
- var nonNetworkType = isNew
- ? context.NonNetworkTypes.First(nt => nt.TypeID == nonNetworkTypeDto.TypeId)
- : new NonNetworkType
- {
- TypeID = nonNetworkTypeDto.TypeId,
- TypeName = nonNetworkTypeDto.TypeName
- };
- return nonNetworkType;
- }
Now, instead of calling the TransformationHelper class I directly access the extension method in Figure 3 from the context as seen in Figure 6, line 9
- /// <summary>
- /// Saves the non network device.
- /// </summary>
- /// <param name=”nonNetworkDeviceDto”>The non network device dto.</param>
- public void SaveNonNetworkDevice(NonNetworkDeviceDto nonNetworkDeviceDto)
- {
- using (var context = new AssetNetworkEntities2())
- {
- var changedDevice = context.ConvertToNonNetworkDeviceEntity(nonNetworkDeviceDto);
- if (!nonNetworkDeviceDto.DeviceId.Equals(-1))
- {
- var originalDevice =
- context.NonNetworkDevices.Include(“Status”).Include(“NonNetworkType”).FirstOrDefault(
- d => d.DeviceId.Equals(nonNetworkDeviceDto.DeviceId));
- context.ApplyAllReferencedPropertyChanges(originalDevice, changedDevice);
- context.ApplyPropertyChanges(originalDevice.EntityKey.EntitySetName, changedDevice);
- }
- else
- {
- var maxNetworkDevice = context.NonNetworkDevices.OrderBy(“it.DeviceId DESC”).First();
- changedDevice.DeviceId = maxNetworkDevice.DeviceId + 1;
- context.AddToNonNetworkDevices(changedDevice);
- }
- context.SaveChanges();
- }
- }
I now no longer need to set the references to null and reattach.
Lesson Learned: Wish I’d waited for EF4 !!!