error is : The context cannot be used while the model is being created.
I'm using this code :
Parallel.Invoke(AddDataParallel);
private void AddDataParallel()
{
Parallel.For(1001, 2001, delegate(int i)
{
User user = new User();
user.UserName = "user" + i;
_userService2.Add(user);
});
}
error :
public T Add(T entity)
{
return _entities.Add(entity);//The context cannot be used while the model is being created.
}
why ?
You seem to use only one context instance (wrapped in _userService2). But an ObjectContext (or DbContext) is not thread-safe as per MSDN. See Remarks:
The ObjectContext class is not thread safe. The integrity of data objects in an ObjectContext cannot be ensured in multithreaded scenarios.
So you have to re-design your insert scenario. Parallellization against a database is always tricky as you make yourself your own concurrent user. If you want fast inserts, take a look at BulkInsert.
Related
The application was developed on ASP NET Core 3. To log user actions, I decided to use a single method in the Project class. Faced the problem of using one singleton dbContext from different threads.
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
string connection = Configuration.GetConnectionString("ConnectionDB");
services.AddDbContext<DataBaseContext>(options => options.UseSqlServer(connection), ServiceLifetime.Transient, ServiceLifetime.Singleton);
services.AddSingleton<Project>();
}
Project.cs
public async Task AddUserLog(string action, string message, int userId)
{
try
{
UserLog userLog = new UserLog()
{
Action = action,
Message = message,
UserId = userId
Datepoint = DateTime.Now
};
_dbContext.UserLog.Add(userLog);
await _dbContext.SaveChangesAsync();
}
catch (Exception ex)
{
await AddSystemLog("Project", "AddUserLog", ex.Message);
}
}
SchemeController.cs
public class SchemeController : ControllerBase
{
private readonly Project _project;
public SchemeController(Project project)
{
_project = project;
}
[Authorize(Policy = "AdvancedControl")]
[HttpPost("[action]")]
public async Task SomeMethode()
{
for (int i = 0; i < 10; i++)
{
await _project.AddUserLog("Text", "Message", 42);
}
}
}
Already at the second iteration of the loop, I catch an exception in the AddUserLog method:
"A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext."
I suggest several solutions:
Add the log to the buffer table and then save it to the database by timer. But this is not the best way out;
Block the method while it is being saved to the database.
But I don’t like any of the options.
Please tell me the correct approach in solving this issue.
So, you trying to use shared resource (singleton Project class) to perform parallel operations (save UserLogs) while your shared resource implementation is not thread-safe (exceptions raised).
You have at lease three ways to solve this:
Do not use shared resource: register Project per scope instead of singletone;
Do not perform operations in parallel: seems hard to achieve because you making webapp and you can't force user(s) to wait
Refactor your resource to be thread-safe: add locks/mutexes/buffering... inside Project
There is no one "correct" way - all 3 are correct. Choose one you like (or combine several).
Usually using scoped dbcontext is recommended (because connections are pooled), but it's the creator of app who should decide.
Faced with the following issue: I am actively use DominoDocument class (wrapped Document) in my projects, particularly as basis for my business model objects.
Very often I have a need to access / iterate my business model objects as Anonymous user thus underlying lotus.domino.Document retrieved based on SessionAsSigner session object (for example in case of some REST Services, or in case of xAgent, etc).
The behavior of restoreWrappedDocument() method in such cases really breaks all flexibility of using such architecture: this method tries to restore wrapped document based on current execution environment access rights, and of course that causes errors with ACL.
Let’s consider the following code snippet as example:
public void test3() {
try {
System.out.println(">>>>>");
System.out.println(">>>>> START");
lotus.domino.Database db = AppBean.getSessionAsSigner().getDatabase(AppBean.getInstance().getContactsDBserverName(), AppBean.getInstance().getContactsDBname(), false);
Document notesDoc = db.getAllDocuments().getFirstDocument();
String dbName = notesDoc.getParentDatabase().getServer() + "!!" + notesDoc.getParentDatabase().getFilePath();
DominoDocument ds = DominoDocument.wrap(dbName, notesDoc, null, "exception", false, "UseWeb", null);
System.out.println(">> 1 >> " + ds.getValue("form"));
ds.getDocument().recycle();
try {
ds.restoreWrappedDocument();
}catch(Throwable e2){
System.out.println(">> 2 - exception - >> " + e2.toString());
e2.printStackTrace();
}
try {
System.out.println(">> 3 >> " + ds.getValue("form"));
}catch(Throwable e3){
System.out.println(">> 3 - exception - >> " + e3.toString());
}
System.out.println(">>>>> END");
System.out.println(">>>>>");
}catch(Exception e){
e.printStackTrace();
}
}
1) Scenario 1: executing this code by authenticated user that has access to target DB gives the following result:
So method works as expected and everything perfect.
2) Scenario 2: executing this code by Anonymous user causes Exception (generally, what is expected):
You can clearly see that restoreWrappedDocument() executes some helper methods in order to get DB, and of course that is done with current user access level (Anonymous).
Possible solutions:
The obvious solution is to add custom logic to my business object model, which will perform custom restore (basically based on Server&DB names and document UNID or NoteID).
What I am very curious whether there is any more smart or built-in method exist for restoring wrapped documents with SessionAsSigner rights?
Thanks!
I don't think there's a proper way to do this, other than your option 1, for better or for worse.
However, and I'm not saying this is a good idea, it seems like DominoDocument likely gets to its session through the current request map. If you want to be tricky, you could try temporarily swapping session out for sessionAsSigner in the request scope, calling restoreWrappedDocument, and then swapping it back.
A solution with a Helper class using Java Reflection:
(Incomplete, missing some parts)
package ch.hasselba.xpages;
import java.lang.reflect.Field;
import lotus.domino.Database;
import lotus.domino.Document;
import lotus.domino.NotesException;
import com.ibm.xsp.FacesExceptionEx;
import com.ibm.xsp.model.domino.DominoUtils;
import com.ibm.xsp.model.domino.wrapped.DominoDocument;
public class DominoDocumentUtil {
private static final long serialVersionUID = 1L;
private transient final Field wrappedObj;
private transient final DominoDocument dominoDoc;
public DominoDocumentUtil(DominoDocument doc) throws SecurityException,
NoSuchFieldException {
dominoDoc = doc;
wrappedObj= doc.getClass().getDeclaredField("_wrappedObject");
wrappedObj.setAccessible(true);
}
public void restoreWrappedDocument(Database db)
throws IllegalArgumentException, IllegalAccessException {
try {
Document doc = DominoUtils.getDocumentById(db, dominoDoc
.getDocumentId(), dominoDoc.isAllowDeletedDocs());
this.wrappedObj.set(dominoDoc, doc);
} catch (NotesException ne) {
throw new FacesExceptionEx(ne.getMessage());
}
}
}
To use the class you can call the restoreWrappedDocument method with a database opened with sessionAsSigner:
DominoDocumentUtil util = new DominoDocumentUtil(ds);
util.restoreWrappedDocument(db);
Having an huge customers profile page if two or more users start using same page and start editing big change will happen in my database so planing to implement Threads concept where only one user can use that customer page
i'm aware about threads concept but confused how to implement it
hope i need to use Singleton class as well
Any suggestion or Logic's will be helpful
I'm using Struts,Hibernate frame work
You may use application context to store a flag variable. Action will use its value to allow only one simultaneous execution.
public class TestAction extends ActionSupport implements ApplicationAware {
private static final String APP_BUSY_KEY = "APP_BUSY";
Map<String, Object> map;
#Override
public void setApplication(Map<String, Object> map) {
this.map = map;
}
#Override
public String execute() throws Exception {
if (map.containsKey(APP_BUSY_KEY)) {
return ERROR;
} else {
map.put(APP_BUSY_KEY, "1");
try {
// action logic here
} finally {
map.remove(APP_BUSY_KEY);
}
return SUCCESS;
}
}
}
If you plan to implement similar logic for two requests (lock after displaying values and release lock after submitting new values) then logic will be more complex and you will also need to handle lock release after timeout.
I've seen so many questions similar to mine, but no answers that quite seem to apply to my situation.
My ASP.NET MVC app with EF 6 Code first and Unity has a web service that adds something to the database, then fires off another thread that adds more stuff to the database. The reason for using the other thread is to return the original request as quickly as possible. The context class is obtained using the Unity container RegisterType().
I've got lots of repository classes all using the same context, so to make sure they get the same instance I could use the PerRequestLifetimeManager in my Unity container, and that's fine for the http request threads but that the other threads can't use the context returned by the PerRequestLifetimeManager because this is only valid on the original http request thread.
So, I can use the PerThreadLifetimeManager. This is great because now the main request thread and the other thread it kicks off get the same instance of the context returned by Unity. The trouble is that so do other requests if they get given the same thread, so this is no good either.
So how can I configure things so that the request threads get their own PerRequest Lifetime Manager created context, and other threads get a different context?
The issue is made a little more difficult by the fact that the new thread calls other classes that need to use a context instance. However, these other classes can be used from the main request thread or the new thread, so grabbing a context instance when the thread is started and then passing it around will be tricky.
Thanks in advance
No takers then...
I'm going to have a go at answering my own question, but could do with some thoughts on my approach.
So I can't use the PerRequestLifetimeManager because worker threads can't use the context that this returns, but I can't use the PerThreadLifetimeManager because the context can last the lifetime of several HTTP requests. This class attempts to provide the best of both worlds.
/// <summary>
/// For the context class the PerRequestLifetimeManager is the most suitable lifetime manager,
/// but this doesn't work when a new worker thread is started as this needs to access the context.
/// The PerThreadLifetimeManager is no good either as the context can last for more than on request.
/// This class attempts to give the best of both worlds: per request lifetime management for HTTP requests
/// and thread storage for worker threads.
/// </summary>
public class PerRequestOrThreadLifetimeManager : PerRequestLifetimeManager, IDisposable
{
private const string threadDataSlotName = "PerRequestOrThreadLifetimeManager";
public override object GetValue()
{
if (System.Web.HttpContext.Current != null)
{
return base.GetValue();
}
else
{
return getManagedObject();
}
}
public override void RemoveValue()
{
throw new NotImplementedException();
}
public override void SetValue(object newValue)
{
if (System.Web.HttpContext.Current != null)
{
base.SetValue(newValue);
}
else
{
Thread.SetData(Thread.GetNamedDataSlot(threadDataSlotName), newValue);
}
}
private object getManagedObject()
{
return Thread.GetData(Thread.GetNamedDataSlot(threadDataSlotName));
}
public void Dispose()
{
try
{
IDisposable obj = getManagedObject() as IDisposable;
if (obj != null)
{
obj.Dispose();
obj = null;
}
}
catch { }
}
}
I have faced this problem while changing a code block.
List<Entity> entities = new List<Entity>();
//Some values added to the list ....
foreach(var entity in entities)
{
Thread th = new Thread(new ThreadStart(SomeMethod));
th.Start(entity);
}
SomeMethod is taking Entity and changing on it. somthing like
private void SomeMethod(Entity entity)
{
//Some operation on entity
}
This is a .NET 2.0 code, while changing it to 4.0 I did a small change i.e.
foreach(var entity in entities)
{
Thread th = new ThreadStart(() => SomeMethod(entity));
th.Start();
}
This is not working, from error it looks like entity variable getting changed between threads and resulting some null reference exception. I havnt changed anything else, as soon as code reverted to the previous way, it is fine. Can anyone explain this?
Please try to use a new local variable in such a way:
foreach(var entity in entities)
{
var copy = entity;
Thread th = new ThreadStart(() => SomeMethod(copy));
th.Start();
}
The approach with a new variable will not work if you introduce the variable in such a way
Thread th = new ThreadStart(() => {var copy = entity; SomeMethod(copy)) };
since that code will still capture the loop variable that is changed in each iteration of the loop.
Also please note that you can get the error you described if the entities collection contains the same element twice (that is two references to the same Entity object). Please make sure that it's not the case.