I am trying to get a list of records from a table and then in a foreach loop I am trying to update the records one by one.
Here is my code
public IEnumerable<MessageOut> GetSMSInformation()
{
using (var db = new DataClasses1DataContext())
{
var sms = db.MessageOuts.Where(msg => msg.msgstatus.Equals("Pending")).Select(msg => msg);
return sms.ToList();
}
}
public void StartSMSSending()
{
var information = GetSMSInformation();
foreach (var sms in information)
{
SendSMS(sms.reciever, sms.msg);
UpdateRecords(sms,"Delivered", DateTime.Now);
}
}
public void UpdateRecords(MessageOut sms, string msgStatus, DateTime sentTime)
{
using (var db = new DataClasses1DataContext())
{
sms.msgstatus = msgStatus;
sms.senttime = sentTime;
db.SubmitChanges();
}
}
The records just don't update.I am not getting any errors as well.
Can someone help me out with this?
In order to perform an update operation with Linq2Sql classes you need to keep your context alive. In the example you have above, you include your selection inside a using statement, so your records are returned and then the context is dumped. When you go to make the update, there is a fresh context, so there is no change registered to the objects. A way to do this could be to make the context a more global object or to pass it as a parameter.
public IEnumerable<MessageOut> GetSMSInformation(DataContext context)
{
var sms = context.MessageOuts.Where(
msg => msg.msgstatus.Equals("Pending")).Select(msg => msg);
return sms.ToList();
}
public void StartSMSSending()
{
using (var db = new DataClasses1DataContext())
{
var information = GetSMSInformation(db);
foreach (var sms in information)
{
SendSMS(sms.reciever, sms.msg);
UpdateRecords(sms,"Delivered", DateTime.Now, db);
}
}
}
public void UpdateRecords(MessageOut sms, string msgStatus, DateTime sentTime, DataContext context)
{
sms.msgstatus = msgStatus;
sms.senttime = sentTime;
context.SubmitChanges();
}
Or, you could do this (my preferred method):
public IEnumerable<MessageOut> GetSMSInformation()
{
var sms = Context.MessageOuts.Where(
msg => msg.msgstatus.Equals("Pending")).Select(msg => msg);
return sms.ToList();
}
public void StartSMSSending()
{
var information = GetSMSInformation(db);
foreach (var sms in information)
{
SendSMS(sms.reciever, sms.msg);
UpdateRecords(sms,"Delivered", DateTime.Now, db);
}
}
public void UpdateRecords(MessageOut sms, string msgStatus, DateTime sentTime)
{
sms.msgstatus = msgStatus;
sms.senttime = sentTime;
Context.SubmitChanges();
}
private DataClasses1DataContext _context = null;
public DataClasses1DataContextContext
{
get
{
return _context ?? (_context = new DataClasses1DataContext());
}
}
You are extracting the data from this DB context:
public IEnumerable<MessageOut> GetSMSInformation()
{
using (var db = new DataClasses1DataContext())
{
var sms = db.MessageOuts.Where(msg => msg.msgstatus.Equals("Pending")).Select(msg => msg);
return sms.ToList();
}
}
Then you are updating data from the previous DB context are you are submitting changes to a different DB context:
public void UpdateRecords(MessageOut sms, string msgStatus, DateTime sentTime)
{
using (var db = new DataClasses1DataContext())
{
sms.msgstatus = msgStatus;
sms.senttime = sentTime;
db.SubmitChanges();
}
}
Pass the DB context as a reference or best keep is as a member of your class. This ensures a single db context instance for your class instance.
My suggestion would be the following:
class SmsCenter
{
private DataClasses1DataContext _dbContext;
public SmsCenter()
{
_dbContext = new DataClasses1DataContext();
}
public IEnumerable<MessageOut> GetSMSInformation()
{
var sms = _dbContext.MessageOuts.Where(msg => msg.msgstatus.Equals("Pending")).Select(msg => msg);
return sms.ToList();
}
public void StartSMSSending()
{
var information = GetSMSInformation();
foreach (var sms in information)
{
SendSMS(sms.reciever, sms.msg);
UpdateRecords(sms,"Delivered", DateTime.Now);
}
}
public void UpdateRecords(MessageOut sms, string msgStatus, DateTime sentTime)
{
sms.msgstatus = msgStatus;
sms.senttime = sentTime;
_dbContext.SubmitChanges();
}
}
you din't relate "sms" object with db, like
sms.msgstatus = msgStatus;
sms.senttime = sentTime;
db.MessageOut.Add(sms);
//or
db.MessageOut=sms;
db.SubmitChanges();
Related
I'm getting the following error on my C# Web API: "Exception thrown: 'System.Threading.ThreadAbortException' in System.Data.dll
Thread was being aborted". I have a long running process on one thread using my data access logic class to get and update records being process. Meanwhile a user submits another group to process which has need of the same data access logic class, thus resulting in the error. Here is a rough sketch of what I'm doing.
WebAPI Class:
public IHttpActionResult OkToProcess(string groupNameToProcess)
{
var logic = GetLogic();
//Gets All Unprocessed Records and Adds them to Blocking Queue
Task.Factory.StartNew(() => dataAccessLogic.LoadAndProcess(groupNameToProcess);
}
public IHttpActionResult AddToProcess(int recordIdToProcess)
{
StaticProcessingFactory.AddToQueue(recordIdToProcess);
}
StaticProcessingFactory
internal static ConcurrentDictionary<ApplicationEnvironment, Logic> correctors = new ConcurrentDictionary<ApplicationEnvironment, Logic>();
internal static BlockingCollection<CorrectionMessage> MessageQueue = new BlockingCollection<Message>(2000);
public void StartService(){
Task.Factory.StartNew(() => LoadService());
}
public void LoadService(){
var logic = GetLogic();
if(isFirstGroupOkToProcessAsPerTextFileLog())
logic.LoadAndProcess("FirstGroup");
if(isSeconddGroupOkToProcessAsPerTextFileLog())
logic.LoadAndProcess("SecondGroup");
}
public static GetLogic(){
var sqlConnectionFactory = Tools.GetSqlConnectionFactory();
string environment = ConfigurationManager.AppSettings["DefaultApplicationEnvironment"];
ApplicationEnvironment applicationEnvironment =
ApplicationEnvironmentExtensions.ToApplicationEnvironment(environment);
return correctors.GetOrAdd(applicationEnvironment, new Logic(sqlConnectionFactory ));
}
public static void AddToQueue(Message message, bool completeAdding = true)
{
if (MessageQueue.IsAddingCompleted)
MessageQueue = new BlockingCollection<Message>();
if (completeAdding && message.ProcessImmediately)
StartQueue(message);
else
MessageQueue.Add(message);
}
public static void StartQueue(Message message = null)
{
if (message != null)
{
if(!string.IsNullOrEmpty(message.ID))
MessageQueue.Add(message);
Logic logic = GetLogic(message.Environment);
try
{
var messages = MessageQueue.TakeWhile(x => logic.IsPartOfGroup(x.GroupName, message.GroupName));
if (messages.Count() > 0)
MessageQueue.CompleteAdding();
int i = 0;
foreach (var msg in messages)
{
i++;
Process(msg);
}
}
catch (InvalidOperationException) { MessageQueue.CompleteAdding(); }
}
}
public static void Process(Message message)
{
Var logic = GetLogic(message.Environment);
var record = logic.GetRecord(message.ID);
record.Status = Status.Processed;
logic.Save(record);
}
Logic Class
private readonly DataAccess DataAccess;
public Logic(SqlConnectionFactory factory)
{
DataAccess = new DataAcess(factory);
}
public void LoadAndProcess(string groupName)
{
var groups = DataAccess.GetGroups();
var records = DataAccess.GetRecordsReadyToProcess(groups);
for(int i = 0; i < records.Count; i++)
{
Message message = new Message();
message.Enviornment = environment.ToString();
message.ID = records[i].ID;
message.User = user;
message.Group = groupName;
message.ProcessImmediately = true;
StaticProcessingFactory.AddToQueue(message, i + 1 == records.Count);
}
}
Any ideas how I might ensure that all traffic from all threads have access to the Data Access Logic without threads being systematically aborted?
Hi im trying to make a notification system. Basically when i add a new row, a notification will be shown on my notificationspage realtime (i use signalR with razor pages asp.net). But for some reason when i get on that page, i get these errors: Unable to cast object of type 'System.DBNull' to type 'System.DateTime'.
at myWebApp.Controllers.SpeedListener.GetAlarmList() in \myWebApp\Controllers\SpeedListener.cs:line 83
at myWebApp.Controllers.SpeedListener.ListenForAlarmNotifications() in \myWebApp\Controllers\SpeedListener.cs:line 43
So apparently theres a problem at the controller.
Here is the code of the controller
namespace myWebApp.Controllers
{
public class SpeedListener :Controller
{
private IHubContext<speedalarmhub> _hubContext;
private IMemoryCache _cache;
public SpeedListener(IHubContext<speedalarmhub> hubContext,IMemoryCache cache)
{
_hubContext = hubContext;
_cache = cache;
}
public static string cs = Database.Database.Connector();
public void ListenForAlarmNotifications()
{
NpgsqlConnection conn = new NpgsqlConnection(cs);
conn.StateChange += conn_StateChange;
conn.Open();
var listenCommand = conn.CreateCommand();
listenCommand.CommandText = $"listen notifytickets;";
listenCommand.ExecuteNonQuery();
conn.Notification += PostgresNotificationReceived;
_hubContext.Clients.All.SendAsync(this.GetAlarmList());
while (true)
{
conn.Wait();
}
}
private void PostgresNotificationReceived(object sender, NpgsqlNotificationEventArgs e)
{
string actionName = e.Payload.ToString();
string actionType = "";
if (actionName.Contains("DELETE"))
{
actionType = "Delete";
}
if (actionName.Contains("UPDATE"))
{
actionType = "Update";
}
if (actionName.Contains("INSERT"))
{
actionType = "Insert";
}
_hubContext.Clients.All.SendAsync("ReceiveMessage", this.GetAlarmList());
}
public string GetAlarmList()
{
List<NotificationModel> not = new List<NotificationModel>();
using var con = new NpgsqlConnection(cs);
{
string query = "Select datumnu, bericht FROM notification";
using NpgsqlCommand cmd = new NpgsqlCommand(query, con);
{
cmd.Connection = con;
con.Open();
using (NpgsqlDataReader dr = cmd.ExecuteReader())
{
while (dr.Read())
{
not.Add(new NotificationModel { Datenow = ((DateTime) dr["datumnu"]).ToString("yyyy/MM/dd"), Bericht = dr["bericht"].ToString() });
}
}
con.Close();
}
}
_cache.Set("notification", SerializeObjectToJson(not));
return _cache.Get("notification").ToString();
}
public String SerializeObjectToJson(Object notification)
{
try
{
return Newtonsoft.Json.JsonConvert.SerializeObject(notification);
}
catch (Exception) { return null; }
}
private void conn_StateChange(object sender, System.Data.StateChangeEventArgs e)
{
_hubContext.Clients.All.SendAsync("Current State: " + e.CurrentState.ToString() + " Original State: " + e.OriginalState.ToString(), "connection state changed");
}
}
}
If needed here is my hub
namespace myWebApp.Hubs
{
public class speedalarmhub : Hub
{
private IMemoryCache _cache;
private IHubContext<speedalarmhub> _hubContext;
public speedalarmhub(IMemoryCache cache, IHubContext<speedalarmhub> hubContext)
{
_cache = cache;
_hubContext = hubContext;
}
public async Task SendMessage()
{
if (!_cache.TryGetValue("notification", out string response))
{
SpeedListener speedlist = new SpeedListener(_hubContext,_cache);
speedlist.ListenForAlarmNotifications();
string jsonspeedalarm = speedlist.GetAlarmList();
_cache.Set("notification", jsonspeedalarm);
await Clients.All.SendAsync("ReceiveMessage", _cache.Get("notification").ToString());
}
else
{
await Clients.All.SendAsync("ReceiveMessage", _cache.Get("notification").ToString());
}
}
}
}
the table name in postgresql is called 'notification', i have two column called 'bericht' with type varchar and 'datumnu' with type date.
Edit suggested by mjwills
DateTime don't accept null value. Check if the value is null and assigne a default value
while (dr.Read())
{
not.Add(new NotificationModel { Datenow = ((DateTime) dr["datumnu"]).ToString("yyyy/MM/dd"), Bericht = dr["bericht"].ToString() });
}
Become
while (dr.Read())
{
DateTime defaultDateTime = DateTime.Now;
if(dr.IsNull("datumnu")){
defaultDateTime = (DateTime)dr["datumnu"];
}
not.Add(new NotificationModel { Datenow = defaultDateTime, Bericht = dr["bericht"].ToString() });
}
in single line
while (dr.Read())
{
not.Add(new NotificationModel { Datenow = (dr.IsNull("datumnu") ? DateTime.Now : (DateTime)dr["datumnu"]), Bericht = dr["bericht"].ToString() });
}
I use following method to update orgnrs in a database:
UpdateService service = new UpdateService()
service.UpdateDocsA(orgnrs);
public void UpdateDocsA(List<string> orgnrs)
{
using (var context = new Data.InexchangeEntitiesA())
{
foreach (string orgnr in orgnrs)
{
try
{
Data.Customers customer = new Data.Customers();
customer.CustNo = orgnr.Trim();
context.Customers.Add(customer);
}
catch (Exception ex)
{
}
}
context.SaveChanges();
}
}
Problem is that I have multiple databases with similar update.
service.UpdateDocsA(orgnrs);
service.UpdateDocsB(orgnrs);
service.UpdateDocsC(orgnrs);
service.UpdateDocsC(orgnrs);
The only difference is in following line:
using (var context = new Data.InexchangeEntitiesA())
using (var context = new Data.InexchangeEntitiesB())
using (var context = new Data.InexchangeEntitiesC())
using (var context = new Data.InexchangeEntitiesD())
I want to create a generic update method. Any ideas how can I achieve it? or how to pass Data.InexchangeEntities to a method?
Assuming that the method signature for InexchangeEntitiesA() and InexchangeEntitiesB() etc. is common why not pass that in to your UpdateDocs method?
If we assume those methods all return an IDataContext object which implements a Customers() method;
UpdateService service = new UpdateService()
service.UpdateDocs(Data.InexchangeEntitiesA, orgnrs);
service.UpdateDocs(Data.InexchangeEntitiesB, orgnrs);
service.UpdateDocs(Data.InexchangeEntitiesC, orgnrs);
service.UpdateDocs(Data.InexchangeEntitiesD, orgnrs);
public void UpdateDocs<T>(Func<T> getContext, List<string> orgnrs) where T : IDataContext
{
using (var context = getContext())
{
foreach (string orgnr in orgnrs)
{
try
{
Data.Customers customer = context.Customers();
customer.CustNo = orgnr.Trim();
context.Customers.Add(customer);
}
catch (Exception ex)
{
}
}
context.SaveChanges();
}
}
Or something like this
public class Customer { }
public interface ISomeFunkyInterface
{
DbSet<Customer> Customers { get; set; }
}
public class DbContextA : DbContext, ISomeFunkyInterface
{
public DbSet<Customer> Customers { get; set; }
}
public void UpdateDocs<T>(List<string> orgnrs)
where T : DbContext, ISomeFunkyInterface ,new()
{
using var context = new T();
foreach (string orgnr in orgnrs)
{
context.Customers.Add(...);
}
context.SaveChanges();
}
service.UpdateDocs(orgnrs, new Data.InexchangeEntitiesA());
service.UpdateDocs(orgnrs, new Data.InexchangeEntitiesB());
service.UpdateDocs(orgnrs, new Data.InexchangeEntitiesC());
service.UpdateDocs(orgnrs, new Data.InexchangeEntitiesD());
service.UpdateDocs(orgnrs, new Data.InexchangeEntitiesE());
...
public void UpdateDocs(List<string> orgnrs, YOURCONTEXTTYPE context)
{
using (context)
{
foreach (string orgnr in orgnrs)
{
try
{
Data.Customers customer = new Data.Customers();
customer.CustNo = orgnr.Trim();
context.Customers.Add(customer);
}
catch (Exception ex)
{
}
}
context.SaveChanges();
}
}
There are multiple ways to achieve that. The most obvious to me is extract what is common and create multiple methods for that:
public void UpdateDocsA(List<string> orgnrs)
{
using (var context = new Data.InexchangeEntitiesA())
{
Perform(context, orgnrs);
}
}
void Perform(DbContext context, List<string> orgnrs)
{
foreach (string orgnr in orgnrs)
{
try
{
Data.Customers customer = new Data.Customers();
customer.CustNo = orgnr.Trim();
context.Customers.Add(customer);
}
catch (Exception ex)
{ }
}
context.SaveChanges();
}
This will reduce the duplicated code massivly. However it still feels ugly, because you still have four Update-methods.
You could also extract what is uncommon and inject a paremeter to switch on:
public void UpdateDocs(string type, List<string> orgnrs)
{
using (var context = GetContext(type))
{
Perform(context, orgnrs);
}
}
DbContext GetContext(string type)
{
return type == "A" ? new Data.InexchangeEntitiesA() :
type == "B" ? new Data.InexchangeEntitiesB() :
type == "C" ? new Data.InexchangeEntitiesC() :
new Data.InexchangeEntitiesD();
}
This does not seem much better, does it? We only have a single method, however it still uses some weird type-switching. Let´s try something more generic - assuming all your entities have some common base-interface or -class:
public void UpdateDocs<T>(List<string> orgnrs) where T: IMyInterface, new()
{
using (var context = new T)
{
Perform(context, orgnrs);
}
}
i want to save the last searched query in a global variable and when i use that in another method it says :
ObjectDisposedException was unhandled by user code: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
IQueryable lastQry = null;
private void SearchMethod()
{
using(var ctx = new entityContex())
{
var qry = ctx.Table1.Where(t=> t.Name.StartWith(txtName.Text)).Take(100);
lastQry = qry;
dgvResult.DataSource = qry.ToList();
}
}
private void RefreshResult()
{
using(var ctx = new entityContex())
{
if(lastQry != null)
//here is the Error ! <<---------------->>
dgvResult.DataSource = lastQry.ToList();
}
}
One way would be to create and store a delegate which runs the query:
Func<entityContex, IQueryable</*Table1 type*/>> queryExecutor = null;
private void SearchMethod()
{
using(var ctx = new entityContex())
{
queryExecutor = c => c.Table1.Where(t=> t.Name.StartWith(txtName.Text)).Take(100);
var qry = queryExecutor(ctx);
dgvResult.DataSource = qry.ToList();
}
}
private void RefreshResult()
{
using(var ctx = new entityContex())
{
if(queryExecutor != null)
dgvResult.DataSource = queryExecutor(ctx).ToList();
}
}
Rather than storing the IQueryable, store the list:
List<Table1> lastQry = null;
private void SearchMethod()
{
using(var ctx = new entityContex())
{
var qry =
ctx.Table1
.Where(t=> t.Name.StartWith(txtName.Text))
.Take(100)
.ToList(); // <--
lastQry = qry;
dgvResult.DataSource = qry;
}
}
private void RefreshResult()
{
using(var ctx = new entityContex())
{
if(lastQry != null)
dgvResult.DataSource = lastQry;
}
}
I have the following scenario, How can i do this without getting the System.InvalidOperationException error.
SomeClass.cs:
using (var eo = new MyEntities())
{
targetRole = (from p in eo.UserRoles
where p.Code == 2
select p).FirstOrDefault();
}
var user = new User
{
UserName = userName,
Password = txtPassword.Text.Trim(),
UserRole = targetRole
};
AnotherClass.AddObject(user);
AnotherClass.cs
public static void AddObject(object poco)
{
using (var eo = new MyEntities())
{
eo.AddObject("Users", poco);
eo.SaveChanges(); //<--- Exceptions Thrown.
}
}
I found the answer myself, I need to attach the targetRole object to the current context:
AnotherClass.cs :
public static void AddObject(object poco)
{
using (var eo = new MyEntities())
{
eo.UserRoles.Attach(targetRole); //<-- the magic
eo.AddObject("Users", poco);
eo.SaveChanges(); //<--- it works like a charm. Hoorah
}
}