Is there a way to set the timeout for CreateSprocAccessor(...) in Enterprise Library 5.0?
Cause the default timeout is not working for long stored procedures.
First thing: adding a timeout in the connection string doesn't solve the problem.
I found a workaround: You can modify the source code of EL 5.0 and generate a new custom DLL.
in ...\EntLib50Src\Blocks\Data\Src\Data\SprocAccessor.cs
in Execute(...) method
add this code just before return: command.CommandTimeout = 120; // 2 mins
Compile and use the new Microsoft.Practices.EnterpriseLibrary.Data.dll that you can find in ...\EntLib50Src\Blocks\bin\Debug
actually, I think there is no need to have your own copy of EL. In order to keep original EL binaries unchanged, you just can do the follow:
inherit from SprocAccessor class
overwrite Execute() method, put here the same code as in SprocAccessor.Execute() but adding the timeout part
you will need also to keep in local variable (in your new class) procedureName as this variable is private in SprocAccessor class
some thing like this:
public class SprocAccessorWithTimeout<T> : SprocAccessor<T>
{
readonly string procedureName;
public SprocAccessorWithTimeout(Database database, string procedureName, IRowMapper<T> rowMapper) : base(database, procedureName, rowMapper)
{
this.procedureName = procedureName;
}
public override IEnumerable<T> Execute(params object[] parameterValues)
{
using (var command = Database.GetStoredProcCommand(this. procedureName))
{
command.CommandTimeout = MaxSpExecutionTimeout;
if (parameterValues.Length > 0)
{
Database.AssignParameters(command, parameterValues);
}
return base.Execute(command);
}
}
}
Related
I need to dynamically creates controllers in a ASP.NET Core 6 MVC application.
I found some way to somewhat achieve this but not quite.
I'm able to dynamically add my controller but somehow it reflects only on the second request.
So here is what I do: first I initialize my console app as follows:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Mvc.Infrastructure;
namespace DynamicControllerServer
{
internal class Program
{
static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
ApplicationPartManager partManager = builder.Services.AddMvc().PartManager;
// Store thePartManager in my Middleware to be able to add controlelr after initialization is done
MyMiddleware._partManager = partManager;
// Register controller change event
builder.Services.AddSingleton<IActionDescriptorChangeProvider>(MyActionDescriptorChangeProvider.Instance);
builder.Services.AddSingleton(MyActionDescriptorChangeProvider.Instance);
var app = builder.Build();
app.UseAuthorization();
app.MapControllers();
// Add Middleware which is responsible to cactn the request and dynamically add the missing controller
app.UseMiddleware<MyMiddleware>();
app.RunAsync();
Console.WriteLine("Server has been started successfully ...");
Console.ReadLine();
}
}
}
Then my middleware looks like this: it basically detects that there is the "dynamic" keyword in the url. If so, it will load the assembly containing the DynamicController:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using System;
using System.Reflection;
namespace DynamicControllerServer
{
public class MyMiddleware
{
public RequestDelegate _next { get; }
private string dllName = "DynamicController1.dll";
static public ApplicationPartManager _partManager = null;
public MyMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
if (httpContext.Request.Path.HasValue)
{
var queryParams = httpContext.Request.Path.Value;
if(httpContext.Request.Path.Value.Contains("api/dynamic"))
{
// Dynamically load assembly
Assembly assembly = assembly = Assembly.LoadFrom(#"C:\Temp\" + dllName);
// Add controller to the application
AssemblyPart _part = new AssemblyPart(assembly);
_partManager.ApplicationParts.Add(_part);
// Notify change
MyActionDescriptorChangeProvider.Instance.HasChanged = true;
MyActionDescriptorChangeProvider.Instance.TokenSource.Cancel();
}
}
await _next(httpContext); // calling next middleware
}
}
}
The ActionDescriptorChange provider looks like this:
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.Extensions.Primitives;
namespace DynamicControllerServer
{
public class MyActionDescriptorChangeProvider : IActionDescriptorChangeProvider
{
public static MyActionDescriptorChangeProvider Instance { get; } = new MyActionDescriptorChangeProvider();
public CancellationTokenSource TokenSource { get; private set; }
public bool HasChanged { get; set; }
public IChangeToken GetChangeToken()
{
TokenSource = new CancellationTokenSource();
return new CancellationChangeToken(TokenSource.Token);
}
}
}
Dynamic controller is in separate dll and is very simple:
using Microsoft.AspNetCore.Mvc;
namespace DotNotSelfHostedOwin
{
[Route("api/[controller]")]
[ApiController]
public class DynamicController : ControllerBase
{
public string[] Get()
{
return new string[] { "dynamic1", "dynamic1", DateTime.Now.ToString() };
}
}
}
Here are the packages used in that project:
<PackageReference Include="Microsoft.AspNetCore" Version="2.2.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.2.3" />
This works "almost" fine ... when first request is made to:
https://localhost:5001/api/dynamic
then it goes in the middleware and load the assembly, but returns a 404 error.
Then second request will actually work as expected:
Second request returns the expected result:
I must doing it wrong and probably my middleware is executed too late in the flow to reflect the dynamic controller right away.
Question is: what should be the proper way to achieve this?
Second question I have is say now the external dll holding our dynamic controller is updated.
How can I reload that controller to get the new definition?
Any help would be appreciated
Thanks in advance
Nick
Here is the answer to my own question in case it can help somebody out there.
It seems building and loading the controller from the middleware will always end up with failure on the first call.
This makes sense since we are already in the http pipeline.
I end up doing same thing from outside the middleware.
Basically my application detect a change in the controller assembly, unload the original assembly and load the new one.
You cannot use the Default context since it will not allow reloading different dll for same assembly:
var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath); // Produce an exception on updates
To be able to reload new dll for same assembly, I’m loading each controller in its own assembly context. To do that you need to create your own class deriving from AssemblyLoadContext and managing assembly load:
public class MyOwnContext: AssemblyLoadContext
{
// You can find lots of example in the net
}
When you want to unload the assembly, you just unload the context:
MyOwnContextObj.Unload();
Now to add or remove the controller on the fly, you need to keep reference of the PartManager and the ApplicationPart.
To add controller
ApplicationPart part = new AssemblyPart(assembly);
_PartManager.ApplicationParts.Add(part);
To remove:
_PartManager.ApplicationParts.Remove(part);
On course once done, still use following piece of code to acknowledge the change:
MyActionDescriptorChangeProvider.Instance.HasChanged = true;
MyActionDescriptorChangeProvider.Instance.TokenSource.Cancel();
That allow updating controller on the fly with no interruption of service.
Hope this helps people out there.
I have done a similar solution (used for managing a web app plugins) with some differences that may help you:
List all the external assemblies in a config file or appsettings.json so all the dll names and/or addresses are known at startup
Instead of registering controllers when they are called, register them at program.cs/start up :
//Foreah dllName from settings file
var assembly = Assembly.LoadFrom(#"Base address" + dllNameLoadedFromSettings);
var part = new AssemblyPart(assembly);
services.AddControllersWithViews()
.ConfigureApplicationPartManager(apm => apm.ApplicationParts.Add(part));
// Any other configuration based on the usage you want
Second: I usually keep plugin dlls in the bin folder so when using IIS as soon as a dll file in bin is changed the upper-level app is automatically reset. So your second question would be solved too.
IoT modules can be created from the environment using :
ModuleClient.CreateFromEnvironmentAsync(settings)
However, there does not seem to be an equivalent method for devices. For now, I am setting the device connection string in the program to test it out, but is there a better way to read teh connection string from iotedge/config.yaml for all the edge devices deployed out there?
Methods to do so for .NET and python would be appreciated.
You can use a yaml parse library to deserialize the document, such as YamlDotNet. In fact, you can refer to YamlDocument in iot edge. But in the class, it does not provide a method to get the key value. Please refer to following code.
public class YamlDocument
{
readonly Dictionary<object, object> root;
public YamlDocument(string input)
{
var reader = new StringReader(input);
var deserializer = new Deserializer();
this.root = (Dictionary<object, object>)deserializer.Deserialize(reader);
}
public object GetKeyValue(string key)
{
if(this.root.ContainsKey(key))
{
return this.root[key];
}
foreach(var item in this.root)
{
var subItem = item.Value as Dictionary<object, object>;
if(subItem != null && subItem.ContainsKey(key))
{
return subItem[key];
}
}
return null;
}
}
And then you can get the device connection string from the config.yaml. If you use python, you can import yaml library to analysis the file.
StreamReader sr = new StreamReader(#"C:\ProgramData\iotedge\config.yaml");
var yamlString = sr.ReadToEnd();
var yamlDoc = new YamlDocument(yamlString);
var connectionString = yamlDoc.GetKeyValue("device_connection_string");
Console.WriteLine("{0}", connectionString);
To get the config file from the host, add the following to the docker deployment file. Note that the source file is config1.yaml which is the same as config.yaml except that it has read permissions for everyone not just root.
"createOptions": "{\"HostConfig\":{\"Binds\":[\"/etc/iotedge/config1.yaml:/app/copiedConfig.yaml\"]}}"
With the above line in place, the copiedConfig.yaml file can be used in the container, along with #Michael Xu's parsing code to derive teh connection string.
Long term, one may want to use the device provisioning service anyway but hope this helps for folks using device conenction strings for whatever reason..
I have the following StructureMap registrations that work in version 2.6.4 and I'm finally upgrading to the latest SM (3.1.2 as of this writing). And need to update it since there doesn't appear to be a IContext.BuildStack anymore.
Here is the old working version with 2.6.4:
initialization.For(typeof(IRepository<,>))
.Use(context =>
{
var genericArgs = context.BuildStack.Current.RequestedType.GetGenericArguments();
return RepositoryFactory.GetInstance(genericArgs[0], genericArgs[1], repositoryName);
}
);
So I figured that changing it to this would work:
initialization.For(typeof (IRepository<,>))
.Use("IRepository<,>", context =>
{
var genericArgs = context.ParentType.GetGenericArguments();
return RepositoryFactory.GetInstance(genericArgs[0], genericArgs[1],
repositoryName);
}
);
But context.ParentType is null. When I look at context.RootType it is set to System.Object which is obviously not what I want.
My test code to get an instance of this repository is:
var userRepository = ObjectFactory.GetInstance<IRepository<User, Guid>>();
I don't see any other property that has this information, but I'm guessing I am missing something.
You are not missing something. On GitHub someone posted a simular question: https://github.com/structuremap/structuremap/issues/288.
Jeremy Miller, the author of structuremap, responded:
It's going to have to be new development. It'll have to come in a 3.2 version.
The suggested workaround is to create a custom instance and override the ClosingType method. You can do this as follows:
public class CustomInstance : Instance
{
public override IDependencySource ToDependencySource(Type pluginType)
{
throw new NotImplementedException();
}
public override Instance CloseType(Type[] types)
{
var repository = RepositoryFactory.GetInstance(types[0], types[1], repositoryName);
return new ObjectInstance(repository);
}
public override string Description
{
get { throw new NotImplementedException(); }
}
public override Type ReturnedType
{
get { return typeof (IRepository<,>); }
}
}
Now, you only have to connect the open generic type to the closing type like this:
initialization.For(typeof (IRepository<,>)).Use(new CustomInstance());
I added a new example to the StructureMap 3 codebase for this scenario based on your question:
https://github.com/structuremap/structuremap/blob/master/src/StructureMap.Testing/Acceptance/builder_for_open_generic_type.cs
I have to ask though, what's the purpose of something like RepositoryBuilder if you're using an IoC container?
Anyway, this implementation should be more efficient than the older Reflection-heavy approach.
I am using Castle to create my database context based on a given interface. I have the following code in my Installer class and this works fine at the moment.
private ConfigureDelegate ConfigureContext()
{
return p => p.Named(p.ServiceType.Name)
.LifeStyle.PerWebRequest
.DependsOn(new { connectionString = ConfigurationManager.ConnectionStrings["conStringName"].ConnectionString });
}
However i now have a scenario where this installer will find more than one concrete implementation of my interface, where each one should have a different connection string supplied.
Is this possible - if so, could someone point me in the right direction.
TIA
Yes, it's possible if you can write a piece of code that provides the connection string name for the service. Perhaps something like this:
private ConfigureDelegate ConfigureContext()
{
return p => p.Named(p.ServiceType.Name)
.LifeStyle.PerWebRequest
.DependsOn(new
{
connectionString =
ConfigurationManager
.ConnectionStrings[GetConnectionName(p.ServiceType.Name)]
.ConnectionString
});
}
private string GetConnectionName(string serviceName)
{
// return the connection name
}
I'm trying to call ObjectContext.ExecuteFunction from my objectcontext object in the repository of my site.
The repository is generic, so all I have is an ObjectContext object, rather than one that actually represents my specific one from the Entity Framework.
Here's an example of code that was generated that uses the ExecuteFunction method:
[global::System.CodeDom.Compiler.GeneratedCode("System.Data.Entity.Design.EntityClassGenerator", "4.0.0.0")]
public global::System.Data.Objects.ObjectResult<ArtistSearchVariation> FindSearchVariation(string source)
{
global::System.Data.Objects.ObjectParameter sourceParameter;
if ((source != null))
{
sourceParameter = new global::System.Data.Objects.ObjectParameter("Source", source);
}
else
{
sourceParameter = new global::System.Data.Objects.ObjectParameter("Source", typeof(string));
}
return base.ExecuteFunction<ArtistSearchVariation>("FindSearchVariation", sourceParameter);
}
But what I would like to do is something like this...
public class Repository<E, C> : IRepository<E, C>, IDisposable
where E : EntityObject
where C : ObjectContext
{
private readonly C _ctx;
// ...
public ObjectResult<E> ExecuteFunction(string functionName, params[])
{
// Create object parameters
return _ctx.ExecuteFunction<E>(functionName, /* parameters */)
}
}
Anyone know why I have to call ExecuteFunction from base instead of _ctx?
Also, is there any way to do something like I've written out? I would really like to keep my repository generic, but with having to execute stored procedures it's looking more and more difficult...
Update: Here's what I've tried and the method does not show up in intellisense/it gives me an error when I try to compile with it
public ArtistSearchVariation findSearchVariation(string source)
{
System.Data.Objects.ObjextContext _ctx = new ObjectContext(/* connection string */);
System.Data.Objects.ObjectParameter sourceParam = new ObjectParameter("Source", source);
return _ctx.ExecuteFunction<ArtistSearchVariation>("FindSearchVariation", sourceParam);
}
Thanks,
Matt
You don't have to use base.ExecuteFunction, the ExecuteFunction method (and overloads) are public, not protected, so you can call them from external sites. Are you having trouble calling it?