I can't get this working, I've googled and probably found every page on how to do this, all two of them!
Basically I'm just trying to get SF Remoting V2 working from a stateless .NET Core 2 MVC App' to a Statefull service.
Here's what I have done:
Client Code in Controller: (Simplified as much as Possible):
public class ValuesController : Controller
{
[HttpGet]
public async Task<IEnumerable<string>> Get()
{
// Provide certificate details.
var serviceProxyFactory = new ServiceProxyFactory((c) => new FabricTransportServiceRemotingClientFactory());
var proxy = serviceProxyFactory.CreateServiceProxy<ICallMe>(new Uri("fabric:/SFExperiment/Stateful1"));
var value3 = await proxy.GetGreeeting("Bob");
return new[] { "value1", "value2", value3 };
}
Service Code Interface:
using System.Threading.Tasks;
using Microsoft.ServiceFabric.Services.Remoting;
using Microsoft.ServiceFabric.Services.Remoting.FabricTransport;
[assembly: FabricTransportServiceRemotingProvider(RemotingListener =
RemotingListener.V2Listener, RemotingClient = RemotingClient.V2Client)]
namespace Stateful1.Abstractions
{
public interface ICallMe : IService
{
Task<string> GetGreeeting(string name);
}
}
Service Code:
Public sealed class Stateful1 : StatefulService, ICallMe
{
public Stateful1(StatefulServiceContext context)
: base(context)
{ }
protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
{
return this.CreateServiceRemotingReplicaListeners();
}
public Task<string> GetGreeeting(string name)
{
return Task.FromResult(#"Congratulations, you have remoting working. :-) ");
}
I have added below to ServiceManifest.xml
<Resources>
<Endpoints>
<!-- To enable Service remonting for remoting services V2-->
<Endpoint Name="ServiceEndpointV2" />
<Endpoint Name="ReplicatorEndpoint" />
</Endpoints>
</Resources>
And it doesn't work.. I get the following Exception:
Invalid partition key/ID '{0}' for selector {1}
What am I doing wrong?
In the call to create a service proxy, you must specify a partition key because you are connecting to a stateful service.
long partitionKey = 0L; //TODO: Determine partition key
var proxy = serviceProxyFactory.CreateServiceProxy<ICallMe>(new Uri("fabric:/SFExperiment/Stateful1"), new ServicePartitionKey(partitionKey), TargetReplicaSelector.PrimaryReplica);
Also, make sure you reuse your service proxy factory, instead of creating a new one.
Have a look at this code for example.
Related
I'm confused with Autofac Examples : WebApiExample.OwinSelfHost, the startup class is following:
public class Startup
{
public void Configuration(IAppBuilder app)
{
// In OWIN you create your own HttpConfiguration rather than
// re-using the GlobalConfiguration.
var config = new HttpConfiguration();
config.Routes.MapHttpRoute(
"DefaultApi",
"api/{controller}/{id}",
new { id = RouteParameter.Optional });
var builder = new ContainerBuilder();
// Register Web API controller in executing assembly.
builder.RegisterApiControllers(Assembly.GetExecutingAssembly());
// OPTIONAL - Register the filter provider if you have custom filters that need DI.
// Also hook the filters up to controllers.
builder.RegisterWebApiFilterProvider(config);
builder.RegisterType<CustomActionFilter>()
.AsWebApiActionFilterFor<TestController>()
.InstancePerRequest();
// Register a logger service to be used by the controller and middleware.
builder.Register(c => new Logger()).As<ILogger>().InstancePerRequest();
// Autofac will add middleware to IAppBuilder in the order registered.
// The middleware will execute in the order added to IAppBuilder.
builder.RegisterType<FirstMiddleware>().InstancePerRequest();
builder.RegisterType<SecondMiddleware>().InstancePerRequest();
// Create and assign a dependency resolver for Web API to use.
var container = builder.Build();
config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
// The Autofac middleware should be the first middleware added to the IAppBuilder.
// If you "UseAutofacMiddleware" then all of the middleware in the container
// will be injected into the pipeline right after the Autofac lifetime scope
// is created/injected.
//
// Alternatively, you can control when container-based
// middleware is used by using "UseAutofacLifetimeScopeInjector" along with
// "UseMiddlewareFromContainer". As long as the lifetime scope injector
// comes first, everything is good.
app.UseAutofacMiddleware(container);
// Again, the alternative to "UseAutofacMiddleware" is something like this:
// app.UseAutofacLifetimeScopeInjector(container);
// app.UseMiddlewareFromContainer<FirstMiddleware>();
// app.UseMiddlewareFromContainer<SecondMiddleware>();
// Make sure the Autofac lifetime scope is passed to Web API.
app.UseAutofacWebApi(config);
app.UseWebApi(config);
}
}
The FirstMiddleware and SecondMiddleware code was as following:
public class FirstMiddleware : OwinMiddleware
{
private readonly ILogger _logger;
public FirstMiddleware(OwinMiddleware next, ILogger logger) : base(next)
{
this._logger = logger;
}
public override async Task Invoke(IOwinContext context)
{
this._logger.Write("Inside the 'Invoke' method of the '{0}' middleware.", GetType().Name);
await Next.Invoke(context);
}
}
public class SecondMiddleware : OwinMiddleware
{
private readonly ILogger _logger;
public SecondMiddleware(OwinMiddleware next, ILogger logger) : base(next)
{
this._logger = logger;
}
public override async Task Invoke(IOwinContext context)
{
this._logger.Write("Inside the 'Invoke' method of the '{0}' middleware.", GetType().Name);
await Next.Invoke(context);
}
}
According to the comments, the middleware registration order matters. FirstMiddleware first, then SecondMiddleware. but the output was second middleware was invoked first.
the program logs output here
What's wrong with the order?
This is the autofac official example.WebApiExample.OwinSelfHost
Looks like you've found a bug! I've filed an issue about it on your behalf. You can read more technical details about it there, but the short version is that over the years we've changed some Autofac internals to support .NET Core and this looks like something we've missed.
The workaround until this is fixed will be to register the middleware in reverse order, which isn't awesome because once the fix is applied you'll have to reverse them back. :(
I am using Microsoft's Authentication/Authorization platform to allow User Sign-ins from Azure AD. I would like to log these events into a database. Issue is, since this type of authentication leverages middleware I am not sure how to inject code to trigger a log event.
Please let me know if there exists documentation I haven't yet found and/or how to write up a custom injection to log these events.
Thanks!
I solved my own problem. For any potential usefulness to anyone else in the future I will add what I did below..
I set up my database according to this documentation: https://learn.microsoft.com/en-us/aspnet/core/tutorials/first-mvc-app/adding-model?view=aspnetcore-5.0&tabs=visual-studio
I created this Middleware Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Identity.Web;
using Application.Models;
using Application.Data;
namespace Application.Middleware
{
// You may need to install the Microsoft.AspNetCore.Http.Abstractions package into your project
public class EventLogCaptureMiddleware
{
private readonly RequestDelegate _next;
private readonly EventLogContext _context;
public EventLogCaptureMiddleware(RequestDelegate next, EventLogContext context)
{
_next = next;
_context = context;
}
public Task Invoke(HttpContext httpContext)
{
var eventLogModel = new EventLogViewModel
{
Timestamp = DateTime.Now,
Type = "TEST",
Method = httpContext.Request.Method,
Upn = httpContext.User.Identity.Name,
Resource = $"{httpContext.Request.Scheme}://{httpContext.Request.Host}{httpContext.Request.Path}"
};
_context.Add(eventLogModel);
var tasks = new Task[] { _context.SaveChangesAsync() };
Task.WaitAll(tasks);
return _next(httpContext);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class EventLogCaptureMiddlewareExtensions
{
public static IApplicationBuilder UseEventLogCaptureMiddleware(this IApplicationBuilder builder)
{
return builder.UseMiddleware<EventLogCaptureMiddleware>();
}
}
}
And injected into Startup.cs likeso:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
//Production Exception Handler ex: API connection failed will trigger exception routed to /Home/Error
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
//Handles User Error: 401, 403, 404, etc. Errors caught must land Application side. Errors occured in API with return 500 and be routed via Exception Handler
app.UseStatusCodePagesWithReExecute("/Home/Error", "?status={0}");
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
//Must include Authentication/Authorization under routing
app.UseAuthentication();
app.UseAuthorization();
app.UseEventLogCaptureMiddleware();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
}
I am connecting a web api service to a backend-stateless service.
The Backservice is called MyProject.Management.Company and its code is:
internal sealed class Company: StatelessService,ICompanyManagement
{
private readonly CompanyManagementImpl _impl;
public Tenents(StatelessServiceContext context, CompanyManagementImpl impl)
: base(context)
{
this._impl = impl;
}
/// <summary>
/// Optional override to create listeners (e.g., TCP, HTTP) for this service replica to handle client or user requests.
/// </summary>
/// <returns>A collection of listeners.</returns>
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new[]
{
new ServiceInstanceListener(serviceContext => new FabricTransportServiceRemotingListener(serviceContext, this), "ServiceEndpoint")
};
}
/// <summary>
/// This is the main entry point for your service instance.
/// </summary>
/// <param name="cancellationToken">Canceled when Service Fabric needs to shut down this service instance.</param>
protected override async Task RunAsync(CancellationToken cancellationToken)
{
// TODO: Replace the following sample code with your own logic
// or remove this RunAsync override if it's not needed in your service.
long iterations = 0;
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
ServiceEventSource.Current.ServiceMessage(this.Context, "Working-{0}", ++iterations);
await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken);
}
}
public Task CreateCompany(Company company)
{
return _impl.CreateCompany(company);
}
public Task<List<Company>> GetAllCompanies()
{
return _impl.GetAllCompanies();
}
public Task<Company> GetCompanyById(string companyId)
{
return _impl.GetCompanyById(companyId);
}
}
The code is Listener code is adopted from This Blog Post and even the documentation code doesn't compile documentation the CreateServiceRemotingListenervextension method doesn't exist.
The ICompanyManagement is an interface inherting from IService interface and the implementation is realized via the CompanyManagament that in this phase just returns static objects.
The API is called MyProject.Portal and the controller code is:
public class CompanyController : Controller
{
ICompanyManagement _proxy;
public CompanyController(StatelessServiceContext context)
{
string serviceUri = $"{context.CodePackageActivationContext.ApplicationName}" + "/MyProject.Management.Company";
_proxy = ServiceProxy.Create<ICompanyManagement>(new Uri(serviceUri));
}
// GET: api/Company
[HttpGet]
public async Task<JsonResult> Get()
{
try
{
var result = await _proxy.GetAllCompanies();
return this.Json(result);
}
catch (Exception ex)
{
throw ex;
}
}
}
When the code run it returns the following error.
NamedEndpoint 'V2Listener' not found in the address
'{"Endpoints":{"ServiceEndpoint":"localhost:59286+12a705ed-11a5-4bf5-bafd-84179c966257-131719261525940414-9e876439-9294-4ec9-8b33-05f17515aaf4"}}'
for partition '12a705ed-11a5-4bf5-bafd-84179c966257'
Finally: I am using .netcore v2, service fabric v6.2.274.
Right after usings in your ICompanyManagement file add following line:
[assembly: FabricTransportServiceRemotingProvider(RemotingListener = RemotingListener.V2Listener, RemotingClient = RemotingClient.V2Client)]
In your service (CompanyManagement) manifest (ServiceManifest.xml file) make sure that endpoint is set to version 2:
<Resources>
<Endpoints>
<Endpoint Name="ServiceEndpointV2" />
</Endpoints>
</Resources>
Change your CreateServiceInstanceListeners method to:
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return this.CreateServiceRemotingInstanceListeners();
}
Finally in your web api controller register your service proxy like this:
ICompanyManagement companyManagementClient = ServiceProxy.Create<ICompanyManagement>(new Uri($"fabric:/{applicationName}/{serviceName}"));
If you follow these steps it will work.
I started creating an OSGI project with Spring DM. I Created two bundles, the first one (bundle1) provides a service that changes the order of a recieved string. The seconde one (bundle2) consumes that service and prints the result in the console.
service implementation:
public final class Bundle1ServiceImpl implements Bundle1Service {
public Bundle1ServiceImpl() {
System.out.println("Bundle1ServiceImpl contructor...");
}
public String scramble(String text) {
List charList = new ArrayList();
char[] textChars = text.toCharArray();
for (int i = 0; i < textChars.length; i++) {
charList.add(new Character(textChars[i]));
}
Collections.shuffle(charList);
char[] mixedChars = new char[text.length()];
for (int i = 0; i < mixedChars.length; i++) {
mixedChars[i] = ((Character) charList.get(i)).charValue();
}
return new String(mixedChars);
}}
spring file for service provider :
bundle1-osgi.xml
<service ref="bundle1Service" interface="fr.thispro.bundle1.Bundle1Service" />
bundle1-context.xml
<bean id="bundle1Service" class="fr.thispro.bundle1.internal.Bundle1ServiceImpl">
</bean>
consumer :
public class Bundle2Consumer {
private final Bundle1Service service;
public Bundle2Consumer(Bundle1Service service) {
this.service = service;
}
public void start() {
System.out.println(service.scramble("Text"));
System.out.println("im started");
}
public void stop() {
System.out.println("im stopped");
}}
spring files for consumer:
bundle2-context.xml
<beans:bean id="consumer" class="fr.thispro.bundle2.Bundle2Consumer" init-method="start" destroy-method="stop" lazy-init="false" ><beans:constructor-arg ref="bundle1Service"/>
bundle2-osgi.xml
<reference id="bundle1Service" interface="fr.thispro.bundle1.Bundle1Service" />
The service is well registred referenced and discovered. But the consumer doesn't print anything even when i start it explicitly by start command.
Thanks in adanvance,
I found the problem. You bundle2 incldues an Activtor but you did not define the activator in the Manifest. So the bundle never actually starts up.
If you intended to start the bundle2 using spring dm then the problem is that the jar does not contain the spring xml files.
I have a class that implements multiple interfaces.
I would like to register these interfaces via XML.
All I've found is documentation for the new Fluent Interface.
Is this option supported via XML?
What would be involved in adding this feature?
[Update] This is now possible in Windsor 2.1 or newer. See the documentation for syntax here.
This feature has not been implemented in the XML interpreter as of yet.. however it is not difficult to add support for it via a facility (obviously this technique is also useful when wanting to add other features absent from the existing configuration parser).
So first off we add a facility which will detect when a handler is created for a type, and at the same time will register any of the forwarded services so they point to the existing handler:
public class HandlerForwardingFacility : AbstractFacility
{
IConversionManager conversionManager;
protected override void Init()
{
conversionManager = (IConversionManager)Kernel.GetSubSystem(SubSystemConstants.ConversionManagerKey);
Kernel.HandlerRegistered += new HandlerDelegate(Kernel_HandlerRegistered);
}
void Kernel_HandlerRegistered(IHandler handler, ref bool stateChanged)
{
if (handler is ForwardingHandler) return;
var model = handler.ComponentModel;
if (model.Configuration == null) return;
var forward = model.Configuration.Children["forward"];
if (forward == null) return;
foreach (var service in forward.Children)
{
Type forwardedType = (Type)conversionManager.PerformConversion(service, typeof (Type));
Kernel.RegisterHandlerForwarding(forwardedType, model.Name);
}
}
}
And then of course we need to make use of this in code, for this example I'm going to have a mutant duck/dog component that supports two separate services - IDuck and IDog:
public interface IDog
{
void Bark();
}
public interface IDuck
{
void Quack();
}
public class Mutant : IDog, IDuck
{
public void Bark()
{
Console.WriteLine("Bark");
}
public void Quack()
{
Console.WriteLine("Quack");
}
}
Now to actually configure the container:
<castle>
<facilities>
<facility id="facility.handlerForwarding" type="Example.Facilities.HandlerForwardingFacility, Example" />
</facilities>
<components>
<component id="mutant" service="Example.IDog, Example" type="Example.Mutant, Example">
<forward>
<service>Example.IDuck, Example</service>
</forward>
</component>
</components>
</castle>
And now we can happily execute a test like this:
WindsorContainer container = new WindsorContainer(new XmlInterpreter());
var dog = container.Resolve<IDog>();
var duck = container.Resolve<IDuck>();
Assert.AreSame(dog, duck);
Hope this helps.