we are trying to upgrade to CSLA 6.
now, we are getting a message:
"ConnectionManager is obsolete, use dependency injection ... use ApplicationContext.LocalContext"
for this code:
using (var ctx = ConnectionManager<OracleConnection>.GetManager("dbEndpoint", true))
We've tried this code snippet but all connections is NULL.
Could you please help us to correctly get Connection?
var services = new ServiceCollection();
services.AddCsla();
var provider = services.BuildServiceProvider();
DataPortalFactory = provider.GetRequiredService<IDataPortalFactory>();
var appContext = provider.GetRequiredService<Csla.ApplicationContext>();
var conn1 = appContext.LocalContext.GetValueOrNull("dbEndpoint");
var conn2 = appContext.LocalContext.GetValueOrNull("__db:default-dbEndpoint");
var conn3 = appContext.LocalContext["dbEndpoint"];
var conn4 = appContext.LocalContext["__db:default-dbEndpoint"];
another experiment:
....
var CONNECTION_ORACLE = new OracleConnection(ConfigurationManager.ConnectionStrings["dbEndpoint"].ConnectionString);
services.AddScoped<IDbConnection>(o => CONNECTION_ORACLE);
....
var provider = services.BuildServiceProvider();
...
var connectionResolved = provider.GetRequiredService<IDbConnection>();
appContext.LocalContext.Add("dbEndpoint", connectionResolved);
then connection is not null;
and inside of Factory is successfully resolved by DI:
public DocFactory(ApplicationContext appContext, IDbConnection connection) : base(
appContext)
{
_connection = connection;
}
then
[Fetch]
public Doc_Fetch(DocCriteria criteria)
{
bool cancel = false;
OnFetching(criteria, ref cancel);
if (cancel) return null;
Doc item = null;
OracleConnection connection = _connection as OracleConnection;
connection is Closed (but NOT null!!). it's possible to open it but if close it, somebody else consuming it will face with a problem or child objects also will face problem with closed connection.
so, making ConnectionManager as Obsolete may be not so obvious way to go. But ConnectionManager was very useful for counting open connection, supporting transactions etc
Could you please provide a workaround for it.
more attempts:
var connectionString =
ConfigurationManager.ConnectionStrings["dbEndpoint"].ConnectionString;
..
appContext.ClientContext.Add("DBConnectionString", connectionString );
...
Factory
using (var connection = new OracleConnection(ApplicationContext.ClientContext["DBConnectionString"].ToString()))
{
connection.Open();
Your DAL should require that a database connection be injected.
public class MyDal : IDisposable
{
public MyDal(OracleConnection connection)
{
Connection = connection;
}
private OracleConnection Connection { get; set; }
public MyData GetData()
{
// use Connection to get the data
return data;
}
public void Dispose()
{
Connection.Dispose();
}
}
Then in the app server startup code, register your DAL type(s) and also register your connection type.
services.AddScoped(typeof(OracleConnection), () =>
{
// initialize the connection here
return connection;
});
services.AddScoped<MyDal>();
Then, in your data portal operation method (such as create, fetch, etc.), inject your DAL:
[Fetch]
private void Fetch([Inject] MyDal dal)
{
var data = dal.GetData();
}
Related
I have question about web api and Repository may be its a duplicate question.
but i tried to search on it and i did not get any satisfactory answer.
In my Repository i am getting data with the help of httpclient.
My question is that i can get an error inside my response or i can get required json data which i can map to my product class.I am returning IEnumerable.
1) If i get an error how can i bubble it up to controller and display an error to user.
2) Return the MessageResponse instead of IEnumerable and handle it inside the controller.
What is the best way.
enter code here
public interface IProduct{
Task<IEnumerable<Product>> All();
}
public class Product:IProduct
{
public async Task<IEnumerable<Product>> All(){
var ResponseMessage=//some response.
}
}
You could customize a ApiException which is used to get the error message of the response, and call the UseExceptionHandler in your startup.cs ,refer to the following :
ProductRep
public class ProductRep : IProduct
{
private readonly HttpClient _client;
public ProductRep(HttpClient client)
{
_client = client;
}
public async Task<IEnumerable<Product>> All()
{
List<Product> productlist = new List<Product>();
var response = await _client.GetAsync("https://localhost:44357/api/values/GetProducts");
string apiResponse = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode == false)
{
JObject message = JObject.Parse(apiResponse);
var value = message.GetValue("error").ToString();
throw new ApiException(value);
}
productlist = JsonConvert.DeserializeObject<List<Product>>(apiResponse);
return productlist;
}
public class ApiException : Exception
{
public ApiException(string message): base(message)
{ }
}
}
Startup.cs
app.UseExceptionHandler(a => a.Run(async context =>
{
var feature = context.Features.Get<IExceptionHandlerPathFeature>();
var exception = feature.Error;
var result = JsonConvert.SerializeObject(new { error = exception.Message });
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(result);
}));
I have a .NET Core 2.1 application. In Startup.cs configuration method, I use:
services.AddDbContext<ApplicationDbContext>(options =
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
...
services.AddMemoryCache();
Then in my controller:
public class DropDownListController : Controller
{
private readonly ApplicationDbContext _context;
private readonly IMemoryCache _memoryCache;
private const string ProvidersCacheKey = "providers";
private const string AgenciesCacheKey = "agencies";
public DropDownListController(ApplicationDbContext context, IMemoryCache memoryCache )
{
_context = context;
_memoryCache = memoryCache;
}
}
and in the controller also, the method to get the dropdownlist:
public JsonResult GetProvider()
{
IEnumerable<DropDownListCode.NameValueStr> providerlist;
if (_memoryCache.TryGetValue(ProvidersCacheKey, out providerlist))
{
return Json(providerlist);
}
else
{
MemoryCacheEntryOptions cacheExpirationOptions = new MemoryCacheEntryOptions();
cacheExpirationOptions.AbsoluteExpiration = DateTime.Now.AddDays(30);
cacheExpirationOptions.Priority = CacheItemPriority.Normal;
DropDownListCode um = new DropDownListCode(_context);
var result = um.GetProviderList();
_memoryCache.Set(ProvidersCacheKey, result);
return Json(result);
}
}
When I set a breakpoint on the line:
return Json(providerlist);
I see the ProvidersCacheKey is in the _memoryCache, but it has no value.
What happened to the data?
When I do a Quick Watch on _memoryCache, I can see the DbContext object was destroyed. But how can that be, the code works fine but the cache object does not have the data I saved to it.
Any help would be appreciated.
The method to get providers is:
public IEnumerable<NameValueStr> GetProviderList()
{
var providerlist = (from a in _context.AgencyProvider
where a.Provider == a.AgencyId
select new NameValueStr
{
id = a.Provider,
name = a.Name
});
return providerlist.Distinct();
}
Adding "ToList()" in the calling method worked:
MemoryCacheEntryOptions cacheExpirationOptions = new MemoryCacheEntryOptions();
cacheExpirationOptions.AbsoluteExpiration = DateTime.Now.AddMinutes(30);
cacheExpirationOptions.Priority = CacheItemPriority.Normal;
DropDownListCode um = new DropDownListCode(_context);
var result = um.GetProviderList().ToList();
_memoryCache.Set(ProvidersCacheKey, result);
return Json(result);
All credit goes to Steve Py… Thank you sir!
Is there a way (and how) to know the status of a connection pool? Like, how many connections are being used, how many are available, ...
We are currently facing issues where the application cannot get a connection from the pool (ConnectionPoolTimeoutException: Timeout waiting for connection from pool) so to track down the cause we would like to log some pool stats each time a new connection is requested.
I have been browsing the Apache HTTPClient API but did not find a way to get this information.
We use PoolingClientConnectionManager.
You can use methods of the ConnPoolControl interface to control parameters of the internal pool
You can have a detailed information total and per route with the code below:
public static void main(String[] args) {
PoolingHttpClientConnectionManager connectionManager = HttpClientUtils.getConnectionManager();
System.out.println(createHttpInfo(connectionManager));
}
private static String createHttpInfo(PoolingHttpClientConnectionManager connectionManager) {
StringBuilder sb = new StringBuilder();
sb.append("=========================").append("\n");
sb.append("General Info:").append("\n");
sb.append("-------------------------").append("\n");
sb.append("MaxTotal: ").append(connectionManager.getMaxTotal()).append("\n");
sb.append("DefaultMaxPerRoute: ").append(connectionManager.getDefaultMaxPerRoute()).append("\n");
sb.append("ValidateAfterInactivity: ").append(connectionManager.getValidateAfterInactivity()).append("\n");
sb.append("=========================").append("\n");
PoolStats totalStats = connectionManager.getTotalStats();
sb.append(createPoolStatsInfo("Total Stats", totalStats));
Set<HttpRoute> routes = connectionManager.getRoutes();
if (routes != null) {
for (HttpRoute route : routes) {
sb.append(createRouteInfo(connectionManager, route));
}
}
return sb.toString();
}
private static String createRouteInfo(PoolingHttpClientConnectionManager connectionManager, HttpRoute route) {
PoolStats routeStats = connectionManager.getStats(route);
String info = createPoolStatsInfo(route.getTargetHost().toURI(), routeStats);
return info;
}
private static String createPoolStatsInfo(String title, PoolStats poolStats) {
StringBuilder sb = new StringBuilder();
sb.append(title + ":").append("\n");
sb.append("-------------------------").append("\n");
if (poolStats != null) {
sb.append("Available: ").append(poolStats.getAvailable()).append("\n");
sb.append("Leased: ").append(poolStats.getLeased()).append("\n");
sb.append("Max: ").append(poolStats.getMax()).append("\n");
sb.append("Pending: ").append(poolStats.getPending()).append("\n");
}
sb.append("=========================").append("\n");
return sb.toString();
}
Update (2019-01-07)
The connection manager is retrieved from an utilitarian class I've created (you can create it differently):
public class HttpClientUtils {
private static final PoolingHttpClientConnectionManager connectionManager = createConnectionManager();
private static PoolingHttpClientConnectionManager createConnectionManager() {
try {
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
SSLContext.getDefault(),
new String[] {"TLSv1", "TLSv1.1", "TLSv1.2"},
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.INSTANCE)
.register("https", socketFactory)
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
cm.setMaxTotal(200);
cm.setDefaultMaxPerRoute(20);
return cm;
} catch (NoSuchAlgorithmException | RuntimeException ex) {
Logger.getLogger(HttpClientUtils.class.getName()).log(Level.SEVERE, null, ex);
return null;
}
}
public static PoolingHttpClientConnectionManager getConnectionManager() {
return connectionManager;
}
}
The MiniProfiler site gives the following code for generating an Entity Framework ObjectContext:
public static MyModel Get()
{
var conn = new StackExchange.Profiling.Data.EFProfiledDbConnection(GetConnection(), MiniProfiler.Current);
return ObjectContextUtils.CreateObjectContext<MyModel>(conn); // resides in the MiniProfiler.EF nuget pack
}
However, using Entity Framework 5, I am not using an ObjectContext - rather I am using a DbContext. I cannot plug the model name in here, since the CreateObjectContext<T>() method expects T to be of type ObjectContext. (For the same reason, the code given in this answer also doesn't work).
Additionally, I am using autofac to initialize my Db connections. This is being registered with the following (MyData = the name of my EF DataContext):
Builder.RegisterType<MyData>().As<DbContext>().InstancePerHttpRequest();
So combining two parts: how can I use autofac to initialize my DbContext tied into MiniProfiler.EF? And if that is not possible, at least how can I do the first part (create a factory method for MiniProfiler.EF to return a DbContext)?
I just got this working:
public static class DbContextUtils
{
private const BindingFlags PrivateInstance = BindingFlags.NonPublic | BindingFlags.Instance;
public static T CreateDbContext<T>() where T : DbContext
{
return CreateDbContext<T>(GetProfiledConnection<T>());
}
public static T CreateDbContext<T>(this DbConnection connection) where T : DbContext
{
var workspace = new MetadataWorkspace(new[] { "res://*/" }, new[] { typeof(T).Assembly });
var factory = DbProviderServices.GetProviderFactory(connection);
var itemCollection = workspace.GetItemCollection(DataSpace.SSpace);
var providerFactoryField = itemCollection.GetType().GetField("_providerFactory", PrivateInstance);
if (providerFactoryField != null) providerFactoryField.SetValue(itemCollection, factory);
var ec = new EntityConnection(workspace, connection);
return CtorCache<T, DbConnection>.Ctor(ec);
}
public static DbConnection GetProfiledConnection<T>() where T : DbContext
{
var dbConnection = ObjectContextUtils.GetStoreConnection("name=" + typeof(T).Name);
return new EFProfiledDbConnection(dbConnection, MiniProfiler.Current);
}
internal static class CtorCache<TType, TArg> where TType : class
{
public static readonly Func<TArg, TType> Ctor;
static CtorCache()
{
var argTypes = new[] { typeof(TArg) };
var ctor = typeof(TType).GetConstructor(argTypes);
if (ctor == null)
{
Ctor = x => { throw new InvalidOperationException("No suitable constructor defined"); };
}
else
{
var dm = new DynamicMethod("ctor", typeof(TType), argTypes);
var il = dm.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Newobj, ctor);
il.Emit(OpCodes.Ret);
Ctor = (Func<TArg, TType>)dm.CreateDelegate(typeof(Func<TArg, TType>));
}
}
}
}
It is based on the code in MiniProfiler's ObjectContextUtils.
You use it like this:
builder.Register(c => DbContextUtils.CreateDbContext<MyData>()).As<DbContext>().InstancePerHttpRequest();
This solution REQUIRES your DbContext to have a constructor which takes a DbConnection and passes it to base, like this:
public MyData(DbConnection connection)
: base(connection, true)
{
}
There is a constructor of the DbContext class which takes an existing DbConnection
So you need a new contructor on your MyData which just calls the base
public class MyData : DbContext
{
public MyData(DbConnection existingConnection, bool contextOwnsConnection)
: base(existingConnection, contextOwnsConnection)
{
}
//..
}
Then you register your MyData with Register:
builder.Register(c =>
{
var conn = new EFProfiledDbConnection(GetConnection(), MiniProfiler.Current);
return new MyData(conn, true);
}).As<DbContext>().InstancePerHttpRequest();
I am making a Silverlight app using WCF. I want to get the status of the hard-disks from remote servers and I am able to do that on the server side using a Management object. I have defined a wrapper class to hold the data of the hard-disks and store the objects in a list which I return.
Earlier, when the wrapper class was in the server project, it worked fine. However, when I transferred the class to a class library project in the same solution, the asynchronous call-completed event handler on the client side now gives me an event argument that is empty, i.e. an empty list
I tried debugging both the server and client code, and I see that the server creates the list properly and accesses the disk objects nicely. But the client code simply shows the list to be of size 0.
My client code is:
private void getDiskStatus()
{
diskSpaceStatus.Text = "Running...";
if (server == string.Empty)
{
server = "localhost";
}
diskServer.Text = server;
LogReaderClient proxy = new LogReaderClient();
proxy.getDiskSpaceCompleted += new EventHandler<getDiskSpaceCompletedEventArgs>(proxy_getDiskSpaceCompleted);
proxy.getDiskSpaceAsync(server);
}
void proxy_getDiskSpaceCompleted(object sender, getDiskSpaceCompletedEventArgs e)
{
diskSpaceStatus.Text = "Completed";
try
{
List<uDisk> udisks = new List<uDisk>();
foreach (Disk d in e.Result)
{
uDisk ud = new uDisk(d);
udisks.Add(ud);
}
diskTable.ItemsSource = udisks;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
Where uDisk is another wrapper class for the client side.
My server code is:
[OperationContract]
public List<Disk> getDiskSpace(string server)
{
ConnectionOptions conn = new ConnectionOptions();
ManagementScope scope = new ManagementScope("\\\\" + server + "\\root\\cimv2", conn);
try
{
scope.Connect();
}
catch (Exception ex)
{
error = ex.Message;
}
ObjectQuery oq = new ObjectQuery("select FreeSpace, Size, Name from Win32_LogicalDisk where DriveType=3");
ManagementObjectSearcher search = new ManagementObjectSearcher(scope, oq);
ManagementObjectCollection moc = search.Get();
List<Disk> disks = new List<Disk>();
Disk d;
foreach (ManagementObject mo in moc)
{
d = new Disk(mo);
disks.Add(d);
}
return disks;
}
And the server wrapper class is:
namespace LogFilter.DataObjects
{
[DataContract]
public class Disk
{
[DataMember]
public string name;
[DataMember]
public double freeSpace;
[DataMember]
public double size;
[DataMember]
public double percentFree;
public Disk()
{}
public Disk(ManagementObject mo)
{
this.name = Convert.ToString(mo["Name"]);
this.freeSpace = Convert.ToDouble(mo["FreeSpace"]);
this.size = Convert.ToDouble(mo["Size"]);
this.percentFree = freeSpace * 100 / size;
}
}
}
The wrapper class is in the namespace LogFilter.DataObjects and the Server code is in the namespace LogFilter.Web.
Can anyone provide a solution to this?
Also can someone please give me a resource as to how to set the transfermode in a Silverlight application to Buffered?