Based on the documentation available at https://learn.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-communication-wcf
Does a Stateful Service in WCF supports Sessions?
Tried the below attributes but it does not work.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class CalculatorService : ICalculator
[ServiceContract(SessionMode=SessionMode.Required)]
public interface ICalculator
Any changes I need to do WCFCommunicationListener to support sessions?
protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
{
return new[]
{
new ServiceReplicaListener( (context) =>
new WcfCommunicationListener<ICalculator>(context, new CalculatorService(),WcfUtility.CreateTcpListenerBinding(),"WCFServiceEndpoint")
)
};
}
I could able to achieve sessions in WCF Stateful Service using the following code.
protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
{
return new[]
{
new ServiceReplicaListener( (context) =>
{
var wcfCommunicationListener = new WcfCommunicationListener<ICalculator>(context, new CalculatorService(),WcfUtility.CreateTcpListenerBinding(),"WCFServiceEndpoint");
wcfCommunicationListener.ServiceHost.Description.Behaviors.Remove(typeof(ServiceBehaviorAttribute));
wcfCommunicationListener.ServiceHost.Description.Behaviors.Add(new ServiceBehaviorAttribute() { InstanceContextMode = InstanceContextMode.PerSession});
return wcfCommunicationListener;
}
)
};
}
Related
Runtime:
I am using .NET 6 and EF Core in an Azure Function. To connect with an Azure SQL Database, I want to use AAD-Authentication, so I configured my DbContext as follows:
public class FunctionContext : DbContext {
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
SqlConnection connection = new();
var credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions { ManagedIdentityClientId = Environment.GetEnvironmentVariable("userAssignedClientId") });
var token = credential.GetToken(new Azure.Core.TokenRequestContext(new[] { "https://database.windows.net/.default" }));
connection.ConnectionString = Environment.GetEnvironmentVariable("SqlConnectionString");
connection.AccessToken = token.Token;
optionsBuilder.UseSqlServer(connection);
optionsBuilder.LogTo(Console.WriteLine);
optionsBuilder.UseExceptionProcessor();
optionsBuilder.EnableSensitiveDataLogging();
}
}
The connection string "SqlConnectionString" is available as an environment variable and has the following form:
"Server=demo.database.windows.net; Database=testdb";
Migrations:
I want to update the database with every deployment. I am using Azure DevOps pipelines to deploy the application, and I have a service principal that I can use to log in. So I need to use a connection string that looks like this:
"Server=demo.database.windows.net; Authentication=Active Directory Service Principal; Encrypt=True; Database=testdb; User Id=AppId; Password=secret";
Is there a possiblity to use two different connection strings for runtime and migrations?
I tried modifiying the Factory method that Update-Database uses to create the context, but since the OnConfiguring method pasted above is called anyway, I still end up with the same connection string.
The solution I found was not to implement the OnConfiguring method, but pass the configuration directly in the Startup.cs as follows:
Startup.cs (Context at runtime)
internal class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddDbContext<FunctionContext>(options =>
{
SqlConnection connection = new();
var credentialOptions = new DefaultAzureCredentialOptions {
ManagedIdentityClientId = Environment.GetEnvironmentVariable("userAssignedClientId")};
var credential = new DefaultAzureCredential(credentialOptions);
var token = credential.GetToken(new Azure.Core.TokenRequestContext(new[] { "https://database.windows.net/.default" }));
connection.ConnectionString = Environment.GetEnvironmentVariable("SqlConnectionString");
connection.AccessToken = token.Token;
options.UseSqlServer(connection);
options.LogTo(Console.WriteLine);
options.UseExceptionProcessor();
options.EnableSensitiveDataLogging();
});
}
}
DesignTimeFunctionContextFactory.cs (Context at design time)
public class DesignTimeFunctionContextFactory : IDesignTimeDbContextFactory<FunctionContext>
{
public FunctionContext CreateDbContext(string[] args)
{
var optionsBuilder = new DbContextOptionsBuilder<FunctionContext>();
optionsBuilder.UseSqlServer(Environment.GetEnvironmentVariable("SqlAdminConnectionString"), options => options.EnableRetryOnFailure());
return new FunctionContext(optionsBuilder.Options);
}
}
If your Service Fabric service has multiple listeners, I have noticed that you can create the CreateServiceInstanceListeners using 2 different patterns. (.Net Framework / Statefull / Stateless)
The first method is yielding returns while the second one is returning an array.
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
yield return new ServiceInstanceListener(serviceContext => new OwinCommunicationListener(Startup.ConfigureApp, serviceContext, ServiceEventSource.Current, "ServiceEndpoint")),
yield return new ServiceInstanceListener(wcfContext => new WcfCommunicationListener<IWebApi>(wcfServiceObject: this, serviceContext: wcfContext,endpointResourceName: "WcfEndpoint",listenerBinding: WcfUtility.CreateTcpListenerBinding()),"WcfEndpoint")
}
versus
protected override IEnumerable<ServiceInstanceListener> CreateServiceInstanceListeners()
{
return new[]
{
new ServiceInstanceListener(serviceContext => new OwinCommunicationListener(Startup.ConfigureApp, serviceContext, ServiceEventSource.Current, "ServiceEndpoint")),
new ServiceInstanceListener(wcfContext => new WcfCommunicationListener<IWebApi>(wcfServiceObject: this, serviceContext: wcfContext,endpointResourceName: "WcfEndpoint",listenerBinding: WcfUtility.CreateTcpListenerBinding()),"WcfEndpoint")
};
}
Is there any advantage of using one over the other?
There is no big difference. The returned enumerable is iterated once per service instance.
Here is the code snipped from sources on GitHub.
if (this.instanceListeners == null)
{
this.instanceListeners = this.userServiceInstance.CreateServiceInstanceListeners();
}
var endpointsCollection = new ServiceEndpointCollection();
var listenerOpenedCount = 0;
foreach (var entry in this.instanceListeners)
{
var communicationListener = entry.CreateCommunicationListener(this.serviceContext);
this.AddCommunicationListener(communicationListener);
var endpointAddress = await communicationListener.OpenAsync(cancellationToken);
endpointsCollection.AddEndpoint(entry.Name, endpointAddress);
listenerOpenedCount++;
var traceMsg = entry.Name.Equals(ServiceInstanceListener.DefaultName)
? "Opened communication listener with default name."
: $"Opened communication listener with name {entry.Name}.";
ServiceTrace.Source.WriteInfoWithId(TraceType, this.traceId, traceMsg);
}
First the error:
Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and
then later trying to use the same context instance elsewhere in your
application. This may occur if you are calling Dispose() on the
context, or wrapping the context in a using statement. If you are
using dependency injection, you should let the dependency injection
container take care of disposing context instances.
Object name: 'MemberContext'.
I have 3 projects, Domain, API and WebSPA app.
Domain has 2 modules, DomainModule and MediatorModule
public class DomainModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(typeof(MemberContext).Assembly)
.AsImplementedInterfaces()
.InstancePerLifetimeScope(); // via assembly scan
builder.RegisterType<MemberContext>().AsSelf()
.InstancePerLifetimeScope(); // or individually
}
}
public class MediatorModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
// enables contravariant Resolve() for interfaces with single contravariant ("in") arg
builder
.RegisterSource(new ContravariantRegistrationSource());
// mediator itself
builder
.RegisterType<Mediator>()
.As<IMediator>()
.InstancePerLifetimeScope();
// request handlers
builder
.Register<SingleInstanceFactory>(ctx =>
{
var c = ctx.Resolve<IComponentContext>();
return t =>
{
object o;
return c.TryResolve(t, out o) ? o : null;
};
})
.InstancePerLifetimeScope();
// notification handlers
builder
.Register<MultiInstanceFactory>(ctx =>
{
var c = ctx.Resolve<IComponentContext>();
return t => (IEnumerable<object>) c.Resolve(typeof(IEnumerable<>).MakeGenericType(t));
})
.InstancePerLifetimeScope();
}
}
In API project I have also 2 modules, ApplicationModule and again MediatorModule same as the one above.
public class ApplicationModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(typeof(Startup).Assembly)
.AsImplementedInterfaces()
.InstancePerLifetimeScope(); // via assembly scan
builder.RegisterType<MemberContext>().AsSelf().InstancePerLifetimeScope(); // or individually
}
}
No, when I debug I can see that member context gets newed up on each request, yet on second request, it throws above error. To make sure I am not going crazy, I modified constructor of dbcontext to create an id for context so i can verify they are different. What am I doing wrong?
public MemberContext(DbContextOptions<MemberContext> options) : base(options)
{
MemberContextId = Guid.NewGuid();
Console.WriteLine("member context created: " + MemberContextId);
}
Here is the startup in API
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
// .AllowCredentials()
);
});
services.AddMvc()
.AddControllersAsServices();//Injecting Controllers themselves thru DI
//For further info see: http://docs.autofac.org/en/latest/integration/aspnetcore.html#controllers-as-services
AddSwaggerGen(services);
//var connection = Configuration["ConnectionString"];
//services.AddDbContext<MemberContext>(options => options.UseSqlServer(connection),ServiceLifetime.Scoped);
services.AddEntityFrameworkSqlServer()
.AddDbContext<MemberContext>(options =>
{
options.UseSqlServer(Configuration["ConnectionString"]
//,sqlServerOptionsAction: sqlOptions =>
//{
// sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().Assembly.GetName().Name);
// sqlOptions.EnableRetryOnFailure(maxRetryCount: 10, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null);
//}
);
},
ServiceLifetime.Scoped //Showing explicitly that the DbContext is shared across the HTTP request scope (graph of objects started in the HTTP request)
);
var container = new ContainerBuilder();
container.Populate(services);
container.RegisterAssemblyModules(typeof(VIN.Members.Domain.Entities.Member).Assembly,
typeof(Startup).Assembly);
return new AutofacServiceProvider(container.Build());
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
//NOTE: must be before UseMVC !!!
app.UseCors("CorsPolicy");
app.UseMvc();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
}
private void AddSwaggerGen(IServiceCollection services)
{
services.AddSwaggerGen(options =>
{
options.DescribeAllEnumsAsStrings();
options.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info
{
Title = "VIN Members HTTP API",
Version = "v1",
Description = "Members Service HTTP API",
TermsOfService = "Terms Of Service"
});
});
}
}
UPDATE:
What I am trying to do is delete a record. On client side code looks like this
onDelete(item: IMember) {
//TODO: replace this with dialog service component
if (window.confirm('Are sure you want to delete this member?')) {
//put your delete method logic here
this.service.deleteMember(item).subscribe(x => {
this.getMembers();
});
}
}
this delete request gets mapped to a controller that passes it to mediator
Controller
// DELETE api/members/5
[HttpDelete("{id}")]
public void Delete(Guid id)
{
var command = new DeleteMember.Command(id);
_mediator.Send(command).ConfigureAwait(false);
}
and finally handler
public class DeleteMember
{
public class Command : IRequest
{
public Command(Guid memberId)
{
Guard.NotNull(memberId, nameof(memberId));
MemberId = memberId;
}
public Guid MemberId { get; }
}
public class Handler : AsyncRequestHandler<Command>
{
private MemberContext _context;
public Handler(MemberContext context)
{
_context = context;
Console.WriteLine("Delete member context: " + context.MemberContextId);
}
protected override async Task HandleCore(Command cmd)
{
try
{
var member = await _context.FindAsync<Member>(cmd.MemberId);//.ConfigureAwait(false);
// if (member != null)
//// {
_context.Remove(member);
await _context.SaveChangesAsync().ConfigureAwait(false);
// }
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
}
As you can see there is no code that disposes that context. Scratching my head.
See this commented out check for member if null. That was throwing error as well, I commented it out just to see what will happen, and now it throws as SaveChangesAsync.
As request completes, context gets disposed. Since command handler uses SaveChangesAsync(), context is disposed before save completes. Culprit is controller method :). It should be async as well.
[HttpDelete("{id}")]
public async Task Delete(Guid id)
{
var command = new DeleteMember.Command(id);
await _mediator.Send(command).ConfigureAwait(false);
}
Your DbContext is scoped, meaning that Dependency Injection will return the same DbContext object every time one is asked for within the same HTTP request (in the case of ASP.NET).
That means that you should not be calling Dispose on your DbContext (otherwise that same object can't be used a second time). That seems to be what is happening to you, intentionally or not.
That does mean you should not be using using with it. Are you using using anywhere in your code against your DbContext?
I don't think you showed the line where the Exception is being thrown.
Update:
Try overriding Dispose in your MemberContext class. Something like this:
public override void Dispose() {
base.Dispose();
}
But just set a breakpoint there. When it breaks (if it does) check the stack trace and see what called it.
This can also be caused by having async void instead of async Task within WebAPI in my experience.
enter image description hereI want to implement versioning in azure mobile service so I am not able to find the endpoints to configure and based on version number it will take the code.
Now URL is
http://{{host}}/tables/EntityName
but my requirement is
http://{{host}}/tables/v1/EntityName
or
http://{{host}}/tables/v2/EntityName
So please help me on this.
According to your requirement, I followed Microsoft/aspnet-api-versioning to check the versioning with azure mobile app project. You could refer to the details below to achieve your purpose.
Install the Microsoft.AspNet.WebApi.Versioning package.
Enable the API versioning in your Startup.MobileApp.cs as follows:
public static void ConfigureMobileApp(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
var constraintResolver = new DefaultInlineConstraintResolver()
{
ConstraintMap =
{
["apiVersion"] = typeof( ApiVersionRouteConstraint )
}
};
config.AddApiVersioning();
config.MapHttpAttributeRoutes(constraintResolver);
//your mobile configuration
app.UseWebApi(config);
}
For your TableController, you could define it as follows:
tables/v1.0/todoitem
[ApiVersion("1.0")]
[RoutePrefix("tables/v{version:apiVersion}/todoitem")]
public class TodoItemController : TableController<ToDoItem>
{
private MobileServiceContext context;
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
context = new MobileServiceContext();
DomainManager = new EntityDomainManager<ToDoItem>(context, Request);
}
[Route]
// GET tables/v{version}/todoitem
public IQueryable<ToDoItem> GetAllTodoItems()
{
return Query();
}
}
tables/v2.0/todoitem
[ApiVersion("2.0")]
[ControllerName("TodoItem")]
[RoutePrefix("tables/v{version:apiVersion}/todoitem")]
public class TodoItemV2Controller : TableController<ToDoItem>
{
private MobileServiceContext context;
protected override void Initialize(HttpControllerContext controllerContext)
{
base.Initialize(controllerContext);
context = new MobileServiceContext();
DomainManager = new EntityDomainManager<ToDoItem>(context, Request);
}
[Route]
// GET tables/v{version}/todoitem
public IQueryable<ToDoItem> GetAllTodoItems()
{
return Query();
}
}
For more details, you could refer to ASP.NET API Versioning.
I have a custom Nancy Bootstrapper which uses StructureMapNancyBootstrapper but the issue is the same regardless of container.
public class CustomNancyBootstrapper : StructureMapNancyBootstrapper
{
protected override void RequestStartup(IContainer container, IPipelines pipelines, NancyContext context)
{
var auth = container.GetInstance<ICustomAuth>();
auth.Authenticate(context);
}
}
I want to write a test to assert that Authenticate is called with the context... something like this...
[Test]
public void RequestStartup_Calls_CustomAuth_Authenticate_WithContext()
{
// set up
var mockAuthentication = new Mock<ICustomAuth>();
var mockContainer = new Mock<IContainer>();
var mockPipelines = new Mock<IPipelines>();
var context = new NancyContext();
mockContainer.Setup(x => x.GetInstance<ICustomAuth>()).Returns(mockAuthentication.Object);
// exercise
_bootstrapper.RequestStartup(_mockContainer.Object, _mockPipelines.Object, context);
// verify
mockAuthentication.Verify(x => x.Authenticate(context), Times.Once);
}
The problem is that I can't call RequestStartup because it's protected as defined in NancyBootstrapperBase.
protected virtual void RequestStartup(TContainer container, IPipelines pipelines, NancyContext context);
Is there a "proper"/"offical" Nancy way to do this without creating another derived class and exposing the methods as that just seems like a hack?
Thanks
I guess you can "fake" the request by using Browser from Nancy.Testing:
var browser = new Browser(new CustomNancyBootstrapper());
var response = browser.Get("/whatever");
There is a good set of articles about testing NancyFx application:
http://www.marcusoft.net/2013/01/NancyTesting1.html
Turns out Nancy offers a IRequetStartup interface so you can take the code out of the custom bootstrapper and do something like this...
public class MyRequestStart : IRequestStartup
{
private readonly ICustomAuth _customAuthentication;
public MyRequestStart(ICustomAuth customAuthentication)
{
if (customAuthentication == null)
{
throw new ArgumentNullException(nameof(customAuthentication));
}
_customAuthentication = customAuthentication;
}
public void Initialize(IPipelines pipelines, NancyContext context)
{
_customAuthentication.Authenticate(context);
}
}
and the test is easy and concise
[Test]
public void When_Initialize_Calls_CustomAuth_Authenticate_WithContext()
{
// set up
var mockAuth = new Mock<ICustomAuth>();
var requestStartup = new MyRequestStart(mockAuth.Object);
var mockPipeline = new Mock<IPipelines>();
var context = new NancyContext();
// exercise
requestStartup.Initialize(mockPipeline.Object, context);
// verify
mockAuth.Verify(x => x.Authenticate(context), Times.Once);
}
https://github.com/NancyFx/Nancy/wiki/The-Application-Before%2C-After-and-OnError-pipelines#implementing-interfaces