ZKEmkeeper: Events not triggering, DLL look like not working - c#

I'm stucked for a while trying to use zkemkeeper sdk to use on a application that uses a InBios(Controller) for fingerprint. While i trying to trigger some event nothing happen, i just created a Console Application for test the SDK before start implementing on ASP.NET MVC here is my code, i first connect to the device and then i add the event OnAttTransactionEx someone can point me what i'm doing wrong.
Here is my code:
private static void Main(string[] args)
{
CZKEMClass zkem = new CZKEMClass();
int idwErrorCode = 0;
const string ipAddr = "10.0.1.240";
bool isConnected;
try
{
isConnected = zkem.Connect_Net(ipAddr, Convert.ToInt32(4370));
}
catch (Exception ext)
{
Console.WriteLine("Erro: " + ext);
zkem.GetLastError(ref idwErrorCode);
if (idwErrorCode != 0)
{
zkem.GetLastError(idwErrorCode);
}
else
{
Console.WriteLine("No data from terminal returns!");
}
throw new Exception();
}
if (isConnected)
{
//Here you can register the realtime events that you want to be triggered(the parameters 65535 means registering all)
zkem.EnableDevice(1, true);
zkem.RegEvent(1, 65535);
zkem.OnAttTransactionEx += axCZKEM1_OnAttTransactionEx;
string sdwEnrollNumber = "";
int idwVerifyMode = 0;
int idwInOutMode = 0;
int idwYear = 0;
int idwMonth = 0;
int idwDay = 0;
int idwHour = 0;
int idwMinute = 0;
int idwSecond = 0;
int idwWorkcode = 0;
zkem.EnableDevice(1, false); //disable the device
if (zkem.ReadGeneralLogData(1))
{
//read all the attendance records to the memory
while (zkem.SSR_GetGeneralLogData(1, out sdwEnrollNumber, out idwVerifyMode,
out idwInOutMode, out idwYear, out idwMonth, out idwDay, out idwHour,
out idwMinute, out idwSecond, ref idwWorkcode))
{
//get records from the memory
DateTime datetime = new DateTime(idwYear, idwMonth, idwDay, idwHour, idwMinute, idwSecond);
int unixDate = (int) datetime.Subtract(new DateTime(1970, 1, 1)).TotalSeconds;
try
{
Console.WriteLine(idwInOutMode);
Console.WriteLine(sdwEnrollNumber);
Console.WriteLine(unixDate);
}
catch (Exception ex)
{
//ignored
}
try
{
Console.WriteLine("inserted: " +
$"{idwYear}/{idwMonth}/{idwDay} {idwHour}:{idwMinute}:{idwSecond}.000");
}
catch (Exception ex)
{
}
}
}
Console.WriteLine("Fim");
}
else
{
zkem.GetLastError(ref idwErrorCode);
if (idwErrorCode != 0)
{
zkem.GetLastError(idwErrorCode);
}
else
{
Console.WriteLine("No data from terminal returns!");
}
}
zkem.EnableDevice(1, true);
Console.WriteLine("Teste");
do
{
while (!Console.KeyAvailable)
{
}
} while (Console.ReadKey(true).Key != ConsoleKey.Escape);
}
public static void axCZKEM1_OnAttTransactionEx(string sEnrollNumber, int iIsInValid, int iAttState,
int iVerifyMethod, int iYear, int iMonth, int iDay, int iHour, int iMinute, int iSecond, int iWorkCode)
{
Console.WriteLine("Finger Recognized");
}

You must use STA thread .
Thread TT = new Thread(() =>
{
CZKEMClass zkem = new CZKEMClass();
Application.Run();
});
TT.IsBackground = true;
TT.SetApartmentState(ApartmentState.STA);
TT.Start();
Then create event. ZKEM events fire on STA threads.

Related

How can I get absent days from ZKTeco device?

I have a ZKTeco K80 device, what I can get now are the logs data ( DateTime, InOut, VerifyMethod..)
private void btnPullData_Click(object sender, EventArgs e)
{
try
{
ShowStatusBar(string.Empty, true);
ICollection<MachineInfo> lstMachineInfo = manipulator.GetLogData(objZkeeper, int.Parse(tbxMachineNumber.Text.Trim()));
if (lstMachineInfo != null && lstMachineInfo.Count > 0)
{
BindToGridView(lstMachineInfo);
ShowStatusBar(lstMachineInfo.Count + " records found !!", true);
}
else
DisplayListOutput("No records found");
}
catch (Exception ex)
{
DisplayListOutput(ex.Message);
}
}
public ICollection<MachineInfo> GetLogData(ZkemClient objZkeeper, int machineNumber)
{
string dwEnrollNumber1 = "";
int dwVerifyMode = 0;
int dwInOutMode = 0;
int dwYear = 0;
int dwMonth = 0;
int dwDay = 0;
int dwHour = 0;
int dwMinute = 0;
int dwSecond = 0;
int dwWorkCode = 0;
ICollection<MachineInfo> lstEnrollData = new List<MachineInfo>();
objZkeeper.ReadAllGLogData(machineNumber);
while (objZkeeper.SSR_GetGeneralLogData(machineNumber, out dwEnrollNumber1, out dwVerifyMode, out dwInOutMode, out dwYear, out dwMonth, out dwDay, out dwHour, out dwMinute, out dwSecond, ref dwWorkCode))
{
string inputDate = new DateTime(dwYear, dwMonth, dwDay, dwHour, dwMinute, dwSecond).ToString();
MachineInfo objInfo = new MachineInfo();
objInfo.MachineNumber = machineNumber;
objInfo.IndRegID = int.Parse(dwEnrollNumber1);
objInfo.DateTimeRecord = inputDate;
objInfo.dwInOutMode = dwInOutMode;
lstEnrollData.Add(objInfo);
}
return lstEnrollData;
}
Ref : Csharp-ZKTeco-Biometric-Device-Getting-Started
I'm searching for a method to get the absent days , how can I configure the device to count all the absent days starting from a week and except Saturday and Sunday or this is not related to the device and should I configure it myself using SQL tables??
Well, you can not get the absent days from the biometric device. It must be part of your application logic. You have to read all the attendance data from the biometric device, and consider all the missing dates as absent days.

How can I re-encode video frames to another codec using ffmpeg?

I am trying to learn ffmpeg, so I started a small project where I am sending an MP4 video stream to my C# application where I want to re-encode the video to webM and send it to an icecast server.
My icecast server is receiving the video but I am not able to reproduce it (the video time is updated each time I press play but the video doesn't play and I only see a black frame)
Anyone can help me? I have no idea of what is wrong in my code.
My code execution flow is openInput->openOutput->streamingTest
private void openInput()
{
_pInputFormatContext = ffmpeg.avformat_alloc_context();
var pFormatContext = _pInputFormatContext;
ffmpeg.avformat_open_input(&pFormatContext, configuration.Source, null, null).ThrowExceptionIfError();
ffmpeg.avformat_find_stream_info(_pInputFormatContext, null).ThrowExceptionIfError();
// find the first video stream
for (var i = 0; i < _pInputFormatContext->nb_streams; i++)
if (_pInputFormatContext->streams[i]->codec->codec_type == AVMediaType.AVMEDIA_TYPE_VIDEO)
{
pInputStream = _pInputFormatContext->streams[i];
break;
}
if (pInputStream == null) throw new InvalidOperationException("Could not found video stream.");
_inputStreamIndex = pInputStream->index;
_pInputCodecContext = pInputStream->codec;
var codecId = _pInputCodecContext->codec_id;
var pCodec = ffmpeg.avcodec_find_decoder(codecId);
if (pCodec == null) throw new InvalidOperationException("Unsupported codec.");
ffmpeg.avcodec_open2(_pInputCodecContext, pCodec, null).ThrowExceptionIfError();
configuration.CodecName = ffmpeg.avcodec_get_name(codecId);
configuration.FrameSize = new Size(_pInputCodecContext->width, _pInputCodecContext->height);
configuration.PixelFormat = _pInputCodecContext->pix_fmt;
_pPacket = ffmpeg.av_packet_alloc();
_pFrame = ffmpeg.av_frame_alloc();
}
private bool openOutput()
{
int ret;
_pOutputFormatContext = ffmpeg.avformat_alloc_context();
fixed (AVFormatContext** ppOutputFormatContext = &_pOutputFormatContext)
{
ret = ffmpeg.avformat_alloc_output_context2(ppOutputFormatContext, null, "webm", configuration.Destination);
if (ret < 0)
{
return false;
}
}
AVOutputFormat* out_format = ffmpeg.av_guess_format(null, configuration.Destination, null);
// Configure output video stream
_pOutputStream = ffmpeg.avformat_new_stream(_pOutputFormatContext, null);
AVStream* pInputVideoStream = null;
for (var i = 0; i < _pInputFormatContext->nb_streams; i++)
{
if (_pInputFormatContext->streams[i]->codec->codec_type == AVMediaType.AVMEDIA_TYPE_VIDEO)
{
pInputVideoStream = _pInputFormatContext->streams[i];
}
}
ffmpeg.avcodec_parameters_copy(_pOutputStream->codecpar, pInputVideoStream->codecpar);
_pOutputStream->codecpar->codec_type = AVMediaType.AVMEDIA_TYPE_VIDEO;
_pOutputStream->codecpar->codec_id = AVCodecID.AV_CODEC_ID_VP8;
AVDictionary* opt_dict;
ffmpeg.av_dict_set(&opt_dict, "content_type", "video/webm", 0);
ffmpeg.av_dict_set(&opt_dict, "user_agent", "GCS", 0);
fixed (AVFormatContext** ppOutputFormatContext = &_pOutputFormatContext)
{
ret = ffmpeg.avio_open2(&_pOutputFormatContext->pb, configuration.Destination, ffmpeg.AVIO_FLAG_WRITE, null, &opt_dict);
if (ret < 0)
{
return false;
}
}
ret = ffmpeg.avformat_write_header(_pOutputFormatContext, null);
if (ret < 0)
{
return false;
}
ffmpeg.av_dump_format(_pOutputFormatContext, 0, configuration.Destination, 1);
return true;
}
private unsafe void streamingTest(object gggg)
{
isStreamUp = true;
AVPacket frame = new AVPacket();
AVPacket* pFrame = &frame;
ffmpeg.av_init_packet(pFrame);
updateState(VideoStreamStates.Streaming);
try
{
long start_time = ffmpeg.av_gettime();
DateTime lastFrame = DateTime.MinValue;
while (isStreamUp)
{
if (cancelationToken.IsCancellationRequested)
{
throw new TaskCanceledException();
}
try
{
int error;
isReadingFrame = true;
do
{
error = ffmpeg.av_read_frame(_pInputFormatContext, pFrame);
if (error == ffmpeg.AVERROR_EOF)
{
frame = *pFrame;
continue;
}
error.ThrowExceptionIfError();
} while (frame.stream_index != _inputStreamIndex);
isWritingFrame = true;
//frame.stream_index = _outputStreamIndex;
_pOutputCodecContext = ffmpeg.avcodec_alloc_context3(_pOutputFormatContext->video_codec);
int ret = 0;
while (ret >= 0)
{
ret = ffmpeg.avcodec_receive_packet(_pOutputCodecContext, pFrame);
}
//ffmpeg.avcodec_send_frame(_pOutputCodecContext, pFrame);
//ffmpeg.avcodec_send_packet(_pOutputCodecContext, pFrame);
ret = ffmpeg.av_write_frame(_pOutputFormatContext, pFrame);
isWritingFrame = false;
if (frame.stream_index == _inputStreamIndex)
{
if (ret < 0)
{
Console.WriteLine("Missed frame");
missedFrames++;
}
else
{
Console.WriteLine("Sent frame");
sentFrames++;
}
AVRational time_base = _pInputFormatContext->streams[_inputStreamIndex]->time_base;
AVRational time_base_q = new AVRational();
time_base_q.num = 1;
time_base_q.den = ffmpeg.AV_TIME_BASE;
long pts_time = ffmpeg.av_rescale_q(frame.dts, time_base, time_base_q);
//long pts_time = ffmpeg.av_rescale_q(frame.dts, time_base_q, time_base);
long now_time = ffmpeg.av_gettime() - start_time;
if (pts_time > now_time)
ffmpeg.av_usleep((uint)(pts_time - now_time));
}
else
Console.WriteLine("????");
}
catch (Exception ex)
{
Console.WriteLine("Erro ao enviar: " + ex.Message);
}
finally
{
ffmpeg.av_packet_unref(pFrame);
}
}
}
catch (TaskCanceledException)
{
updateState(VideoStreamStates.Stopped);
}
catch (Exception e)
{
Console.WriteLine(e.Message.ToString());
}
}

C# Issues with MCP2210.dll

I am currently developing a project with the USB Device MCP2210 by Microchip in c# on Visual Studio. I am using .Net v4 and the mcp2210_dll_dotnetv4_x86.dll released by Microchip. I am unfortunately stumped on why I am having issues connecting to the HID device. My issue arises from the call
D1.Handle = MCP2210.M_Mcp2210_OpenByIndex(D1.VID, D1.PID, i-1, null)<
Its on line 84. The response is error -106, The Device is already connected. Which is great and all but I need to connect it with this function call to receive the proper handle. If i change the index, currently i-1, I get a -101 error, no device at that index. Does anyone have a suggestion to diagnose or solve this problem? Ill post the project code below
using mcp2210_dll_m;
using System;
using System.Text;
namespace HID_Command_Line
{
public struct Device
{
public ushort PID { get; set; }
public ushort VID { get; set; }
public IntPtr Handle { get; set; }
public Device (ushort vid, ushort pid, IntPtr handle)
{
VID = vid;
PID = pid;
Handle = handle;
}
}
class Program
{
static void Main(string[] args)
{
byte[] Command = new byte[64];
string temp;
ConsoleKeyInfo code;
temp = ReadCommand();
do
{
Command = Converter.ToByteArray(temp);
Converter.CheckByteArray(Command);
code = Console.ReadKey();
Console.Write("\r\n");
if (code.Key == ConsoleKey.Y)
{
if (ConnectUSB() == true)
{
// SetUpDeviceSettings();
}
}
else if (code.Key == ConsoleKey.N)
{
temp = ReadCommand();
}
} while (code.Key != ConsoleKey.Escape);
Console.WriteLine("Exited Correctly");
}
public static string ReadCommand()
{
string temp;
Console.WriteLine("Please Enter the Command in Hex Format");
//Console.WriteLine(" Type Cntrl + Space for Nul ");
temp = Console.ReadLine();
int length = temp.Length / 2;
for (int i = length; i < 64; i++)
{
temp += "00";
}
return temp;
}
public static bool ConnectUSB()
{
bool confirmation = false;
Device D1 = new Device(0x04D8, 0x00DE, (IntPtr)null);
uint i = (uint) MCP2210.M_Mcp2210_GetConnectedDevCount(D1.VID, D1.PID);
if (i == 1)
{
Console.WriteLine("yeah Baby, Press any Button to Connect to Single Devices in your Area"); //assuming that the index for 1 device is always 0
Console.Read();
D1.Handle = MCP2210.M_Mcp2210_OpenByIndex(D1.VID, D1.PID, i-1, null);
Console.WriteLine("Handle is {0}", D1.Handle);
int Error = MCP2210.M_Mcp2210_GetLastError();
Console.WriteLine("Response is {0} \r\n", Error);
switch (Error)
{
case -101:
{
Console.WriteLine("Error, No Such Index Exists");
break;
}
case -103:
{
Console.WriteLine("Error, Device Not Found");
break;
}
case -104:
{
Console.WriteLine("Error, Internal Buffer too small");
break;
}
case -105:
{
Console.WriteLine("Error, Open Device Error");
break;
}
case -106:
{
Console.WriteLine("Error, Device Already Connected");
confirmation = true;
break;
}
case -20:
{
Console.WriteLine("Error, MALLOC Unsucessful");
break;
}
case -10:
{
Console.WriteLine("Error, NULL Returned");
break;
}
case -1:
{
Console.WriteLine("Error, Unknown Error");
break;
}
case 0:
{
Console.WriteLine("Success");
confirmation = true;
break;
}
}
}
else if (i > 1)
{
Console.WriteLine("To Many Devices Connected. Only one is to be run at a time \r\n");
Console.Read();
}
else
{
Console.WriteLine("System does not register the device. Please connect the device");
}
return confirmation;
}
// public static SetUpDeviceSettings()
// {
// return;
// }
}
static class Converter
{
public static void CheckByteArray(byte[] bytes)
{
var convert = new StringBuilder("Is this command correct[] { ");
foreach (var b in bytes)
{
convert.Append(b + ", ");
}
convert.Append("} \r\n [y]es, [n]o or Escape to Exit \r\n");
Console.WriteLine(convert.ToString());
}
public static byte[] ToByteArray(String HexString)
{
int NumberChars = HexString.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2)
{
bytes[i / 2] = Convert.ToByte(HexString.Substring(i, 2), 16);
}
return bytes;
}
}
}
The third argument of the method ("M_Mcp2210_OpenByIndex") is the number MCP connected on your machine according to the dll doc (available at http://ww1.microchip.com/downloads/en/DeviceDoc/MCP2210_DLLv2.zip)
you can get this number with MCP2210.M_Mcp2210_GetConnectedDevCount(MCP2210_VID, MCP2210_PID)
try: MCP2210.M_Mcp2210_OpenByIndex(D1.VID, D1.PID, (uint) MCP2210.M_Mcp2210_GetConnectedDevCount(MCP2210_VID, MCP2210_PID), null)
OR
MCP2210.M_Mcp2210_OpenByIndex(D1.VID, D1.PID, (uint) 0, null)

How to show an informative real time progress data during long server process

I have a so long process may take 1 hour .
This process consists of many steps run from year to year .My main problem is :
How to provide an informative real time progress to the end user during the process not just a dummy loading bar.
int index = Convert.ToInt32(e.CommandArgument);
bool done = false;
int res = -1;
int fromVal = int.Parse(gv_balance.Rows[index].Cells[0].Text);
int toVal = int.Parse(gv_balance.Rows[index].Cells[1].Text);
int finMonth = 1;
int finYear = 0;
int EndServ = 0;
int calcYear = int.Parse(gv_balance.Rows[index].Cells[2].Text);
int total;
total = ((toVal - fromVal) + 1);
string msg = string.Empty;
int confirm = Balance.GetConfirmState(calcYear);
if (confirm == 0)
{
RadProgressContext progress = RadProgressContext.Current;
progress.Speed = "N/A";
finYear = fromVal;
for (int i = fromVal; i <= toVal; i++)
{
decimal ratio;
//Load New Employees
if (toVal - fromVal > 0)
{
ratio = ((decimal)toVal - i) / (toVal - fromVal) * 100;
}
else
{
ratio = ((decimal)toVal - i) / 1 * 100;
}
progress.PrimaryTotal = total;
progress.PrimaryValue = total;
progress.PrimaryPercent = 100;
progress.SecondaryTotal = 100; // total;
progress.SecondaryValue = ratio;//i ;
progress.SecondaryPercent = ratio; //i;
progress.CurrentOperationText = "Step " + i.ToString();
if (!Response.IsClientConnected)
{
//Cancel button was clicked or the browser was closed, so stop processing
break;
}
progress.TimeEstimated = (toVal - i) * 100;
//Stall the current thread for 0.1 seconds
System.Threading.Thread.Sleep(100);
EndServ = i + 1;
if (i == fromVal)
{
//--->STEP1
//Load intial data
int intial = Balance.PrepareIntialData(calcYear);
//--->STEP2
res = Balance.CalcEndServed(calcYear, EndServ - 1, 6, 30);
}
//--->STEP3
int newEmps = Balance.PrepareNewEmployees(calcYear, i);
for (int j = 0; j < 2; j++)
{
if (j == 0)
{
finMonth = 7;
finYear = i;
}
else
{
finMonth = 1;
finYear = i + 1;
}
//--->STEP4
int promotion1 = Balance.PreparePromotionFirst(finYear, finMonth, calcYear);
//--->STEP5
int promotion2 = Balance.PreparePromotionSecond(finYear, finMonth, calcYear);
//--->STEP6
int appointment1 = Balance.PrepareAppointmentFirst(finYear, finMonth, calcYear);
//--->STEP7
int appointment2 = Balance.PrepareAppointmentSecond(finYear, finMonth, calcYear);
//--->STEP8
int bonus = Balance.PrepareBonus(finMonth, finYear, 0, calcYear);
//--->STEP9
int salary = Balance.PrepareSalary(finYear, finMonth, calcYear);
(((CheckBox)gv_balance.Rows[index].Cells[3].FindControl("chk_redirect")).Checked == true)
{
//--->STEP9
int acco = Balance.PrepareFinanceAccount(finYear, finMonth, calcYear);
}
}
//--->STEP10
res = Balance.CalcEndServed(calcYear, EndServ, 6, 30);
Balance.CalcStudy(calcYear);
UpdateProgressContext();
if (res < 0)
{
success_lb.Visible = false;
error_lb.Visible = true;
error_lb.Text = "ERROR";
}
else
{
done = true;
success_lb.Visible = true;
error_lb.Visible = false;
success_lb.Text = "Success";
}
}
}
I want to show the current step for example :
(Promotion 1 ) in ---> 1-2018 and the percentage of the whole process beside the estimated time .
To report progress of a very long task using signalR you can do something like this (it's only an example to show how it can work):
Server part
We start by mapping SignalR.
public class Startup
{
public void Configuration(IAppBuilder app)
{
// Any connection or hub wire up and configuration should go here
app.MapSignalR();
}
}
We create a hub class (don't forget to install the signalr package):
(If you want to report progress to all connected users or a specific group of users take a look here : http://www.asp.net/signalr/overview/guide-to-the-api/working-with-groups)
In the given example it report progress only to the caller of the Start function.
public class MyHub : Hub
{
public void Start(string arg)
{
Task.Run(() =>
{
AVeryLongTask();
});
}
//simulate a long task
void AVeryLongTask()
{
for (int i = 0; i < 10000; i++)
{
Thread.Sleep(100);
Clients.Caller.ReportProgress("AVeryLongTask", i * 100 / 10000);
}
}
}
Client Part
In the html you must add these references :
<!--Script references. -->
<!--Reference the jQuery library. -->
<script src="Scripts/jquery-1.6.4.min.js"></script>
<!--Reference the SignalR library. -->
<script src="/Scripts/jquery.signalR-2.0.0.js"></script>
<!--Reference the autogenerated SignalR hub script. -->
<script src="/signalr/hubs"></script>
and now the Js part to get the progress from the hub :
$(function() {
// Declare a proxy to reference the hub.
var hub = $.connection.myHub;
// Create a function that the hub can call to report progress.
hub.client.reportProgress = function(functionName, progress) {
$('#progression').append('<li><strong>' + progress + '</strong>: ' + functionName + '</li>');
};
// Start the connection.
$.connection.hub.start().done(function() {
$('#startlongprocess').click(function() {
//start the long process
hub.server.start("arg");
alert("started");
});
});
});
The html container for the progress and the start button :
<div class="container">
<input type="button" id="startlongprocess" value="Send" />
<ul id="progression"></ul>
</div>
If you need more explanations don't hesitate to ask.
( My example is based on this one http://www.asp.net/signalr/overview/getting-started/tutorial-getting-started-with-signalr from signalr team )
You can use web sockets to push progress updates down to the client. SignalR is a dotnet library that wraps websockets and falls back where websockets isn't available. There are comprehensive examples online showing how to implement progress reporting using SignalR so no need to repeat them. Have a look here:
https://github.com/dragouf/SignalR.Progress
or here:
https://www.safaribooksonline.com/blog/2014/02/06/server-side-signalr/
for examples.
Here is a simplification of your problem that I think will solve your problem. If you run your long operation in a task, you can update the application with a status object. If your application is WPF, and you bind the status, it will update automatically. In WinForms, you can bind or just implement the event handler.
void Main()
{
var status = new Status();
object locker = new object();
status.PropertyChanged += Status_PropertyChanged;
//
// Long running job in a task
//
var task = new Task((s) =>
{
for(int i = 0; i < 1000; i++)
{
Task.Delay(TimeSpan.FromSeconds(5)).Wait();
//Thread.Sleep(5000);
lock (locker)
{
status.Message = string.Format("Iteration: {0}", i);
}
}
}, status);
//
// start and wait for the task to complete. In a real application, you may end differently
task.Start();
task.Wait();
}
static void Status_PropertyChanged(object sender,
PropertyChangedEventArgs e)
{
var s = sender as Status;
if(s != null && string.Equals(e.PropertyName, "Message"))
{
Console.WriteLine( s.Message);
}
}
public class Status : PropertyNotifier
{
private string _Message = string.Empty;
public string Message
{
get { return _Message; }
set { SetField(ref _Message, value); }
}
}
public abstract class PropertyNotifier : INotifyPropertyChanged, INotifyPropertyChanging
{
public event PropertyChangingEventHandler PropertyChanging;
public event PropertyChangedEventHandler PropertyChanged; // ? = new Delegate{};
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanging(string propertyName)
{
PropertyChangingEventHandler handler = PropertyChanging;
if (handler != null) handler(this, new PropertyChangingEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
OnPropertyChanging(propertyName);
field = value;
OnPropertyChanged(propertyName);
return true;
}
}

Slow updates with Entity Framework 4.3 and SQL Server Compact Edition 3.5

I am developing a WPF application that uses a SQL Server Compact Edition database and Entity Framework, but I get really slow performance of updates and deletes. So I created a simple Foobar console application (below) and got the same issue.
I understand that Entity Framework creates some overhead and that SQL Server Compact Edition is slower than an regular SQL Server. But is it this much difference between a INSERT and an UPDATE/DELETE?
Main
public static void CreateFoobars()
{
DateTime start = DateTime.Now;
for (int i = 0; i < 10000; i++)
{
Foobar foobar = new Foobar();
foobar.FoobarID = i;
foobar.Column1 = "Column1";
foobar.Column2 = "Column2";
foobar.Column3 = "Column3";
foobar.Column4 = "Column4";
_localRepository.CreateFoobar(foobar);
}
bool result = _localRepository.Save();
TimeSpan timeSpan = CalculateTimeSpan(start, DateTime.Now);
Console.WriteLine("Created Foobars? {0}, Time: {1}", result, timeSpan);
}
public static void CountFoobars()
{
Console.WriteLine("Count Foobars: {0}", _localRepository.GetAllFoobars().Count());
}
public static void UpdateFoobars()
{
DateTime start = DateTime.Now;
for (int i = 0; i < 10000; i++)
{
Foobar foobar = new Foobar();
foobar.FoobarID = i;
foobar.Column1 = "Column11";
foobar.Column2 = "Column22";
foobar.Column3 = "Column33";
foobar.Column4 = "Column44";
_localRepository.UpdateFoobar(foobar);
}
bool result = _localRepository.Save();
TimeSpan timeSpan = CalculateTimeSpan(start, DateTime.Now);
Console.WriteLine("Updated Foobars? {0}, Time: {1}", result, timeSpan);
}
public static void DeleteFoobars()
{
DateTime start = DateTime.Now;
for (int i = 0; i < 10000; i++)
{
_localRepository.DeleteFoobar(i);
}
bool result = _localRepository.Save();
TimeSpan timeSpan = CalculateTimeSpan(start, DateTime.Now);
Console.WriteLine("Deleted Foobars? {0}, Time: {1}", result, timeSpan);
}
Repository
public IQueryable<Foobar> GetAllFoobars()
{
return _entities.Foobars;
}
public Foobar GetFoobar(int foobarID)
{
return _entities.Foobars.ByFoobarID(foobarID).FirstOrDefault();
}
public bool CreateFoobar(Foobar foobar)
{
try
{
_entities.AddToFoobars(foobar);
}
catch (Exception e)
{
}
return false;
}
public bool UpdateFoobar(Foobar foobar)
{
try
{
Foobar f = this.GetFoobar(foobar.FoobarID);
if (f != null)
{
f.Column1 = foobar.Column1;
f.Column2 = foobar.Column2;
f.Column3 = foobar.Column3;
f.Column4 = foobar.Column4;
return true;
}
}
catch (Exception e)
{
}
return false;
}
public bool DeleteFoobar(int foobarID)
{
try
{
Foobar f = this.GetFoobar(foobarID);
if (f != null)
{
_entities.DeleteObject(f);
return true;
}
}
catch (Exception e)
{
}
return false;
}
public bool Save()
{
try
{
this.Context.SaveChanges();
return true;
}
catch (Exception e)
{
}
return false;
}
Results (on a i7 with SSD and 16 RAM, almost doubled time on a older C2D)
Count Foobars: 0
Created Foobars? True, Time: 00:00:04:0700057
Count Foobars: 10000
Update Foobars? True, Time: 00:00:59:8800838
Count Foobars: 10000
Deleted Foobars? True, Time: 00:00:57:8000810
Count Foobars: 0
Tested
LazyLoading = false
Disabling identities in database.
I suspect that your create function faster because it is just creating 10000 local objects and then committing them all in one step to the database, while your delete and update functions all query one item from the database before changing it.

Categories