0

I want send from service to service object:

public abstract class Notification : AggregateRoot
{
    public string Title { get; set; }
    public string Content { get; set; }
    public DateTime Created { get; set; }
    public NotificationType NotificationType { get; set; }
}

public class Alert : Notification
{
    public object LinkedObject { get; set; }
    public bool WasSeen { get; set; }
}

And from my unit test:

[Theory, AutoNSubstituteData]
    public async void Send_NotificationIsAlertTypeDocumentDontExist_DocumentShouldBeCreatedAndNotificationSaved(
        IDocumentDbRepository<AlertsDocument> repository,
        CampaignAlertsSender sender,
        Alert notification
        )
    {
        // Arrange
        notification.NotificationType = NotificationType.Alert;
        notification.LinkedObject = new
        {
            MerchantId = Guid.NewGuid()
        };
        repository.GetItemAsync(Arg.Any<Expression<Func<AlertsDocument, bool>>>()).Returns((Task<AlertsDocument>) null);

        // Act
        await sender.SendAsync(notification);

        // Assert
        await repository.Received(1).GetItemAsync(Arg.Any<Expression<Func<AlertsDocument, bool>>>());
        await repository.Received(1).CreateItemAsync(Arg.Any<AlertsDocument>());
    }

Look at the linkedobject it is object but I make it with new. And send it to service.

public override async Task SendAsync(Notification notification)
    {
        if(notification == null)
            throw new ArgumentNullException(nameof(notification));

        var alert = notification as Alert;
        if(alert == null)
            throw new ArgumentException();

        var linkedObject = alert.LinkedObject as dynamic;
        Guid merchantId = Guid.Parse(linkedObject.MerchantId); // here is problem! linkedObject "object" dont have "MerchantId". 
        var document = await Repository.GetItemAsync(doc => doc.MerchantId == merchantId);
        if (document == null)
        {
            document = new AlertsDocument
            {
                MerchantId = merchantId,
                Entity = new List<Alert>()
            };
            document.Entity.Add(alert);

        }
    }

Here is problem! linkedObject "object" dont have "MerchantId". But why? While debuging I see the value MerchantId in linkedObject. How to do it?

Error:

    An exception of type 'Microsoft.CSharp.RuntimeBinder.RuntimeBinderException' occurred in mscorlib.dll but was not handled in user code
Additional information: 'object' does not contain a definition for 'MerchantId'
Nerf
  • 938
  • 1
  • 13
  • 30

2 Answers2

1

LinkedObject is created as an anonymous type which are generated as internal types. If the code accessing the object is not in the same assembly then you will get that error. The debugger can see because it is using reflection but when you try to access it via dynamic you get the error (again because anonymous types are generated as internal).

You however can still get to it via reflection as well.

var linkedObject = alert.LinkedObject as dynamic;    
Guid merchantId = (Guid)linkedObject.GetType()
                           .GetProperty("MerchantId")
                           .GetValue(linkedObject, null);

but this can get messy very fast.

If you take a look at my answer provided here

How do you unit test ASP.NET Core MVC Controllers that return anonymous objects?

A dynamic wrapper which uses reflection under the hood to access the properties of the anonymous type was used.

The same theory applies and you could probably use that wrapper to access the properties of the linkedObject.

var linkedObject = new DynamicObjectResultValue(alert.LinkedObject);
Guid merchantId = (Guid)linkedObject.MerchantId;
Community
  • 1
  • 1
Nkosi
  • 235,767
  • 35
  • 427
  • 472
0

From your code it seems that MerchantId is already a Guid, so you just need to cast it, instead of parsing:

var linkedObject = (dynamic)alert.LinkedObject;
var merchantId = (Guid)linkedObject.MerchantId;
Eli Arbel
  • 22,391
  • 3
  • 45
  • 71