Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix RabbitMq data portal channel #4068

Merged
merged 16 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions Samples/RabbitMqExample/RabbitMqBusiness/PersonEdit.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Csla;

namespace RabbitMqBusiness;

public class PersonEdit : BusinessBase<PersonEdit>
{
public static readonly PropertyInfo<string> NameProperty = RegisterProperty<string>(nameof(Name));
public string Name
{
get => GetProperty(NameProperty);
set => SetProperty(NameProperty, value);
}

[Create]
private void Create()
{ }

[Fetch]
private void Fetch(string name)
{
using (BypassPropertyChecks)
{
Name = name;
}
}
}
13 changes: 13 additions & 0 deletions Samples/RabbitMqExample/RabbitMqBusiness/RabbitMqBusiness.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\Source\Csla\Csla.csproj" />
</ItemGroup>

</Project>
49 changes: 49 additions & 0 deletions Samples/RabbitMqExample/RabbitMqExample.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.11.35017.193
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RabbitMqExample", "RabbitMqExample\RabbitMqExample.csproj", "{545F0983-64D9-40BB-BBD7-4FB34E43B315}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RabbitMqService", "RabbitMqService\RabbitMqService.csproj", "{2E538173-7C94-487D-A114-1696E7B9D7C1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Csla", "..\..\Source\Csla\Csla.csproj", "{AED731F2-DCC1-4751-B942-823CCB6ECF2B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Csla.Channels.RabbitMq", "..\..\Source\Csla.Channels.RabbitMq\Csla.Channels.RabbitMq.csproj", "{44B3E82D-6BEF-43F5-867D-1F671EE58ABD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RabbitMqBusiness", "RabbitMqBusiness\RabbitMqBusiness.csproj", "{D9C45CD8-E7D7-4FAE-BE53-FB0F4B815D3B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{545F0983-64D9-40BB-BBD7-4FB34E43B315}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{545F0983-64D9-40BB-BBD7-4FB34E43B315}.Debug|Any CPU.Build.0 = Debug|Any CPU
{545F0983-64D9-40BB-BBD7-4FB34E43B315}.Release|Any CPU.ActiveCfg = Release|Any CPU
{545F0983-64D9-40BB-BBD7-4FB34E43B315}.Release|Any CPU.Build.0 = Release|Any CPU
{2E538173-7C94-487D-A114-1696E7B9D7C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2E538173-7C94-487D-A114-1696E7B9D7C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2E538173-7C94-487D-A114-1696E7B9D7C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2E538173-7C94-487D-A114-1696E7B9D7C1}.Release|Any CPU.Build.0 = Release|Any CPU
{AED731F2-DCC1-4751-B942-823CCB6ECF2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AED731F2-DCC1-4751-B942-823CCB6ECF2B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AED731F2-DCC1-4751-B942-823CCB6ECF2B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AED731F2-DCC1-4751-B942-823CCB6ECF2B}.Release|Any CPU.Build.0 = Release|Any CPU
{44B3E82D-6BEF-43F5-867D-1F671EE58ABD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{44B3E82D-6BEF-43F5-867D-1F671EE58ABD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{44B3E82D-6BEF-43F5-867D-1F671EE58ABD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{44B3E82D-6BEF-43F5-867D-1F671EE58ABD}.Release|Any CPU.Build.0 = Release|Any CPU
{D9C45CD8-E7D7-4FAE-BE53-FB0F4B815D3B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D9C45CD8-E7D7-4FAE-BE53-FB0F4B815D3B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D9C45CD8-E7D7-4FAE-BE53-FB0F4B815D3B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D9C45CD8-E7D7-4FAE-BE53-FB0F4B815D3B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {0D79C8CE-4F00-464B-8CC2-0E2AC086E03C}
EndGlobalSection
EndGlobal
20 changes: 20 additions & 0 deletions Samples/RabbitMqExample/RabbitMqExample/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using Csla;
using Csla.Configuration;
using Microsoft.Extensions.DependencyInjection;
using RabbitMqBusiness;

var services = new ServiceCollection();
services.AddCsla(o => o
.DataPortal(o => o
.AddClientSideDataPortal(o => o
.UseRabbitMqProxy(o => o
.DataPortalUrl = "rabbitmq://localhost:5672/myservice"))));
var serviceProvider = services.BuildServiceProvider();

Console.WriteLine("RabbitMq Example starting");
var portal = serviceProvider.GetRequiredService<IDataPortal<PersonEdit>>();
var person = await portal.FetchAsync("Abdi");
Console.WriteLine("Person fetched");
Console.WriteLine(person.Name);
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
16 changes: 16 additions & 0 deletions Samples/RabbitMqExample/RabbitMqExample/RabbitMqExample.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\Source\Csla.Channels.RabbitMq\Csla.Channels.RabbitMq.csproj" />
<ProjectReference Include="..\..\..\Source\Csla\Csla.csproj" />
<ProjectReference Include="..\RabbitMqBusiness\RabbitMqBusiness.csproj" />
</ItemGroup>

</Project>
22 changes: 22 additions & 0 deletions Samples/RabbitMqExample/RabbitMqService/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Csla.Channels.RabbitMq;
using Csla.Configuration;
using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();
services.AddCsla(o => o.
DataPortal(o => o.
AddServerSideDataPortal(o => o.
UseRabbitMqPortal(o => o.
DataPortalUri = new Uri("rabbitmq://localhost:5672/myservice")))));
var serviceProvider = services.BuildServiceProvider();

Console.WriteLine("RabbitMq Service starting");

var factory = serviceProvider.GetRequiredService<IRabbitMqPortalFactory>();
var rabbitMqService = factory.CreateRabbitMqPortal();
using (rabbitMqService)
{
rabbitMqService.StartListening();
Console.WriteLine("Press any key to exit");
Console.ReadKey();
}
16 changes: 16 additions & 0 deletions Samples/RabbitMqExample/RabbitMqService/RabbitMqService.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\Source\Csla.Channels.RabbitMq\Csla.Channels.RabbitMq.csproj" />
<ProjectReference Include="..\..\..\Source\Csla\Csla.csproj" />
<ProjectReference Include="..\RabbitMqBusiness\RabbitMqBusiness.csproj" />
</ItemGroup>

</Project>
20 changes: 20 additions & 0 deletions Samples/RabbitMqExample/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# RabbitMq Data Portal Channel Example

This example shows the basic use of the RabbitMq data portal channel in `Csla.Channels.RabbitMq`.

To run the example, use the following steps:

1. Install Docker Desktop
2. Run the RabbitMq container
1. `docker run -d --hostname my-rabbit --name some-rabbit -p 5672:5672 rabbitmq:latest`
3. Open the solution in Visual Studio
4. Set the startup to multiple projects
1. `RabbitMqExample.Server` and `RabbitMqExample.Client`
2. Make sure the `RabbitMqExample.Server` is the first project executed
5. Run the solution

The example shows a simple `EditPerson` object retrieved from the app server.

Look at the `Program.cs` file in the service to see how to configure the data portal server to use the RabbitMq channel.

Look at the `Program.cs` file in the client to see how to configure the data portal client to use the RabbitMq proxy.
20 changes: 20 additions & 0 deletions Source/Csla.Channels.RabbitMq/IRabbitMqPortalFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//-----------------------------------------------------------------------
// <copyright file="IRabbitMqPortalFactory.cs" company="Marimer LLC">
// Copyright (c) Marimer LLC. All rights reserved.
// Website: https://cslanet.com
// </copyright>
// <summary>Implement extension methods data portal channel</summary>
//-----------------------------------------------------------------------

namespace Csla.Channels.RabbitMq;

/// <summary>
/// Factory used to get an instance of RabbitMqPortal
/// </summary>
public interface IRabbitMqPortalFactory
{
/// <summary>
/// Creates an instance of RabbitMqPortal
/// </summary>
RabbitMqPortal CreateRabbitMqPortal();
}
14 changes: 2 additions & 12 deletions Source/Csla.Channels.RabbitMq/ProxyListener.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,7 @@ public static ProxyListener GetListener(Uri queueUri)
{
lock (typeof(ProxyListener))
{
if (_instance == null)
{
_instance = new ProxyListener(queueUri);
}
_instance ??= new ProxyListener(queueUri);
}
}
return _instance;
Expand Down Expand Up @@ -92,7 +89,7 @@ private void InitializeRabbitMQ()
if (string.IsNullOrWhiteSpace(_queueUri.Query))
query = [];
else
query = _queueUri.Query.Substring(1).Split('&');
query = _queueUri.Query[1..].Split('&');
if (query.Length == 0 || !query[0].StartsWith("reply="))
{
IsNamedReplyQueue = false;
Expand Down Expand Up @@ -128,7 +125,6 @@ public void StartListening()
var consumer = new EventingBasicConsumer(Channel);
consumer.Received += (_, ea) =>
{
Console.WriteLine($"Received reply for {ea.BasicProperties.CorrelationId}");
if (Wip.WorkInProgress.TryRemove(ea.BasicProperties.CorrelationId, out WipItem? item))
{
item.Response = ea.Body.ToArray();
Expand All @@ -148,19 +144,13 @@ public void StartListening()
basicProperties: ea.BasicProperties,
body: ea.Body);
}
else
{
Console.WriteLine($"## WARN Undeliverable reply for {ea.BasicProperties.CorrelationId} (discarded by {Environment.MachineName})");
}
}
};
Console.WriteLine($"Listening on queue {ReplyQueue?.QueueName}");
Channel.BasicConsume(queue: ReplyQueue?.QueueName, autoAck: true, consumer: consumer);
}

public void Dispose()
{
Connection?.Close();
Channel?.Dispose();
Connection?.Dispose();
IsListening = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// <summary>Implement extension methods data portal channel</summary>
//-----------------------------------------------------------------------

using Csla.Channels.RabbitMq;
using Csla.DataPortalClient;
using Microsoft.Extensions.DependencyInjection;

Expand All @@ -14,7 +15,7 @@ namespace Csla.Configuration
/// <summary>
/// Implement extension methods data portal channel
/// </summary>
public static class RabbitMqProxyExtensions
public static class RabbitMqChannelExtensions
{
/// <summary>
/// Configure data portal client to use RabbitMqProxy.
Expand All @@ -39,5 +40,25 @@ public static DataPortalClientOptions UseRabbitMqProxy(this DataPortalClientOpti
});
return config;
}

/// <summary>
/// Configure the data portal server to use the RabbitMq channel.
/// </summary>
/// <param name="config"></param>
/// <param name="options"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public static DataPortalServerOptions UseRabbitMqPortal(this DataPortalServerOptions config, Action<Channels.RabbitMq.RabbitMqPortalOptions>? options)
{
if (config is null)
throw new ArgumentNullException(nameof(config));

var portalOptions = new RabbitMqPortalOptions();
options?.Invoke(portalOptions);

config.Services.AddScoped(_ => portalOptions);
config.Services.AddTransient(typeof(IRabbitMqPortalFactory), portalOptions.PortalFactoryType);
return config;
}
}
}
Loading
Loading