cannot convert from 'TableDependency.SqlClient.Base.Enums.DmlTriggerType' to 'TableDependency.SqlClient.Base.Abstracts.ITableDependencyFilter' - asp.net-core

I'm trying to follow this tutorial, but getting this error
Argument 6: cannot convert from
'TableDependency.SqlClient.Base.Enums.DmlTriggerType' to
'TableDependency.SqlClient.Base.Abstracts.ITableDependencyFilter'
(CS1503)
Also, in the same tutorial author used Hubcontext in startup file like this
services.AddScoped<IHubContext<NonProductionHub>, HubContext<NonProductionHub>>();
I'm not sure to whether its correct or not because I'm getting the following error on HubContext and not on IHubContext
The type or namespace name 'HubContext<>' could not be found (are you
missing a using directive or an assembly reference?)
public class InventoryDatabaseSubscription : IDatabaseSubscription
{
private bool disposedValue = false;
private readonly IInventoryRepository _repository;
private readonly IHubContext<NonProductionHub> _hubContext;
private SqlTableDependency<Apps> _tableDependency;
public InventoryDatabaseSubscription(IInventoryRepository repository, IHubContext<NonProductionHub> hubContext)
{
_repository = repository;
_hubContext = hubContext;
}
public void Configure(string DefaultConnection)
{
_tableDependency = new SqlTableDependency<Apps>(DefaultConnection, null, null, null, null, DmlTriggerType.All);
_tableDependency.OnChanged += Changed;
_tableDependency.OnError += TableDependency_OnError;
_tableDependency.Start();
Console.WriteLine("Waiting for receiving notifications...");
}
private void TableDependency_OnError(object sender, ErrorEventArgs e)
{
Console.WriteLine($"SqlTableDependency error: {e.Error.Message}");
}
private void Changed(object sender, RecordChangedEventArgs<Apps> e)
{
if (e.ChangeType != ChangeType.None)
{
// TODO: manage the changed entity
var changedEntity = e.Entity;
_hubContext.Clients.All.SendAsync("UpdateCatalog", _repository.Apps);
}
}
#region IDisposable
~InventoryDatabaseSubscription()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
_tableDependency.Stop();
}
disposedValue = true;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
}

Argument 6: cannot convert from 'TableDependency.SqlClient.Base.Enums.DmlTriggerType' to 'TableDependency.SqlClient.Base.Abstracts.ITableDependencyFilter' (CS1503)
From the error , you could go to the definition of SqlTableDependency method to check the arguments contained
public SqlTableDependency(string connectionString, string tableName = null, string schemaName = null,
IModelToTableMapper<T> mapper = null, IUpdateOfModel<T> updateOf = null,
ITableDependencyFilter filter = null, DmlTriggerType notifyOn = DmlTriggerType.All,
bool executeUserPermissionCheck = true, bool includeOldValues = false);
The value DmlTriggerType.All should be the seventh instead of sixth, and the value of the sixth parameter is null , change the code like below :
_tableDependency = new SqlTableDependency<Apps>(DefaultConnection, null, null, null, null, null, DmlTriggerType.All);
The type or namespace name 'HubContext<>' could not be found (are you missing a using directive or an assembly reference?)
The HubContext allows you to send messages to your connected clients. It has many of the same features to communicate with clients as when you are inside of a Hub.
In order to get an instance of the HubContext, you need to be using dependency injection by specifying you want an IHubContext<T> in the constructor. Where T is your Hub. Refer to the following example :
public class HomeController : Controller
{
private readonly IHubContext<NotificationHub> _hubContext;
public HomeController(IHubContext<NotificationHub> hubContext)
{
_hubContext = hubContext;
}
}
Referece :https://learn.microsoft.com/en-us/aspnet/core/signalr/hubcontext?view=aspnetcore-2.2

Related

Read ASP.NET Core logs per scope/operation

Let's say I have several ASP.NET BackgroundServices and each is logging to its own scope/operation (OP1 and OP2).
public class MyBackgroundService1 : BackgroundService
{
private readonly ILogger<MyBackgroundService1> _logger;
public MyBackgroundService1(ILogger<MyBackgroundService1> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var activity = new Activity("OP1");
activity.Start();
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Hello from MyBackgroundService1");
await Task.Delay(5000, stoppingToken);
}
}
}
public class MyBackgroundService2 : BackgroundService
{
private readonly ILogger<MyBackgroundService2> _logger;
public MyBackgroundService2(ILogger<MyBackgroundService2> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var activity = new Activity("OP2");
activity.Start();
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Hello from MyBackgroundService2");
await Task.Delay(1000, stoppingToken);
}
}
}
Now I would like to use Blazor and want to display a table per operation with all corresponding logs.
Example output
OP1 Logs:
Hello from MyBackgroundService1
Hello from MyBackgroundService1
OP2 Logs:
Hello from MyBackgroundService2
Hello from MyBackgroundService2
How would I do that?
For this purpose, you need to create a log provider that stores the information in the database and then retrieves the information from the log table.
First, create a class to store logs in the database as follows:
public class DBLog
{
public int DBLogId { get; set; }
public string? LogLevel { get; set; }
public string? EventName { get; set; }
public string? Message { get; set; }
public string? StackTrace { get; set; }
public DateTime CreatedDate { get; set; }=DateTime.Now;
}
Now, We need to create a custom DBLogger. The DBLogger class inherits from the ILogger interface and has three methods, the most important of which is the Log method, which is actually called every time the Logger is called in the program. To read more about the other two methods, you can refer here.
public class DBLogger:ILogger
{
private readonly LogLevel _minLevel;
private readonly DbLoggerProvider _loggerProvider;
private readonly string _categoryName;
public DBLogger(
DbLoggerProvider loggerProvider,
string categoryName
)
{
_loggerProvider= loggerProvider ?? throw new ArgumentNullException(nameof(loggerProvider));
_categoryName= categoryName;
}
public IDisposable BeginScope<TState>(TState state)
{
return new NoopDisposable();
}
public bool IsEnabled(LogLevel logLevel)
{
return logLevel >= _minLevel;
}
public void Log<TState>(
LogLevel logLevel,
EventId eventId,
TState state,
Exception exception,
Func<TState, Exception, string> formatter)
{
if (!IsEnabled(logLevel))
{
return;
}
if (formatter == null)
{
throw new ArgumentNullException(nameof(formatter));
}
var message = formatter(state, exception);
if (exception != null)
{
message = $"{message}{Environment.NewLine}{exception}";
}
if (string.IsNullOrEmpty(message))
{
return;
}
var dblLogItem = new DBLog()
{
EventName = eventId.Name,
LogLevel = logLevel.ToString(),
Message = $"{_categoryName}{Environment.NewLine}{message}",
StackTrace=exception?.StackTrace
};
_loggerProvider.AddLogItem(dblLogItem);
}
private class NoopDisposable : IDisposable
{
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
}
}
}
Now we need to create a custom log provider so that an instance of the above custom database logger (DBLogger) can be created.
public class DbLoggerProvider : ILoggerProvider
{
private readonly CancellationTokenSource _cancellationTokenSource = new();
private readonly IList<DBLog> _currentBatch = new List<DBLog>();
private readonly TimeSpan _interval = TimeSpan.FromSeconds(2);
private readonly BlockingCollection<DBLog> _messageQueue = new(new ConcurrentQueue<DBLog>());
private readonly Task _outputTask;
private readonly IServiceProvider _serviceProvider;
private bool _isDisposed;
public DbLoggerProvider(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
_outputTask = Task.Run(ProcessLogQueue);
}
public ILogger CreateLogger(string categoryName)
{
return new DBLogger(this, categoryName);
}
private async Task ProcessLogQueue()
{
while (!_cancellationTokenSource.IsCancellationRequested)
{
while (_messageQueue.TryTake(out var message))
{
try
{
_currentBatch.Add(message);
}
catch
{
//cancellation token canceled or CompleteAdding called
}
}
await SaveLogItemsAsync(_currentBatch, _cancellationTokenSource.Token);
_currentBatch.Clear();
await Task.Delay(_interval, _cancellationTokenSource.Token);
}
}
internal void AddLogItem(DBLog appLogItem)
{
if (!_messageQueue.IsAddingCompleted)
{
_messageQueue.Add(appLogItem, _cancellationTokenSource.Token);
}
}
private async Task SaveLogItemsAsync(IList<DBLog> items, CancellationToken cancellationToken)
{
try
{
if (!items.Any())
{
return;
}
// We need a separate context for the logger to call its SaveChanges several times,
// without using the current request's context and changing its internal state.
var scopeFactory = _serviceProvider.GetRequiredService<IServiceScopeFactory>();
using (var scope = scopeFactory.CreateScope())
{
var scopedProvider = scope.ServiceProvider;
using (var newDbContext = scopedProvider.GetRequiredService<ApplicationDbContext>())
{
foreach (var item in items)
{
var addedEntry = newDbContext.DbLogs.Add(item);
}
await newDbContext.SaveChangesAsync(cancellationToken);
// ...
}
}
}
catch
{
// don't throw exceptions from logger
}
}
[SuppressMessage("Microsoft.Usage", "CA1031:catch a more specific allowed exception type, or rethrow the exception",
Justification = "don't throw exceptions from logger")]
private void Stop()
{
_cancellationTokenSource.Cancel();
_messageQueue.CompleteAdding();
try
{
_outputTask.Wait(_interval);
}
catch
{
// don't throw exceptions from logger
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_isDisposed)
{
try
{
if (disposing)
{
Stop();
_messageQueue.Dispose();
_cancellationTokenSource.Dispose();
}
}
finally
{
_isDisposed = true;
}
}
}
}
In the end, it is enough to call this custom log provider (DbLoggerProvider) in the Startup.cs or Program.cs class.
var serviceProvider = app.ApplicationServices.CreateScope().ServiceProvider;
loggerFactory.AddProvider(new DbLoggerProvider(serviceProvider));
From now on, every time we call the _logger.LogInformation("");, the log information will also be stored in the database.
Note: Because the number of calls to record logs in the database may be high, a concurrent queue is used to store logs.
If you like, you can refer to my repository that implements the same method.
In order to log the areas separately(scope/operation), you can create several different DBLoggers to store the information in different tables.

Passing arguments to IAsyncAuthorizationFilter inside TypeFilterAttribute

I'm trying to pass arguments to an IAsyncAuthorizationFilter filter from the TypeFilterAttribute. I made a simple example which according to answers such as this one https://stackoverflow.com/a/44435070/937131 should work.
public class SimpleAuthorizeServiceAttribute : TypeFilterAttribute
{
public SimpleAuthorizeServiceAttribute(string service, string context, AccessType access) : base(typeof(SimpleAuthorizeServiceAttribute))
{
Arguments = new[] { new PermissionData(service, context, access) };
}
public class SimpleAuthorizeServiceFilter : IAsyncAuthorizationFilter
{
private readonly PermissionData _permissionData;
public SimpleAuthorizeServiceFilter(PermissionData permissionData)
{
_permissionData = permissionData;
}
public virtual Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
var test = _permissionData.Service != null;
return Task.CompletedTask;
}
}
}
I've then applied this to a method like this:
[HttpGet]
[SimpleAuthorizeService("test-service", "test-context", AccessType.Read)]
public async Task<bool> ServiceTest()
{
bool isAllowed = this.HttpContext.IsUserAllowedToAccess(10, 20, 30);
return isAllowed;
}
Which fails with
System.InvalidOperationException: A suitable constructor for type
'SecurityAttribute.Application.ServiceToServiceAttribute.SimpleAuthorizeServiceAttribute'
could not be located. Ensure the type is concrete and all parameters
of a public constructor are either registered as services or passed as
arguments. Also ensure no extraneous arguments are provided.
I thought this might be because I'm not using params for the TypeFilterAttribute (which is dumb). So i rewrote it to this monstrosity..
public class SimpleAuthorizeServiceAttribute : TypeFilterAttribute
{
public SimpleAuthorizeServiceAttribute(params string[] data) : base(typeof(SimpleAuthorizeServiceAttribute))
{
// hoorray for non type safe paramaters...
var success = AccessType.TryParse(data[2], out AccessType access);
Arguments = new[] { new PermissionData(data[0], data[1], access) };
}
public class SimpleAuthorizeServiceFilter : IAsyncAuthorizationFilter
{
private readonly PermissionData _permissionData;
public SimpleAuthorizeServiceFilter(PermissionData permissionData)
{
_permissionData = permissionData;
}
public virtual Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
var test = _permissionData.Service != null;
return Task.CompletedTask;
}
}
}
....
[HttpGet]
[SimpleAuthorizeService("test-service", "test-context", "Read")]
public async Task<bool> ServiceTest()
{
bool isAllowed = this.HttpContext.IsUserAllowedToAccess(10, 20, 30);
return isAllowed;
}
But that still trows an error:
System.InvalidOperationException: A suitable constructor for type
'SecurityAttribute.Application.ServiceToServiceAttribute.SimpleAuthorizeServiceAttribute'
could not be located. Ensure the type is concrete and all parameters
of a public constructor are either registered as services or passed as
arguments. Also ensure no extraneous arguments are provided. at
Microsoft.Extensions.DependencyInjection.ActivatorUtilities.FindApplicableConstructor(Type
instanceType, Type[] argumentTypes, ConstructorInfo&
matchingConstructor, Nullable`1[]& matchingParameterMap)
So how is one supposed to pass arguments to a IAsyncAuthorizationFilter?
The issue is with this line
public SimpleAuthorizeServiceAttribute(string service, string context, AccessType access) :
base(typeof(SimpleAuthorizeServiceAttribute)) // problem is here
{
Arguments = new[] { new PermissionData(service, context, access) };
}
it should be SimpleAuthorizeServiceFilter not SimpleAuthorizeServiceAttribute.
replace it and it should work.

Getting "no serializer found for class" Exception in restAssured post request

I have a Json Payload for a Post call as below:
{
"action" : "Closed",
"Id" : 30144,
"expireDate" : null,
"inputUser" : "abc",
"previousStatusId" : 1,
"statusId" : 4,
"Notes" : [ ]
}
My POJO classes for the above payload is as below
public class UpdateNoteStatus {
private String action;
private int Id;
private String expireDate;
private String inputUser;
private int previousStatusId;
private int statusId;
private List<Notes> Notes;
public void setAction(String action) {
this.action = action;
}
public void setId(int Id) {
this.Id = Id;
}
public void setExpireDate(String expireDate) {
this.expireDate = expireDate;
}
public void setinputUser(String inputUser) {
this.inputUser = inputUser;
}
public void setPreviousStatusId(int previousStatusId) {
this.previousStatusId = previousStatusId;
}
public void setStatusId(int statusId) {
this.statusId = statusId;
}
public void setNotes(List<Notes> Notes) {
this.Notes = Notes;
}
}
public class Notes{
}
Now I have assigned the values in the main class from where I am making the API call is as below:
ArrayList<Notes> Notes = new ArrayList<Notes>();
UpdateNoteStatus objUpdateNoteStatus = new UpdateNoteStatus();
objUpdateNoteStatus.setAction("Closed");
objUpdateNoteStatus.setId(Integer.parseInt("30144"));
objUpdateNoteStatus.setinputUser("abc");
objUpdateNoteStatus.setPreviousStatusId(1);
objUpdateNoteStatus.setStatusId(4);
objUpdateNoteStatus.setNotes(Notes);
But when I am making the API POST call it is throwing exception - "no serializer found for class and no properties discovered to create beanserializer". Could you please help. The Step is hightlighted in Bold.
RequestSpecification rs = given().contentType("application/json");
**rs = rs.body(objUpdateNoteStatus);** //In This Step I am getting the above mentioned Exception
Response res = rs.when().post("/UpdateStatus");
as you are initializing an empty object , you need to use below Annotation supported in below library
com.jayway.restassured.RestAssured;
#JsonIgnoreProperties(ignoreUnknown=true)
class UpdateNoteStatus

Asp.Net Core 3.1 LinqToDB.Identity UserManager CreateAsync Error

I have implemented LinqToDB.Identity into my project. I have been trying to create a user by using .Net Identity UserManager, but I am getting an error. I have also implemented LinqToDB.Identity optimizations, such as AddLinqToDBStores and IdentityConnectionFactory.
As I have mentioned about it, I am getting an error like this when I try to create an user.
{"Method not found: 'Int32 LinqToDB.DataExtensions.Insert(LinqToDB.IDataContext, System.__Canon, System.String, System.String, System.String)'."}
Here is my AddLinqToDBStores options and configurations.
public static void AddDevPlatformAuthentication(this IServiceCollection services, IConfiguration configuration)
{
services.AddIdentity<AppUser, LinqToDB.Identity.IdentityRole<int>>(options =>
{
options.Password.RequireDigit = true;
options.Password.RequiredLength = 4;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = true;
options.Password.RequireLowercase = false;
options.User.RequireUniqueEmail = true;
//TODO
//options.User.RequireUniqueEmail = true;
//options.SignIn.RequireConfirmedEmail = true;
//options.Lockout.MaxFailedAccessAttempts = 5;
//options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(3);
}).AddLinqToDBStores<int, AppUserClaim, AppUserRole, AppUserLogin, AppUserToken, AppRoleClaim>(new
IdentityConnectionFactory(new SqlServerDataProvider(ProviderName.SqlServer, SqlServerVersion.v2017), "SqlServerIdentity", DataSettingsManager.LoadSettings().ConnectionString))
.AddDefaultTokenProviders();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie();
// Uncomment the following lines to enable logging in with third party login providers
JwtTokenDefinitions.LoadFromConfiguration(configuration);
services.ConfigureJwtAuthentication();
services.ConfigureJwtAuthorization();
}
Here is my IdentityConnectionFactory class that has inherited from IConnectionFactory interface.
public class IdentityConnectionFactory : IConnectionFactory
{
private static readonly Dictionary<string, HashSet<string>> _tables = new Dictionary<string, HashSet<string>>();
private readonly string _configuration;
private readonly string _connectionString;
private readonly string _key;
private readonly IDataProvider _provider;
public IdentityConnectionFactory(IDataProvider provider, string configuration, string connectionString)
{
_provider = provider;
Configuration.Linq.AllowMultipleQuery = true;
//DataConnection.AddConfiguration(configuration, connectionString, provider);
_configuration = configuration;
_connectionString = connectionString;
_key = _configuration + "$$" + _connectionString;
}
public IDataContext GetContext()
{
return new DataContext(_provider, _connectionString);
}
public DataConnection GetConnection()
{
var db = new DataConnection(_provider, _connectionString);
db.AddMappingSchema(AdditionalSchema);
return db;
}
protected MappingSchema AdditionalSchema
{
get
{
if (!(Singleton<MappingSchema>.Instance is null))
return Singleton<MappingSchema>.Instance;
Singleton<MappingSchema>.Instance =
new MappingSchema(_provider.Name) { MetadataReader = new FluentMigratorMetadataReader() };
return Singleton<MappingSchema>.Instance;
}
}
There are so many code blocks that I can not paste here. I would be very happy if someone could help.
If you would like to see the project, you can check here;
https://github.com/dogaanismail/DevPlatform
This problem has been solved by adding LinqToDB.Identity class library into the solution. I have created an issue on Github. You can check from this link.
https://github.com/linq2db/linq2db/issues/2400
I have uninstalled LinqToDB.Identity that is a nuget package. Instead of using LinqToDB.Identity nuget package, it is better to use LinqToDB.Identity class library. In addition, I can debug this class library. It is really useful!
You can obtain LinqToDB.Identity class library with this link
https://github.com/linq2db/LinqToDB.Identity/tree/master/src/LinqToDB.Identity
or if you would, you can check my project that is called DevPlatform.
https://github.com/dogaanismail/DevPlatform
In addition to all of these, I have added an IdentityAttribute for my AppUser primary key. I did not create an user without this attribute.
public class AppUser : IdentityUser<int>, IEntity
{
[Required, Identity]
[Key]
public override int Id { get => base.Id; set => base.Id = value; }
public DateTime CreatedDate { get; set; }
public DateTime? ModifiedDate { get; set; }
public int? CreatedBy { get; set; }
public int? ModifiedBy { get; set; }
public int? StatusId { get; set; }
}
As a result of creating a class library and adding LinqToDB.Identity classes, such as IdentityUser, DefaultConnectionFactory, IConcurrency etc. I created an user successfully by using UserManager.

Domain Events Implementation Using StructureMap Error

I am trying to grasp the fundamentals of raising/handling a Domain Event in my Solution. I am using Visual Studio 2017, .Net Core 1.1, C#, StructureMap 4.5.1.
The failure in my code came to light in a Unit Test which failed when checking if my Domain Event was being raised correctly.
My Startup.cs class includes the following code:
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddSingleton(_config);
services.AddAutoMapper();
services.AddMvc()
.AddControllersAsServices();
return ConfigureIoC(services);
}
public IServiceProvider ConfigureIoC(IServiceCollection services)
{
var container = new Container();
container.Configure(config =>
{
config.Scan(scan =>
{
scan.AssemblyContainingType(typeof(Startup));
scan.Assembly("Shared");
scan.Assembly("TaskScheduling");
scan.Assembly("TaskScheduling_Tests");
scan.WithDefaultConventions();
scan.ConnectImplementationsToTypesClosing(typeof(IHandle<>));
});
//Populate the container using the service collection
config.Populate(services);
});
return container.GetInstance<IServiceProvider>();
}
Where possible I have been following Udi Dahan's approach Domain Events - Salvation
My DomainEvents class implements the following Interface:
using System;
namespace Shared.Interfaces
{
public interface IDomainEvent
{
DateTime DateTimeEventOccurred { get; }
}
}
The DomainEvents class is as follows:
using System;
using System.Collections.Generic;
using Shared.Interfaces;
using StructureMap;
namespace Shared
{
/// <summary>
/// http://udidahan.com/2009/06/14/domain-events-salvation/
/// http://msdn.microsoft.com/en-gb/magazine/ee236415.aspx#id0400046
///
/// This class registers Domain Events and makes sure they get called.
/// </summary>
public static class DomainEvents
{
[ThreadStatic]
private static List<Delegate> actions;
public static IContainer Container { get; set; }
// Registers a callback for the given domain event.
public static void Register<T>(Action<T> callback) where T : IDomainEvent
{
if (actions == null)
{
actions = new List<Delegate>();
}
actions.Add(callback);
}
// Clears callbacks passed to Register on the current thread.
public static void ClearCallbacks()
{
actions = null;
}
// Raises the given domain event.
public static void Raise<T>(T args) where T : IDomainEvent
{
foreach (var handler in Container.GetAllInstances<IHandle<T>>())
{
handler.Handle(args);
}
if (actions != null)
{
foreach (var action in actions)
{
if (action is Action<T>)
{
((Action<T>)action)(args);
}
}
}
}
}
}
I have a Task class which when updated raises a TaskUpdatedEvent. The TaskUpdatedEvent class is as follows:
using Shared.Interfaces;
using System;
namespace TaskScheduling.Model.Events
{
public class TaskUpdatedEvent : IDomainEvent
{
/// <summary>
/// When creating a TaskUpdatedEvent you have to pass in the Task object.
/// </summary>
/// <param name="task"></param>
public TaskUpdatedEvent(ScheduleAggregate.Task task)
: this()
{
TaskUpdated = task;
}
public TaskUpdatedEvent()
{
this.Id = Guid.NewGuid();
DateTimeEventOccurred = DateTime.Now; // IDomainEvent interface requirement.
}
public Guid Id { get; private set; }
public DateTime DateTimeEventOccurred { get; private set; }
public ScheduleAggregate.Task TaskUpdated { get; private set; }
}
}
and the event is raised with the following lines in my Task class:
var taskUpdatedEvent = new TaskUpdatedEvent(this);
DomainEvents.Raise(taskUpdatedEvent);
I only have one Unit Test, so far, to check if this event is being raised. The Unit Test is as follows:
using System;
using NUnit.Framework;
using Shared;
using TaskScheduling.Model.ScheduleAggregate;
using TaskScheduling.Model.Events;
namespace TaskScheduling_Tests
{
[TestFixture]
public class TaskUpdatedEventShould
{
private Task testTask;
private readonly Guid testScheduleId = Guid.NewGuid();
private const int TestLocationId = 567;
private const int TestDeviceId = 123;
private const int TestTaskTypeId = 1;
private readonly DateTime testStartTime = new DateTime(2014, 7, 1, 9, 0, 0);
private readonly DateTime testEndTime = new DateTime(2014, 7, 1, 9, 30, 0);
private readonly DateTimeRange newTaskTimeRange = new DateTimeRange(new DateTime(2014, 6, 9, 10, 0, 0), TimeSpan.FromHours(1));
private const string TestTitle = "Unit Test Title";
[SetUp]
public void SetUp()
{
DomainEvents.ClearCallbacks();
testTask = Task.Create(
testScheduleId,
TestLocationId,
TestDeviceId,
TestTaskTypeId,
testStartTime,
testEndTime,
TestTitle
);
}
[Test]
public void EntityConstructor_IsNot_Null()
{
Assert.IsNotNull(testTask);
}
[Test]
public void RaiseTaskUpdatedEvent()
{
// Arrange
Guid updatedAppointmentId = Guid.Empty;
DomainEvents.Register<TaskUpdatedEvent>(aue =>
{
// This defines happens when the event is raised/
// The 'updatedAppointmentId' is changed from being all zeros to the testTask's id value.
updatedAppointmentId = testTask.Id;
});
// Act
testTask.UpdateTime(newTaskTimeRange);
// Assert
Assert.AreEqual(testTask.Id, updatedAppointmentId);
}
}
}
The failure appears to occur in the DomainEvent class when the Raise method is called. Debugging shows that the event is raised and the arguments are set, however the Container is Null so the foreach loop cannot check for handlers.
I cannot figure out why the Container is Null but I'm sure I must be missing something obvious. Any suggestions welcome.