Social Icons

twitter google plus linkedin rss feed

Pages

Featured Posts

23.3.19

Configuring and Updating nested value objects in Entity Framework

Wow three years since the last post... not bad!

For those of you who are curious I have finally left SharePoint and now I am sure we both are happier. But with a new platform come new issues.

I am working now in a web application with .net core and entity framework and we have started using ValueObjects. All was fun and games until last wednesday when I decided to make one value object a child of another.

We have User:
public class User: ValueObject
{
    public string ActiveDirectorySID { get; private set; }
    public string Email { get; private set; }
    public string Name { get; private set; }

    (...)
}

We have Position:
public class Position : ValueObject
{
    public Guid PositionId { get; private set; }
    public string JobTitle { get; private set; }
    public User Employee { get; private set; }
    (...)
}

And Finally we have CompanyPolicy:
public class CompanyPolicy : Entity
{
    public string Title { get; private set; }
    (...)
    public Position Owner { get; private set; }
    public Position Reviewer { get; private set; }
    public Position Validator { get; private set; }
    (...)
}

As you can see CompanyPolicy owns three Positions and each position owns an User. The first issue we need to deal with is the mappings of the CompanyPolicy object. After reading half of the internet the solution ended up being easy, the configuration code for the Positions inside a CompanyPolicy is:
public override void Configure(EntityTypeBuilder<CompanyPolicy> builder)
{
  base.Configure(builder);

  builder.Property(x => x.Title);
  (...)
  builder.OwnsOne(p => p.Owner, cb => { cb.OwnsOne(e => e.Employee); });
  builder.OwnsOne(p => p.Reviewer, cb => { cb.OwnsOne(e => e.Employee); });
  builder.OwnsOne(p => p.Validator, cb => { cb.OwnsOne(e => e.Employee); });
  (...)
}

So far so good, we add the migration an it does not fail, we update the databases and it works... we add a value and all the fields get populated... We did it?! NOPE. The values never update. No matter what you do.

We read the other half of the internet and found this thread where they show a workaround and say this is already solved in the 3.0 preview, and as we don't want to wait and we don't want to install VS2019 we decided to go for the workaround.

Based on it and after a couple of iterations we came up with this method:
public static void UpdateChildValueObjects<TEntity, TParent>(
    this BaseAllensContext context,
    TEntity rootEntity,
    Expression<Func<TParent, ValueObject>> getChildValueObject,
    params Expression<Func<TEntity,TParent>>[] getParentValueObjectArray)
        where TEntity : class where TParent:class
        {
    var values = new List<ValueObject>();

    //In the DetectChanges and when setting the values the rootEntity gets
    //the values from the database. That's why we need to save them before!
    for (int i = 0; i < getParentValueObjectArray.Length; i++)
    {
        values.Add(getChildValueObject.Compile()
          .Invoke(getParentValueObjectArray[i].Compile().Invoke(rootEntity)));
    }

    context.ChangeTracker.DetectChanges();

    for (var i = 0; i < getParentValueObjectArray.Length; i++)
    {
        context.Entry(rootEntity).Reference(getParentValueObjectArray[i])
          .TargetEntry.Reference(getChildValueObject)
          .CurrentValue = values[i];
    }

    context.Entry(rootEntity).State = EntityState.Modified;
}

Ugly anyone? Hard to read? It is not ugly, I like it. And it sits in your repository beautifully.

In the repository we have added this method:

public void Update(CompanyPolicy policy)
{
    Context.UpdateChildValueObjects(policy, x => x.Employee,
        x => x.Owner, x => x.Validator, x => x.Reviewer);
}

This is what I wanted to show :)
So with this method in the repository you are telling that you want to update the Employee ValueObject in the Owner, Validator and Reviewer ValueObjects of the Entity policy. And you might be thinking... That looks nice but, how do you use it?
Well this is the ugly part...

First after we update the CompanyPolicy entity as we would normally do we need to make sure these value objects are updated so... I call the update first on the entity and then on the repository, like this:
var owner= companyPolicyDTO.Owner != null
    ? Mapper.Map<Position>(companyPolicyDTO.Owner)
    : Position.Empty;
var reviewer = companyPolicyDTO.Reviewer != null
    ? Mapper.Map<Position>(companyPolicyDTO.Reviewer)
    : Position.Empty;
var validator = companyPolicyDTO.Validator != null
    ? Mapper.Map<Position>(companyPolicyDTO.Validator)
    : Position.Empty;

companyPolicy.Update(companyPolicyDTO.Title, owner, reviewer, validator);
companyPolicyRepository.Update(companyPolicy);

And this is it. First the standard update then our workaround.

Known Issues:
- The method UpdateChildValueObjects sets the values of the non updated fields of the entity to what they where in the database. This means you can't call this method twice in the same transaction because the second time it will not update the values!

public void Update(CompanyPolicy policy)
{
    Context.UpdateChildValueObjects(policy, x => x.Employee,
        x => x.Owner, x => x.Validator, x => x.Reviewer);
}
OK

public void Update(CompanyPolicy policy)
{
    Context.UpdateChildValueObjects(policy, x => x.Employee,
        x => x.Owner);
    Context.UpdateChildValueObjects(policy, x => x.Employee,
        x => x.Reviewer);
    Context.UpdateChildValueObjects(policy, x => x.Employee,
        x => x.Validator);
}
Only the owner will be updated!

I hope this helps someone!

No comments:

Post a Comment

22.9.15

How To Clone a Virtual Machine in Azure

This is quite simple, once you know how to do it but... when you don't...

My favourite way of cloning virtual machines has always been copying the hard disks and deploying the virtual machine in a different network segment. We do have all the ingredients we need for this recipe in Azure so let's get to it!

You will need:

  • The blob that contains the vhd you want to clone
  • A new Cloud Service (or one where the source virtual machine is not running)
  • Notepad.exe (or similar, although nothing beats the original)
  • Azure PowerShell
  • Attention to detail
  • Just a bit of patience
Let's start.

The first thing you need to do is to find out the disk(s) you want to clone. In the virtual machine's dashboard page, if you scroll down a bit, you will be able to see them.

After you scroll down do not forget to scroll right too, the URL we need is a bit hidden... copy it in your notepad.

This is the address of the blob that contains the disk and in it you can find:

http://[yourStorageAccount].blob.core.windows.net/[yourContainerName]/[yourBlobName].vhd

Finally you need  the primary access key for your storage account. 

Go to Storage, select your Storage Account and look on the footer of the page for the key icon:


There copy your primary key and paste it to your notepad.

And that's all you need.

#From http://michaelwasham.com/windows-azure-powershell-reference-guide/copying-vhds-blobs-between-storage-accounts/
#And after that from http://www.codegrimoire.com
####################################################################################################################
### Source VHD - anonymous access container ###
$storageUri = "http://yourStorageAccount.blob.core.windows.net/"
$containerName = "yourContainerName" ##I am using the same source and destination here
$sourceBlobName = "yourBlobame.vhd"

### Destination Blob
$destBlobName = "newBlobName.vhd"

######### New Disk Name
$newDiskName =  "newDiskName"
$newDiskLabel = "BootDisk" ##In the documentation it is either BootDisk or DataDisk but you can call it something else
$isOsDisk = $true ##$true Or $false. As I only work with windows I will not bother about other OSs in this script

### Target Storage Account ###
$storageAccount = "yourStorageAccount"
$storageKey = "yourPrimaryKey" ##Primary Access Key


###################################################################################
############  Automated script for creating the new VHD  ##########################
###################################################################################

$srcUri = ($storageUri.trim('/'), $containerName.trim('/'), $sourceBlobName) -join '/'
$destUri = ($storageUri.trim('/'), $containerName.trim('/'), $destBlobName) -join '/'
 
### Create the destination context for authenticating the copy
$destContext = New-AzureStorageContext  –StorageAccountName $storageAccount -StorageAccountKey $storageKey  
 
### Create the target container in storage
### Not necessary as i am using an existing one ### New-AzureStorageContainer -Name $containerName -Context $destContext 
 
### Start the Asynchronous Copy ###
$blob1 = Start-AzureStorageBlobCopy -srcUri $srcUri -DestContainer $containerName -DestBlob $destBlobName -DestContext $destContext

### Loop until complete ###                                    
Do{
  $status = $blob1 | Get-AzureStorageBlobCopyState 
  ### Print out status ###
  $status.Status
  Start-Sleep 5
}While($status.Status -eq "Pending") ##This doesn't work as you would expect but the idea is good and maybe they will change the way Get-AzureStorageBlobCopyState works :)


######## After the new blob has been created we will add the new disk #############
if ($isOsDisk){
    Add-AzureDisk -DiskName $newDiskName -MediaLocation $destUri -Label $newDiskLabel -OS "Windows"
}
else{
    Add-AzureDisk -DiskName $newDiskName -MediaLocation $destUri -Label $newDiskLabel
}

Once you edit the script with your data and run it you will have to wait for a couple of minutes before the new disk is available. You do not need to turn off the source virtual machine although it's better safe than sorry

After that go to Virtual Machines, New, Compute, Virtual Machine, From Gallery and choose My disks in the lower part of the left column:

The disk you have just created will appear in the list and after that you just need to create the new virtual machine normally.

By the way, we need an Azure + SharePoint administrator in London, do you oblige?


Is it a good idea to call the cloned VMs Dolly?

No comments:

Post a Comment