Confusion with attaching/detaching and different ObjectContext object - c#

I open a new MessagesWindow which has this code:
namespace Newsletter.UI
{
public partial class MessagesWindow : Window
{
MessageService msgService;
public MessagesWindow()
{
InitializeComponent();
msgService = new MessageService();
}
private void CreateButtonClick(object sender, RoutedEventArgs e)
{
var createMessageWindow = new CreateMessageWindow { Owner = this };
createMessageWindow.ShowDialog();
gridMessages.ItemsSource = from msg in msgService.GetAllMessages()
select msg;
}
private void messagesWindow_Loaded(object sender, RoutedEventArgs e)
{
gridMessages.ItemsSource = from msg in msgService.GetAllMessages()
select msg;
}
private void EditButtonClick(object sender, RoutedEventArgs e)
{
if (gridMessages.SelectedItem == null)
MessageBox.Show("You have to choose the message first.");
else
{
var selectedMessage = (Message)gridMessages.SelectedItem;
var editMessageWindow = new EditMessageWindow(selectedMessage);
editMessageWindow.Owner = this;
editMessageWindow.ShowDialog();
}
gridMessages.ItemsSource = from msg in msgService.GetAllMessages()
select msg;
}
}
}
I open a new window with EditButtonClick where I want to edit my Message. The code of the EditMessageWindow:
namespace Newsletter.UI
{
public partial class EditMessageWindow : Window
{
private Message editMessage;
private MessageService messageService;
public EditMessageWindow(Message message)
{
InitializeComponent();
editMessage = message;
messageService = new MessageService();
}
private void CreateMessageWindowLoaded(object sender, RoutedEventArgs e)
{
subject.Text = editMessage.Subject;
messageContent.AppendText(editMessage.Content);
senderComboBox.ItemsSource = messageService.GetAllSenders();
mailingListComboBox.ItemsSource = messageService.GetAllMailingLists();
}
private void SaveClick(object sender, RoutedEventArgs e)
{
editMessage.Date = DateTime.Now;
editMessage.Content = messageContent.Text;
editMessage.MailingList.Add(messageService.GetAllMailingLists()[mailingListComboBox.SelectedIndex]); //ERROR
editMessage.Sender = messageService.GetAllSenders()[senderComboBox.SelectedIndex];
editMessage.Subject = subject.Text;
editMessage.HasAttachments = false;
messageService.EditMessage(editMessage);
Close();
}
}
}
I get an error on SaveClick:
The relationship between the two objects cannot be defined because
they are attached to different ObjectContext objects.
Finally, MessageService code:
namespace Newsletter.Services
{
public class MessageService
{
private NewsletterEntities _context;
public MessageService()
{
_context = new NewsletterEntities();
}
public void AddMessage(Message newMessage)
{
_context.Messages.AddObject(newMessage);
_context.SaveChanges();
}
public void EditMessage(Message editMessage)
{
var messageToChange = (from m in _context.Messages where editMessage.MessageID == m.MessageID select m).FirstOrDefault();
if (messageToChange != null)
{
messageToChange = editMessage;
_context.SaveChanges();
}
}
public List<Message> GetAllMessages()
{
return (from m in _context.Messages select m).ToList();
}
public List<MailingList> GetAllMailingLists()
{
var query = from m in _context.MailingLists select m;
return query.ToList();
}
public List<Sender> GetAllSenders()
{
var query = from m in _context.Senders select m;
return query.ToList();
}
public void DeleteMessage(Message message)
{
List<MailingList> mailingLists = new List<MailingList>();
foreach(MailingList ml in message.MailingList.ToList())
{
ml.Message.Remove(message);
}
_context.Messages.DeleteObject(message);
_context.SaveChanges();
}
}
}
I'm getting consfused with all those layers and contexts. I know I should probably somehow detach the message, do something with it and attach but I don't know where and how. Should I do it in the service layer in MessageService, UI layer, or maybe DAL layer where I have all my entities? Or should I redesign my solution and do the context logic in different place?

you appear to have the setup of context per window. that will have 2 impacts on how you maintain the objects within the context.
You do not want to pass objects from form to form. instead you will pass the ID(s) from form to form and load the entity within the context.
As an entity is updated within one context you will need to notify the other windows/contexts of the change so they can refresh their data.
here is an MSDN article detailing the process of context per form. The article uses NHibernate, but the process would be almost identical for EF and DbCotext
http://msdn.microsoft.com/en-us/magazine/ee819139.aspx

Related

How to instantiate a new Windows Form with Quartz.NET without problems?

I'm using the Quartz.NET library to create a job in my C# application.
I have some registers in my database, so I have a table wich contains a column called "start_date". The job runs every 50 seconds, so I compare the dates from the column "start_date" with the date of my computer, and if the dates are equal, I want to instantiate a new Windows Form with a message and a button.
At the moment, the new Windows Form is opening at the right moment, but the message is not showed and the window stops to respond.
Basically, in my code I have something like this:
FormMessage.cs
public partial class FormMessage : Form
{
public FormMessage()
{
InitializeComponent();
}
public FormMessage(double minutes)
{
InitializeComponent();
string message = string.Format("You have {0} minutes!", minutes);
lblMessage.Text = message ;
}
private void btnOK_Click(object sender, EventArgs e)
{
this.Close();
}
}
JobMessage.cs
public class JobMessage: IJob
{
List<Information> informations;
public void Execute(IJobExecutionContext context)
{
//Class with methods to get registers from database.
InformationAPI infoAPI = new InformationAPI();
informations = infoAPI.GetInformations();
foreach (Information info in informations)
{
DateTime computerDateTime = DateTime.Now;
DateTime infoDateTime = info.StartDate;
double difference;
if (DateTime.Compare(computerDateTime, infoDateTime) < 0)
{
difference = Math.Round(infoDateTime.Subtract(computerDateTime).TotalMinutes);
if (difference == 5)
{
FormMessage formMessage = new FormMessage(difference);
formMessage.Show();
}
}
}
}
}
Someone have some idea of the reason why the FormMessage window stops to respond?
Thank you for your attention!
You can try Quartz Listeners to let them open the form to show the data and keep the execution out of the job scope:
Action<IJobExecutionContext, JobExecutionException> listenerAction = (c, e) => {
var dataMap = context.GetJobDetail().GetJobDataMap();
var difference = dataMap.GetIntValue("difference");
FormMessage formMessage = new FormMessage(difference);
formMessage.Show();
}
var listener = new SyncJobListener(listenerAction);
And add the listener in to the scheduler:
scheduler.ListenerManager.AddJobListener(listener,
GroupMatcher<JobKey>.GroupEquals("GroupName"));
Using this SyncJobListener:
public class SyncJobListener : IJobListener
{
private readonly Action<IJobExecutionContext, JobExecutionException> _syncExecuted;
public string Name { get; private set; }
public SyncJobListener(
Action<IJobExecutionContext, JobExecutionException> syncExecuted
)
{
Name = Guid.NewGuid().ToString();
_syncExecuted = syncExecuted;
}
public void JobToBeExecuted(IJobExecutionContext context)
{
}
public void JobExecutionVetoed(IJobExecutionContext context)
{
}
public void JobWasExecuted(IJobExecutionContext context, JobExecutionException jobException)
{
_syncExecuted(context, jobException);
}
}
I have not tested this so if the dataMap does not have any data, you are going to need to allow the persistance:
[PersistJobDataAfterExecution]
[DisallowConcurrentExecution]
public class JobMessage: IJob {}

pjsip c#, System.AccessViolationException on answering an incoming call on an separated thread

In the overrided void onIncomingCall I open a new Background thread which shows a new (Popup) Form.
I try to pickup or decline the Call in this Form but then I get an System.AccessViolationException.
it seems that the current call object is locked and cannot be accessed from another Form.
If I use the currentcall.answer(prm) in the onIncomingCall void then the call is established successfully (without another Form and thread).
public class myaccount : Account
{
public override void onIncomingCall(OnIncomingCallParam prm)
{
Call call = new Call(this, prm.callId);
CallOpParam param = new CallOpParam();
param.statusCode = pjsip_status_code.PJSIP_SC_RINGING;
param.statusCode = pjsip_status_code.PJSIP_SC_OK;
pjsipfunctions.currentparam = param;
pjsipfunctions.currentcall = call;
var thread = new Thread(() =>
{
ShowPopup(call,prm.callId.ToString());
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
// Thread myCallingThread = new Thread(new ThreadStart(ShowPopup));
//myCallingThread.SetApartmentState(ApartmentState.STA);
//myCallingThread.IsBackground = true;
//myCallingThread.Start();
}
public void ShowPopup(Call myCall,string call_id)
{
IncommingCallPopup win = new IncommingCallPopup(ref myCall, call_id );
win.Show();
System.Windows.Threading.Dispatcher.Run();
}
}
#####################################
public IncommingCallPopup(ref Call info, string callid)
{
currentCall = info;
Callid = callid;
CurrentCall = currentCall;
Prm.statusCode = pjsip_status_code.PJSIP_SC_RINGING;
InitializeComponent();
labelCallId.Content = callid;
}
private void rejectcall(object sender, RoutedEventArgs e)
{
Prm.statusCode = pjsip_status_code.PJSIP_SC_DECLINE;
CurrentCall.hangup(Prm);
}
private void transfercall(object sender, RoutedEventArgs e)
{
}
private void takecall(object sender, RoutedEventArgs e)
{
Prm.statusCode = pjsip_status_code.PJSIP_SC_OK;
try
{
CurrentCall.answer(Prm);
}
catch(Exception ex)
{
}
}
After crazy searching and trying some things i've figured it out.
Every Thread must registered in the Endpoint that you can hangup, pickup or transfer your call in another Class(Window).
All you need is to write this Method in a Public class and call this void everytime you call your void from another window ->
public void callpopup(String number) {
checkThread();
//answer, decline or something else
}
[MethodImpl(MethodImplOptions.Synchronized)]
public static void checkThread()
{
try
{
if (ep != null && !ep.libIsThreadRegistered())
ep.libRegisterThread(Thread.CurrentThread.Name);
}
catch (Exception e)
{
}
}

Local database with SQLite in Windows phone 8

I create a test app with a local database in Windows phone 8 but I get error and my project can't create my sqlite database
Can you help me ?
Runtime error is this :
An exception of type 'System.Reflection.TargetInvocationException' occurred in mscorlib.ni.dll but was not handled in user code
Additional information: Exception has been thrown by the target of an invocation.
In this line : val = m.GetValue( obj , null ) ;
Person class :
namespace PhoneApp4
{
public class person
{
[SQLite.AutoIncrement , SQLite.PrimaryKey]
public int ID { get; set; }
public string FullName { get; set; }
}
}
mainpage.xaml.cs:
namespace PhoneApp4
{
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
// Sample code to localize the ApplicationBar
//BuildLocalizedApplicationBar();
}
private async void BTO_save_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
SQLiteAsyncConnection conn = new SQLiteAsyncConnection(Path.Combine(ApplicationData.Current.LocalFolder.Path, "dbsqlite.sqlite"), true);
person person = new person
{
FullName = TB_save.Text
};
await conn.InsertAsync(person);
}
private async void BTO_search_Tap(object sender, System.Windows.Input.GestureEventArgs e)
{
SQLiteAsyncConnection conn = new SQLiteAsyncConnection(Path.Combine(ApplicationData.Current.LocalFolder.Path, "people.db"), true);
var query = conn.Table<person>().Where(x => x.ID == Convert.ToInt32(TB_search.Text));
var result = await query.ToListAsync();
foreach (var item in result)
{
LB_search.Items.Add(item.ID.ToString() + " " + item.FullName.ToString());
}
}
app.xaml.cs:
private void Application_Launching(object sender, LaunchingEventArgs e)
{
string dbPath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "dbsqlite.sqlite");
using (var db = new SQLiteConnection(dbPath))
{
db.CreateTable<person>();
}
}
// Code to execute when the application is activated (brought to foreground)
// This code will not execute when the application is first launched
private void Application_Activated(object sender, ActivatedEventArgs e)
{
string dbPath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "dbsqlite.sqlite");
if (FileExists("dbsqlite.sqlite").Result)
{
using (var db = new SQLiteConnection(dbPath))
{
db.CreateTable<person>();
}
}
}
// Code to execute when the application is deactivated (sent to background)
// This code will not execute when the application is closing
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
string dbPath = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "dbsqlite.sqlite");
if (!FileExists("dbsqlite.sqlite").Result)
{
using (var db = new SQLiteConnection(dbPath))
{
db.CreateTable<person>();
}
}
}
See this sample
http://code.msdn.microsoft.com/SQLite-for-Windows-Phone-8-8ff3beaf
private async void Application_Launching(object sender, LaunchingEventArgs e)
{
try
{
await ApplicationData.Current.LocalFolder.GetFileAsync("UniversityDB.db");
Connection = new SQLiteAsyncConnection("UniversityDB.db");
}
catch (FileNotFoundException)
{
DataService.CreateDbAsync();
}
}
and
public static async void CreateDbAsync()
{
App.Connection = new SQLiteAsyncConnection("UniversityDB.db");
var universityTask = App.Connection.CreateTableAsync<University>();
var studentTask = App.Connection.CreateTableAsync<Student>();
await Task.WhenAll(new Task[] { universityTask, studentTask });
}

Create a singleton for referenced assembly

I have a hardware component that can be controlled via an SDK (a .DLL file referenced in the project).
I'm trying to implement a helper class that will handle the instantiation of the object and will serve the required object reference to each window that will require it.
This is how I create the object in the code behind of the window:
//private fields
SystemConnector myConn;
MyHardware mySystem; // this is the object i need a reference to
public Window1()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
SystemDiscoverer SystemDiscoverer = new SystemDiscoverer();
SystemDiscoverer.Discovered += (sysInfo) =>
{
myConn = new SystemConnector(sysInfo.IPAddress);
if (myConn != null)
mySystem = new MyHardware(myConn);
};
SystemDiscoverer.Discover();
}
private void WindowBase_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
if (myConn != null)
myConn.Dispose();
if (mySystem != null)
mySystem.Dispose();
}
I want to move this logic into a helper class but I have some questions I couldn't find a solution for:
all of the Singleton examples I could find were for custom objects
and did not explain the use of a referenced object.
How to handle
the creation of the object since it's done inside of an event handler?
To handle the creation from an event handler, try:
LazyInitializer.EnsureInitialized(...)
It is threadsafe, so you should be able to initialize a static property for your singleton just fine.
MSDN article here:
http://blogs.msdn.com/b/jolud/archive/2010/04/02/initialization-of-shared-resources.aspx
An example, from the ASP.NET MVC 4 standard project template.
public sealed class InitializeSimpleMembershipAttribute : ActionFilterAttribute {
private static SimpleMembershipInitializer _initializer;
private static object _initializerLock = new object();
private static bool _isInitialized;
public override void OnActionExecuting(ActionExecutingContext filterContext) {
// Ensure ASP.NET Simple Membership is initialized only once per app start
LazyInitializer.EnsureInitialized(ref _initializer, ref _isInitialized, ref _initializerLock);
}
private class SimpleMembershipInitializer {
public SimpleMembershipInitializer() {
Database.SetInitializer<UsersContext>(null);
try {
using(var context = new UsersContext()) {
if(!context.Database.Exists()) {
// Create the SimpleMembership database without Entity Framework migration schema
((IObjectContextAdapter)context).ObjectContext.CreateDatabase();
}
}
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);
}
catch(Exception ex) {
throw new InvalidOperationException("The ASP.NET Simple Membership database could not be initialized. For more information, please see http://go.microsoft.com/fwlink/?LinkId=256588", ex);
}
}
}
}
I would wrap MyHardware class inside a singleton with an async method which wrap the SystemDiscoverer.Discovered Event:
public sealed class MyHardwareSingleton
{
static MyHardwareSingleton()
{ }
private MyHardwareSingleton()
{ }
private static readonly MyHardwareSingleton _myHardware = new MyHardwareSingleton();
private SystemConnector _myConn;
private MyHardware _mySystem;
public MyHardwareSingleton Instance
{
get { return _myHardware; }
}
public Task<MyHardware> GetHardwareAsync()
{
if (_mySystem != null)
{
return Task.FromResult(_mySystem);
}
var tcs = new TaskCompletionSource<MyHardware>();
SystemDiscoverer SystemDiscoverer = new SystemDiscoverer();
SystemDiscoverer.Discovered += (sysInfo) =>
{
myConn = new SystemConnector(sysInfo.IPAddress);
if (myConn != null)
{
mySystem = new MyHardware(myConn);
tcs.TrySetResult(mySystem);
return tcs.Task;
}
// This indicated that myConn came back null.
tcs.TrySetResult(null);
return tcs.Task;
};
// Make SystemDiscoverer run asynchrnously. We will await it so when it completes we will get the desired MyHardware instance.
SystemDiscoverer.DiscoverAsync();
return tcs.Task;
}
}
DiscoverAsync might be added inside the SystemConnector class and look like this:
public Task DiscoverAsync()
{
return Task.Run(() => Discover());
}
And then you can call it from your code via:
private async void Window_Loaded(object sender, RoutedEventArgs e)
{
var myHardware = await MyHardwareSingleton.Instance.DiscoverAsync();
}
Of course, i didn't handle any exception handling / cancellation. This is just a sample of what could be done using TAP

Calling own WCF WebService twice from different BackgroundWorkers closes application

I've written a small WCF WebService (.svc) which I want to call from other applications. It's working fine so far.
In another application I've got a Windows Form. When this is shown, it will call BackgroundWorker1 to get a list of objects from the WebService and show it to the user on it's completed1-method. So far, so good.
Now, after the user selects one object and clicks "ok", then BackgroundWorker2 will call the WebService again to gather more information. But here, within the doWork2-method exactly after the WebService-call, the whole application breaks without any exceptions or errors. It just closes directly after the WebService is called.
This is very weird, because as I have a look at the WebServices log files, it seems to work normal and still logs the successful operation AFTER the other application is closed. So the WebService cannot be the problem, I think.
Another weird thing: If I call the WebService the second time on the GUI thread (and not with BackgroundWorker2), it just works. It blocks the UI, but it works.
So, why is my application just "broken" after the second call without any notification?
Any ideas are very much welcomed.
Thanks in advance.
Greets
Here's some simplified code. The application closes in "bgwGetSingleCar_DoWork":
public partial class MyForm : Form
{
private Controller _ctrl { get; set; }
private Config _config { get; set; }
private List<Cars> _cars { get; set; }
public bool Result { get; private set; }
public MyForm(Controller ctrl, Config config)
{
this._ctrl = ctrl;
this._config = config;
this.Result = false;
InitializeComponent();
}
private void MyForm_Load(object sender, EventArgs e)
{
try
{
this.bgwGetAllOffers.RunWorkerAsync(new WorkerInfo()
{
WorkerType = WorkerType.Type1,
IdLocal = this._config.IdLocal,
IdExternal = this._config.IdExternal,
});
}
catch (Exception ex)
{
// ...
}
}
private void FillList(List<Cars> list)
{
// ...
}
private void btnOk_Click(object sender, EventArgs e)
{
CarListItem v = (CarListItem)this.lstBoxCars.SelectedValue;
this._config.IdExternal = v.IdExternal;
try
{
this.bgwGetSingleCar.RunWorkerAsync(new WorkerInfo()
{
WorkerType = WorkerType.Type2,
IdLocal = this._config.IdLocal,
IdExternal = this._config.IdExternal,
});
}
catch (Exception ex)
{
// ...
}
}
private void bgwGetAllCars_DoWork(object sender, DoWorkEventArgs e)
{
try
{
WorkerInfo info = (WorkerInfo)e.Argument;
Client client = new Client();
GetCarsResult result = client.GetAllCars(new GetAllCarsRequest()
{
IdLocal = info.IdLocal,
IdExternal = info.IdExternal
});
if (!result.Success)
{
// ...
}
if (result.Cars != null)
{
this._cars = result.Cars.ToList();
}
}
catch (Exception ex)
{
/// ...
}
}
private void bgwGetAllCars_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.FillList(this._cars);
}
private void bgwGetSingleCar_DoWork(object sender, DoWorkEventArgs e)
{
try
{
WorkerInfo info = (WorkerInfo)e.Argument;
Client client = new Client();
// Application closes after this line, but WebService log still shows activity
GetCarsResult result = client.GetSingleCar(new GetSingleCarRequest()
{
IdLocal = info.IdLocal,
IdExternal = info.IdExternal
});
if (result.Success)
{
this.Result = true;
}
}
catch (Exception ex)
{
/// ...
}
}
private void bgwGetSingleOffer_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.DialogResult = System.Windows.Forms.DialogResult.OK;
}
}
public class CarListItem : Car
{
public override string ToString()
{
return ((DateTime)this.LastUpdate).ToString("dd.MM.yyyy - HH:mm");
}
}
I just found my "design break".
It was the button's "DialogResult" which caused the application to close too soon. But strange that the debugger didn't step ahead...

Categories

Resources