访问和保存动态对象

时间:2016-07-15 10:20:51

标签: c# unit-testing dynamic

我想从服务发送到服务对象:

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; }
}

从我的单元测试:

[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>());
    }

查看链接对象object,但我使用new。并将其发送给服务。

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);

        }
    }

这是问题! linkedObject&#34; object&#34;没有&#34; MerchantId&#34;。 但为什么?在debuging的同时,我在linkedObject中看到了MerchantId的值。 怎么做?

错误:

    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'

2 个答案:

答案 0 :(得分:1)

LinkedObject创建为匿名类型,生成为internal类型。如果访问该对象的代码不在同一个程序集中,那么您将收到该错误。调试器可以看到,因为它正在使用反射,但是当您尝试通过dynamic访问它时,您会收到错误(同样是因为匿名类型是作为内部生成的)。

然而,您仍然可以通过反思来实现它。

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

但这可能会非常快。

如果你看看我在这里提供的答案

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

使用动态包装器,它使用引擎盖下的反射来访问匿名类型的属性。

同样的理论适用,您可以使用该包装器来访问linkedObject的属性。

var linkedObject = new DynamicObjectResultValue(alert.LinkedObject);
Guid merchantId = (Guid)linkedObject.MerchantId;

答案 1 :(得分:0)

从您的代码中看来,MerchantId已经是Guid,所以您只需要强制转换它,而不是解析:

var linkedObject = (dynamic)alert.LinkedObject;
var merchantId = (Guid)linkedObject.MerchantId;