So I'm writing a WEB API with .NET 5 and I started with something like this to get my data:
public ProfileModel Get(string email)
{
using (JobsDB data = new JobsDB())
{
return data.Profiles.Where(x => x.Email.ToUpper() == email.ToUpper()).FirstOrDefault();
}
}
But I just ran across an article that made me think I should be writing it using async/await like this:
public async Task<ProfileModel> Get(string email)
{
using (JobsDB data = new JobsDB())
{
return await data.Profiles.Where(x => x.Email.ToUpper() == email.ToUpper()).FirstOrDefaultAsync();
}
}
Now I realize that when the client application calls the WEB API, they will do so in an asynchronous manner (in their JavaScript code), so I always thought that the WEB API itself didn't have to use asynchronous methods. So is there a true advantage to using async/await in the WEB API itself? Is it considered "best practices" to do so anyway?
Yes, it is a best practice to make your API Endpoints asynchronous in according to this Microsoft's article.
An asynchronous operations allows to "optimize" amount of threads involved in request handling by involving the same thread to process another request, while the previous is under await (more details here).
Also, you can read this article to understand the difference between return await Task and return Task (there is a difference which is important to know).
Related
In our system, we have one C++ component acting as a Thrift Server, and one .netCore/C# component as a client.
So far, I was managing a single connection, so using a singleton to create my ThriftPushClientWrapper which implements TBaseClient. (via the generated object from the thrift interface)
.AddSingleton<IThriftPushClientWrapper>(sp =>
{
var localIpAddress = IPAddress.Parse(serverIp);
var transport = new TSocketTransport(localIpAddress, dataPort);
var protocol = new TBinaryProtocol(transport);
return new ThriftPushClientWrapper(protocol);
});
(so far using 0.13 version of the Thrift library, need to update to 0.14.1 soon, but wonder if the server part must be updated too/first).
This is working great.
Now, I want multiple clients that can connect to the server simultaneously, all on the same ip:port
So I am starting a ClientFactory, but wonder how to deal with the creation of the client.
To be more precise, the server part is configured for 5 threads, so I need 5 clients.
One simple approach would be to create a new client each time, but probably inefficient.
A better approach is to have a collection of 5 clients, and using the next available free one.
So I started with the following factory, where I should get the index from outside.
private readonly ConcurrentDictionary<int, IThriftPushClientWrapper> _clientDict;
public IThriftPushClientWrapper GetNextAvailablePushClient(int index)
{
IThriftPushClientWrapper client;
if (_clientDict.ContainsKey(index))
{
if (_clientDict.TryGetValue(index, out client) && client != null)
return client;
else // error handling
}
// add new client for the expecting index
client = CreateNewPushClient();
_clientDict.TryAdd(index, client);
return client;
}
private IThriftPushClientWrapper CreateNewPushClient()
{
var localIpAddress = IPAddress.Parse(serverIp);
var transport = new TSocketTransport(localIpAddress, dataPort);
var protocol = new TBinaryProtocol(transport);
return new ThriftPushClientWrapper(protocol);
}
My next issue it to determine how to set the index from outside.
I started with a SemaphoreSlim(5,5) using the semaphore.CurrentCount as index, but probably not the best idea. Also tried with a rolling index from 0 to 5. But apparently, a CancellationToken is used to cancel further procceesing. Not sure the root cause yet.
Is it possible to determine whether a TBaseClient is currently busy or available?
What is the recommended strategy to deal with a pool of clients?
The easiest solution to solve this is to do it right. If you are going to use some resource from a pool of resources, either get it off the pool, or mark it used in some suitable way for that time.
It's notable that the question has nothing to do with Thrift in particular. You are trying to solve a weak resource management approach by trying to leverage other peoples code that was never intended to work in such a context.
Regarding how to implement object pooling, this other question can provide further advice. Also keep in mind that especially on Windows platforms not all system resources can be shared freely across threads.
I am trying to read all existing messages on an Azure ServiceBus Subscription, using the Microsoft.Azure.ServiceBus.dll (in .Net Core 2.1) but am struggling.
I've found many examples that the following should work, but it doesn't:
var client = new SubscriptionClient(ServiceBusConnectionString, topicName, subscription, ReceiveMode.PeekLock, null);
var totalRetrieved = 0;
while (totalRetrieved < count)
{
var messageEnumerable = subscriptionClient.PeekBatch(count);
//// ... code removed from this example as not relevant
}
My issue is that the .PeekBatch method isn't available, and I'm confused as to how I need to approach this.
I've downloaded the source for the ServiceBusExplorer from GitHub (https://github.com/paolosalvatori/ServiceBusExplorer) and the above code example is pretty much as it's doing it. But not in .Net Core / Microsoft.Azure.ServiceBus namespace.
For clarity though, I'm trying to read messages that are already on the queue - I've worked through other examples that create listeners that respond to new messages, but I need to work in this disconnected manner, after the message has already been placed on the queue.
ServiceBusExplorer uses WindowsAzure.ServiceBus Library, which is a .Net Framework Library and you cannot use it in .Net Core applications. You should use Microsoft.Azure.ServiceBus (.Net Standard Library) in .Net Core applications.
Check here for samples of Microsoft.Azure.ServiceBus
var client = new SubscriptionClient(ServiceBusConnectionString, topicName, subscription, ReceiveMode.PeekLock, null);
client .RegisterMessageHandler(
async (message, token) =>
{
await subscriptionClient.CompleteAsync(message.SystemProperties.LockToken);
}
);
Try using RegisterMessageHandler. It will
receive messages continuously from the entity. It registers a message handler and
begins a new thread to receive messages. This handler is awaited
on every time a new message is received by the receiver.
I'll explain what I've done so far. I'm using VS2010.
Firstly I've created a ASP.NET Web Service Application (framewkork 3.5) with a service with these operations:
[WebMethod]
public Boolean ShoppingTripNeeded(DateTime d)
{
DBConnection db = new DBConnection();
return db.ShoppingTripNeeded(d);
}
[WebMethod]
public String[] ShopsToVisit(DateTime d)
{
DBConnection db = new DBConnection();
return db.ShopsToVisit(d);
}
[WebMethod]
public Item[] ItemsToBuy(DateTime d, String shop)
{
DBConnection db = new DBConnection();
return db.ItemsToBuy(d, shop);
}
And now I'm creating a WCF Workflow Service Application, in which I want to call sequently the 3 methods above, so I've added a Service Reference to my service wsdl here:
http://awtassignment3-shoppinglistservice1.cloudapp.net/Service1.asmx?WSDL
This referencing adds the 3 operations. The first one "ShoppingTripNeeded" seems to be fine (receiving a DateTime and returning a Boolean), but for the other operations, the parameters have changed in a strange way...
For example the operation ShopsToVisit now ask for a ShopsToVisitRequestBody and returns a ShopsToVisitResponseBody... I don't know why this happens! because the first operation is fine...
Moreover, as I'm working with a workflow, I can't "play" with this types to find out what's going on...
Have you any guess? any help will be fine...
Thanks very much!
Is there a reason you are using ASMX? WCF has replaced Web Services as far more superior service communication technology. See SO: Web Services — WCF vs. Standard, SO: Web Service vs WCF Service
To answer your question:
WF 3.5 will wrap any operation with Request/Response message pattern that is not a primitive (e.g. bool, int). String[] is not a primitive type hence it will be wrapped.
WF 4.0 adding service reference (dialog) will wrap all operations in Request/Response message pair by default regardless if this is primitive or complex type. On top of that, it will create Activity for each operation that it discovers.
Using Request/Response message pattern allows for controlling the message shape specifically message headers.
For Reference:
SO: When should I use Message Contracts instead of DataContract and why?
MSDN: Message Contracts
Data can be fetched into an application through web service can it be possible to update data in web service from our application ..
In general a Web Service implementation can be any arbitrary code, so insert/update/delete is nothing special.
I suspect that there's something behind your question, some specific issue in mind? You may want to explain why you think there's a problem.
Of course create a web method that takes parameters and then save the data as required
[WebMethod]
public bool UpdateData(string firstName, string lastName, int id)
{
// do some data access code here
}
Then use in your code in a similar way as to when your are acquiring data from the web service:
MyWebServiceClient client = new MyWebServiceClient();
bool updated = client.UpdateData("Jon", "Skeet", 1);
I've been using NHibernate for a while now and have found from time to time that if I try to request two pages simultaniously (or as close as I can) it will occasionally error. So I assumed that it was because my Session management was not thread safe.
I thought it was my class so I tried to use a different method from this blog post http://pwigle.wordpress.com/2008/11/21/nhibernate-session-handling-in-aspnet-the-easy-way/ however I still get the same issues. The actual error I am getting is:
Server Error in '/AvvioCMS' Application.
failed to lazily initialize a collection, no session or session was closed
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: NHibernate.LazyInitializationException: failed to lazily initialize a collection, no session or session was closed
Either that or no datareader is open, but this is the main culprit.
I've placed my session management class below, can anyone spot why I may be having these issues?
public interface IUnitOfWorkDataStore
{
object this[string key] { get; set; }
}
public static Configuration Init(IUnitOfWorkDataStore storage, Assembly[] assemblies)
{
if (storage == null)
throw new Exception("storage mechanism was null but must be provided");
Configuration cfg = ConfigureNHibernate(string.Empty);
foreach (Assembly assembly in assemblies)
{
cfg.AddMappingsFromAssembly(assembly);
}
SessionFactory = cfg.BuildSessionFactory();
ContextDataStore = storage;
return cfg;
}
public static ISessionFactory SessionFactory { get; set; }
public static ISession StoredSession
{
get
{
return (ISession)ContextDataStore[NHibernateSession.CDS_NHibernateSession];
}
set
{
ContextDataStore[NHibernateSession.CDS_NHibernateSession] = value;
}
}
public const string CDS_NHibernateSession = "NHibernateSession";
public const string CDS_IDbConnection = "IDbConnection";
public static IUnitOfWorkDataStore ContextDataStore { get; set; }
private static object locker = new object();
public static ISession Current
{
get
{
ISession session = StoredSession;
if (session == null)
{
lock (locker)
{
if (DBConnection != null)
session = SessionFactory.OpenSession(DBConnection);
else
session = SessionFactory.OpenSession();
StoredSession = session;
}
}
return session;
}
set
{
StoredSession = value;
}
}
public static IDbConnection DBConnection
{
get
{
return (IDbConnection)ContextDataStore[NHibernateSession.CDS_IDbConnection];
}
set
{
ContextDataStore[NHibernateSession.CDS_IDbConnection] = value;
}
}
}
And the actual store I am using is this:
public class HttpContextDataStore : IUnitOfWorkDataStore
{
public object this[string key]
{
get { return HttpContext.Current.Items[key]; }
set { HttpContext.Current.Items[key] = value; }
}
}
I initialize the SessionFactory on Application_Start up with:
NHibernateSession.Init(new HttpContextDataStore(), new Assembly[] {
typeof(MappedClass).Assembly});
Update
Thanks for your advice. I have tried a few different things to try and simplify the code but I am still running into the same issues and I may have an idea why.
I create the session per request as and when it is needed but in my global.asax I am disposing of the session on Application_EndRequest. However I'm finding the Application_EndRequest is being fired more than once while I am in debug at the end of loading a page. I thought that the event is only suppose to fire once at the very end of the request but if it isn't and some other items are trying to use the Session (which is what the error is complaining about) for whatever weird reason that could be my problem and the Session is still thread safe it is just being disposed of to early.
Anyone got any ideas? I did a google and saw that the VS development server does cause issues like that but I am running it through IIS.
While I haven't seen your entire codebase or the the problem you're trying to solve, a rethinking of how you are using NHibernate might be in order. From the documentation:
You should observe the following
practices when creating NHibernate
Sessions:
Never create more than one concurrent
ISession or ITransaction instance per
database connection.
Be extremely careful when creating
more than one ISession per database
per transaction. The ISession itself
keeps track of updates made to loaded
objects, so a different ISession might
see stale data.
The ISession is not threadsafe! Never
access the same ISession in two
concurrent threads. An ISession is
usually only a single unit-of-work!
That last bit is the most relevant (and important in the case of a multithreaded environment) to what I'm saying. An ISession should be used once for a small atomic operation and then disposed. Also from the documentation:
An ISessionFactory is an
expensive-to-create, threadsafe object
intended to be shared by all
application threads. An ISession is an
inexpensive, non-threadsafe object
that should be used once, for a single
business process, and then discarded.
Combining those two ideas, instead of storing the ISession itself, store the session factory since that is the "big" object. You can then employ something like SessionManager.GetSession() as a wrapper to retrieve the factory from the session store and instantiate a session and use it for one operation.
The problem is also less obvious in the context of an ASP.NET application. You're statically scoping the ISession object which means it's shared across the AppDomain. If two different Page requests are created within that AppDomain's lifetime and are executed simultaneously, you now have two Pages (different threads) touching the same ISession which is not safe.
Basically, instead of trying to keep a session around for as long as possible, try to get rid of them as soon as possible and see if you have better results.
EDIT:
Ok, I can see where you're trying to go with this. It sounds like you're trying to implement the Open Session In View pattern, and there a couple different routes you can take on that:
If adding another framework is not an issue, look into something like Spring.NET. It's modular so you don't have to use the whole thing, you could just use the NHibernate helper module. It supports the open session in view pattern. Documentation here (heading 21.2.10. "Web Session Management").
If you'd rather roll your own, check out this codeproject posting by Bill McCafferty: "NHibernate Best Practices". Towards the end he describes implementing the pattern through a custom IHttpModule. I've also seen posts around the Internet for implementing the pattern without an IHttpModule, but that might be what you've been trying.
My usual pattern (and maybe you've already skipped ahead here) is use a framework first. It removes lots of headaches. If it's too slow or doesn't fit my needs then I try to tweak the configuration or customize it. Only after that do I try to roll my own, but YMMV. :)
I can't be certain (as I'm a Java Hibernate guy) in NHibernate but in hibernate Session objects are not thread safe by design. You should open and close a session and never allow it out of the scope of the current thread.
I'm sure that patterns such as 'Open session view' have been implemented in .Net somewhere.
The other interesting issue is when you put a hibernate entity in the session. The problem here is that the session that it is attached to will be closed (or should be) on the request finishing. You have to reattach the entity to the new (hibernate) session if you wish to navigate any non loaded associations. This in it's self causes a new issue if two requests try to do this at the same time as something will blow up if you try to attach an entity to two sessions.
Hope this helps.
Gareth
The problem ended up being that my library for inversion of control was not managing the objects being created in HTTP context correctly so I was getting references for objects that should of not been available to that context. This was using Ninject 1.0, once I updated to Ninject 2.0 (beta) the problem was resolved.