Let say you have to write a code for the periodic payment application, and in your database you store the payment start date. After that you want to check if the next payment is within +/- 1 day of the current date - do something. And obviously, you font want to write a lot of code to do that :)
The first idea I had was to create a whole bunch of unions for each day and then check if current date is one of those, but later I shrinked that to 3 conditions connected with 'or' (here is NHibernate function for that)
var settlementDateDifferenceFromNow = Projections.SqlFunction(
new SQLFunctionTemplate(
NHibernateUtil.Int32,
@"
CASE WHEN
ABS(DATEDIFF(DAY, GETDATE(), DATEADD(MONTH, DATEDIFF(MONTH, ?1, GETDATE()) - 1, ?1))) <= 1 OR
ABS(DATEDIFF(DAY, GETDATE(), DATEADD(MONTH, DATEDIFF(MONTH, ?1, GETDATE()), ?1))) <= 1 OR
ABS(DATEDIFF(DAY, GETDATE(), DATEADD(MONTH, DATEDIFF(MONTH, ?1, GETDATE()) + 1, ?1))) <= 1
THEN 1
ELSE 0
END
"),
NHibernateUtil.Int32,
Projections.Property<Contract>(x => x.SettlementDate_Actual)
);
I am not sure about 'or' conjunction tho... would it cause performance problems?
Imagine, you have to implement batching for a time consuming operation using NHibernate. I would write it like this:
var items = new List<Item>();
for (var page = 0; page == 0 || items.Any(); page++)
{
items = GetListOfItems(page);
foreach (var items in items)
{
DoStuff(item);
}
}
public void GetListOfItems(int page)
{
return CurrentSession.QueryOver<Item>()
.Select(Projections.Id())
.Take(PageSize)
.Skip(PageSize * page)
.List();
}
But what I found is that if the DuStuff is time consuming then it is possible to have duplicated items from the query. Thatis because of NHibernate generated query:
SELECT TOP (@p0) y0_
FROM (
SELECT this_.Id AS y0_
,ROW_NUMBER() OVER (
ORDER BY CURRENT_TIMESTAMP
) AS __hibernate_sort_row
FROM contact.sfContact this_
) AS query
WHERE query.__hibernate_sort_row > @p1
ORDER BY query.__hibernate_sort_row;
That order by CURRENT_TIMESTAMP... If you want consistent results you would have to apply specific ordering:
I think of myself as "open for opportunities" professional. I have a great job that I like but I dont mind to try something new. Recently I found a really interesting puzzle - part of the employment process. That was a task to recreate a web service by its interface so the checking bot could access it and try its tests.
Sounds fun so I decided to try. The hardest part of that process was to understand what is the expected behavior of that service just by methods names. And take into account all tricks from the employer side (like overflow exceptions, impossible combinations of parameters etc.) I`ve solved that puzzle to 90% and that was enough to pass to the real interview. At that point I`ve decided to try the interview as well - just in case :). Unfortunately, I failed and here is my thoughts about the interview.
That was completely my mistake. I have not really prepared myself to the interview and have not refreshed in my memory the most common questions. I thought that I dont need anything to pass.
Here is the list of some of those questions from the interview with my comments - maybe it would help someone (and all of them about c#).
Difference between value type and reference type
hopefully everyone can explain it from the top of their heads).
Boxing vs Unboxing ()
I`ve made my first mistake here as I could not remember what is what...
Is string a value type or reference type?
Just be careful here as well as string is a special case. I`ve remembered it but not sure what I`ve answered :)
What is sealed keyword? Can we derive from sealed class?
some questions about garbage collector
Cant remember those questions, they were like when collector does its work by default, how to suspend it etc. I dont know a lot about it as that's not a day-to-day stuff. Possibly mine knowledge of GC.Collect and that that is not a best line of code to include just not enough :)
SOLID principles
simply name them
Describe Strategy pattern; Singleton pattern
once again, describe them in your own words
What are 4 types of design patterns
I could not remember them, but the common sense is your friend - some of them are aimed at creation of objects, other about interactions and so on.
Session variables in Asp.Net
that was a question about ViewBag, ViewData and TempData. I could not remember them (and the difference between them) as I just so get used to models. Unfortunately, I have not described my thoughts on Models either - so that is my big mistake...
Authorization in Asp.Net
I started talking about security tokens and roles, but the question was only about [Authorize(Roles="...")] attribute. And I forgot to mention it :(
Can 2 different routes lead to the same action in Asp.Net?
Yes - but it would be better to answer with example and I just said yes.
Difference between Shared views and Partial Views
Own words explanation
Difference between WebForms and MVC
I`ve told about postbacks and stateless MVC controllers, probably that was not what interviewer was expecting...
Is doctype needed for HTML5?
I`ve said that no (from the personal experience) but accordingly to the documentation - the answer is yes...
What is the difference between Data Contract and Operational Contract in WCF
I`ve answered honestly that I dont really know WCF :( And by the way, in the job ad there was no mention of WCF
Can WebAPI method return ActionResult?
I`ve answered that yes (especially as I can wrap it as I want) but the proper answer was no
Lifetime management of dependencies in IoC framework
What will I do and how would I approach bug fixing at some client as a software development consultant?
How can I monitor problems in production?
What is TDD, BDD, DDD, Kanban?
After about hour of talking on those questions I was given a small tax calculator application with bugs and txt file with tests. My task was to fix as many bugs as possible and the interviewer was looking at my screen with Skype shared desktop. I`ve fixed some of those tests (not all of them) and after another 30 minutes interviewer asked me to show only the first test - and that was all.
And finally, on the next day or so I`ve received the email with "sorry you failed.". Not a big deal :)
Today I edited a question on stackoverflow about url shortening. Looks like it was about Json serialization, and here is my solution:
using (var client = new HttpClient())
{
var content = new StringContent(
"{\"longUrl\": \"http://www.google.com/\"}",
Encoding.UTF8,
"application/json");
var response = await client.PostAsync("https://www.googleapis.com/urlshortener/v1/url", content);
var responseString = await response.Content.ReadAsStringAsync();
var data = JsonConvert.DeserializeObject(responseString);
var id = data.Id;
}
And a class for urls:
public class ShortenUrl
{
public string Kind { get; set; }
public string Id { get; set; }
public string LongUrl { get; set; }
}
So you would have to use Newtonsoft json serializer to deserialize the json string; and HttpClient to communicate with the API
Thanks to my company, stratton, I was able to attend the YOW! conference in Melbourne. It was a 2-day software conference and it was amazing :). I really liked everything, especially as it was my first big conference.
For now I want to leave some notes about sessions I visited.
Day 1:
1) User story mapping: Discover the whole story by Jeff Patton. It was good especially as Jeff is a great presenter. But the content was a little bit childish - too easy and nothing special.
2) Make impacts, not software by Gojko Adzic. Second session was not very deep as well and I suppose nobody would expect to get some out-of-the box solutions; so from that session I was able to get few ideas at least like how to piss of scrum masters :) Or why we don't actually need all that test coverage.
3) Cool things about D - why and how we use it at Facebook by Andrei Alexandrescu. Finally that was a great speech. Andrei not only covered the simple usage of D (in a great way! so I started to plan when I will download its compiler) but as well talked about purity and some general programming concepts. It was useful and interesting. I would say that it was one of the best sessions from today.
4) Groovy: the awesome parts by Paul King. I wanted to get into groovy a little and the session started with an idea that groovy is the same thing for java as D for C++ but, unfortunately, this topic was not of the same quality as previous one. Frankly, it was just boring... Basic examples and no real value. At least for me.
5) Programming in the large: Architecture and experimentation by Mark Hibberd. Mark did a great job of general description of the general programming myths with a real examples of what his team doing. He even admitted that they could do it better! It was fun to listen to him and it was really motivational. Once again - it made me think of what and how I would do in the future. Thank you Mark.
6) Functionally obvious and succinct by Edward Kmett. This session was hard. Really hard. First of all - I had never seen haskel code before and secondly - it was all about optimization so a lot of "o(log(n))". And during the session I almost constantly had a question "wat" in my head. But later on the way home I rethought what I heard and it comes that this topic is interesting for me and I would like to try to implement the same data structure with C# (and will post about it soon).
Day 2:
1) Reactive, message driven and scalable by Todd L. Montgomery. Todd was talking about the past, present and future of the http protocol. Interesting topic but it was pretty much summarized by his own words that it is likely that most deevlopers wont notice any difference - it is too low level.
2) The scaling dilemma by Mary Poppendieck. Another session about the "agile way". This time on how to scale the agile approach for the enterprise level. Frankly, just another agile talk - nothing new. Maybe those agile coaches just aiming for a real general things?
3) How we went from 1 million to 1 billion events without throwing everything away by Julian Giuca. That was just boring - Julian was speaking about their approach to frameworks, made a lot of examples and he is a good speaker. But the topic was just bad, nothing specific and nothing useful - just a bunch of general ideas that everyone can produce.
4) How to undo almost anything with Git by Peter Bell. Finally, that was the only session on that conference (for me) where the speaker was not only showing slides but actually type something. During that speech I was enjoying the real console and actual usage of the Git. That was really fun!
5) Agility at the essence of software architecture by Simon Brown. Luckily, that session was not a common agile talk. From that one I was able to catch some new ideas and approaches, I am very pleased that I have seen it. Sketching is great :)
6) Pippi's book of the dead trading cards by Elizabethe Kramer. Well, the last session of the conference was the worst one. It was more about psychology then development and it was really bad. Elizabeth told us how she was able to settle one unpleasant situation for one company. But why I would like to know it - I don't know :)
So that was all. Unfortunately, I was not able to network at all, my social skills need a lot of improvements!
Yesterday I found annoying flaw in Rhino mocks. I expected that when you said Repeat.Twice() the expectation is that the method would be executed exactly 2 times and if no - expectation should be failed. But looks like that it saying "at least twice". (Despite the fact that there is as well Repeat.AtLeastOnce())
So here is my setup:
public class Foo
{
public virtual void DoStuff()
{
Console.WriteLine("DoStuff");
}
}
public class Bar
{
public Foo foo { get; set; }
public void CallFoo(int max)
{
for (int i = 0; i < max; i++)
{
foo.DoStuff();
}
}
}
And that test is not failing unfortunately. You can even replace Times(2) with anything else - just the number of executions should be less then the actual number - test still would be green. And you can replace it with Once() or AtLeastOnce() - no difference at all!
[Test]
public void MockTest()
{
var mock = MockRepository.GenerateMock();
mock.Expect(x => x.DoStuff())
.Repeat.Times(2);
var bar = new Bar();
bar.foo = mock;
bar.CallFoo(3);
mock.VerifyAllExpectations();
}
And here is how you can write that test to fail. Finally, checking for exact number of calls!
[Test]
public void MockTest2()
{
var mock = MockRepository.GenerateMock();
var bar = new Bar();
bar.foo = mock;
bar.CallFoo(3);
mock.AssertWasCalled(x => x.DoStuff(),
y => y.Repeat.Times(2));
}
I`ve promised to write about my implementation of the async file uploader for the web forms application. The task was to implement it as a server control that can be added and reused on different pages (actually, in order to replace the old teleric control).
I decided to use this plugin. And the only problem was with a url to upload the file. As it should be available from the different pages I could not use the [WebMethod] so I`ve implemented a http handler to upload the file. Plus few additional changes to the client side - show the amount of files, add an option to delete uploaded file etc.
Here is my implementation:
public class AsyncUploader : CompositeControl
{
private Panel _container;
private Panel _uploadedFilesContainer;
private HtmlInputFile _uploader;
public string[] AllowedFileExtensions { get; set; }
///
/// Maximum file size in bytes
///
public int MaxFileSize { get; set; }
///
/// Gets or sets the virtual path of the folder, where RadUpload will automatically save the valid files after the upload completes.
/// Note that existing files with the same name will be overwritten. As such it is best to append a unique identifier to the folder.
///
public string TargetPhysicalFolder { get; set; }
public int MaxFilesCount { get; set; }
protected string EscapedUniqueId { get { return Regex.Replace(UniqueID, "[$.\\s]", "_"); } }
public List Files
{
get
{
EnsureChildControls();
var targetFolder = TargetPhysicalFolder;
var result = new List();
foreach (var key in Page.Request.Form.AllKeys)
{
if (key.StartsWith(EscapedUniqueId + "savedFile_"))
{
var index = key.Replace(EscapedUniqueId + "savedFile_", "");
var savedFileName = Page.Request.Form[key];
result.Add(new UploadedFile
{
OriginalFileName = Page.Request.Form[EscapedUniqueId + "originalFile_" + index],
SavedFileFullPath = Path.Combine(targetFolder, savedFileName),
SavedFileName = savedFileName
});
}
}
return result;
}
}
protected override void OnPreRender(EventArgs e)
{
if (_container.ClientIDMode != ClientIDMode.Static) throw new ArgumentException("ClientIDMode");
var uniqueId = EscapedUniqueId;
var options = new JavaScriptSerializer().Serialize(new
{
uniqueID = uniqueId,
elementSelector = "#" + uniqueId,
sessionId = Page.Session.SessionID,
maxFiles = MaxFilesCount,
maxFileSize = MaxFileSize,
extensions = AllowedFileExtensions != null && AllowedFileExtensions.Any()
? AllowedFileExtensions.ToDelimitedString("|")
: "*",
submitButtonsSelector = _submitButtonsToDisable
.Select(x => "#" + x.ClientID)
.ToDelimitedString(", ")
});
Page.Session[uniqueId + "_TargetFolder"] = TargetPhysicalFolder;
var setUp = "".F(
options);
Page.ClientScript.RegisterClientScriptBlock(GetType(), "setup" + uniqueId, setUp);
base.OnPreRender(e);
}
protected override void CreateChildControls()
{
_container = new Panel();
_container.ClientIDMode = ClientIDMode.Static;
_container.ID = EscapedUniqueId;
_uploader = new HtmlInputFile();
_uploader.Attributes.Add("class", "filePicker");
if (MaxFilesCount > 1)
_uploader.Attributes.Add("multiple", "true");
_uploadedFilesContainer = new Panel();
_uploadedFilesContainer.Attributes.Add("class", "uploadedFiles");
_uploadedFilesContainer.ID = RandomString.Generate(20);
var errorsContainer = new Panel();
errorsContainer.Attributes.Add("class", "uploadErrors");
_container.Controls.Add(_uploader);
_container.Controls.Add(_uploadedFilesContainer);
_container.Controls.Add(errorsContainer);
this.Controls.Add(_container);
base.CreateChildControls();
}
protected override void OnLoad(EventArgs e)
{
//Register resources (here I`ve used Peter Blum)
ClientScriptLibrary
.RegisterEmbeddedResource(
typeof(AsyncUploader),
"jquery.ui.widget.js",
ClientDependencyType.Javascript);
ClientScriptLibrary
.RegisterEmbeddedResource(
typeof(AsyncUploader),
"jquery.fileupload.js",
ClientDependencyType.Javascript);
ClientScriptLibrary
.RegisterEmbeddedResource(
typeof(AsyncUploader),
"AsyncUploadFormItem.js",
ClientDependencyType.Javascript);
ClientScriptLibrary
.RegisterEmbeddedResource(
typeof(AsyncUploader),
"AsyncUploadFormItem.css",
ClientDependencyType.CSS);
base.OnLoad(e);
}
}
public class UploadedFile
{
public string OriginalFileName { get; set; }
public string SavedFileName { get; set; }
public string SavedFileFullPath { get; set; }
}
var setupfileUpload = function (options) {
var $filePicker = $(options.elementSelector + ' .filePicker');
var $uploadedFiles = $(options.elementSelector + ' .uploadedFiles');
var $uploadErrors = $(options.elementSelector + ' .uploadErrors');
$filePicker.fileupload({
url: 'fileUpload.axd',
dropZone: $filePicker,
add: function (e, data) {
cleanExceptionsPanel();
var count = $uploadedFiles.find('.sentFile').length;
//client validation by file size and file type
if (!isUploadLimit(options.maxFiles, count) ||
!isFileValid(data.files[0].size, data.files[0].name)) return;
//to support duplicated files the div id should be unique for different files - and that Id should be passed to the handler.
var divId = 'id' + (new Date()).getTime();
//submit the form with 2 additional parameters - where to save and file id
data.formData = { targetFolder: options.targetFolder, fileId: divId, sessionId: options.sessionId, controlid: options.uniqueID };
var jqXHR = data.submit();
//append a div containing inputs for a given file
var div = $('
');
div.append('
');
div.append('' + data.files[0].name + '');
var cancelButton = $('x');
cancelButton.on('click', function () { //delete uploaded file and/or cancel the upload process
cleanExceptionsPanel();
jqXHR.abort();
var savedFile = $(this).parent().find('.savedFile').val();
if (savedFile)
$.post('fileUpload.axd', { fileName: savedFile, deleteRequest: true, sessionId: options.sessionId, controlid: options.uniqueID });
$(this).parent().remove();
});
div.append(cancelButton);
div.append('');
$uploadedFiles.append(div);
},
done: function (e, data) {
var res = jQuery.parseJSON(data.result);
var div = $uploadedFiles.find('#' + res.FileId);
//remove the progress bar and insert a 'complete' dot instead
div.find('.progressbar').remove();
div.prepend('
');
var count = div.data('filenumber');
div.append('');
//manually hide the validation error
var validationError = $(options.elementSelector + ' span.errors .errorMessage');
validationError.css('visibility', 'hidden');
validationError.css('display', 'none');
},
progress: function (e, data) {
var p = parseInt(data.loaded / data.total * 100, 10);
if (typeof p === 'number') {
var div = $uploadedFiles.find('#' + data.formData.fileId);
var progress = div.find('.progress');
progress.css('width', p + '%');
}
},
fail: function (e, data) {
var div = $uploadedFiles.find('#' + data.formData.fileId);
//remove the progress bar and insert a 'fail' dot instead
div.find('.progressbar').remove();
div.prepend('
');
}
});
var cleanExceptionsPanel = function() {
$uploadErrors.html('');
}
var isUploadLimit = function(maxCount, currentCount) {
if (maxCount <= 0) return true;
if (maxCount == 1) { // replace existing file
$uploadedFiles.find('.deleteUploadedFile').each(function() { $(this).click(); });
}
else if (maxCount <= currentCount) {
$uploadErrors.append('Maximum number of files is attached');
return false;
}
return true;
};
var isFileValid = function (filesize, filename) {
if (filesize > options.maxFileSize) {
$uploadErrors.append('File is too large to be uploaded');
return false;
}
var pattern = '.+?\.(' + options.extensions + ')';
if (!filename.match(new RegExp(pattern, 'i'))) {
$uploadErrors.append('File type is not supported and cannot be uploaded');
return false;
}
return true;
};
var escapeFileName = function (fileName) {
return fileName.replace(/ |\.|#/g, '_');
}
if (options.submitButtonsSelector) {
$(options.submitButtonsSelector)
.prop('disabled', false)
.each(function() {
{
$(this).attr('title', $(this).attr('data-oldtitle'));
}
});
}
};
}
public class FileUploadHandler : IHttpHandler, IReadOnlySessionState
{
private static readonly Logger _log = LogManager.GetCurrentClassLogger();
private void DeleteFile(HttpContext context)
{
// Make sure to sanitise the filename by calling Path.GetFileName. This will
// prevent deletions from folders other than the target folder (which is known only
// by the server)
var filename = Path.GetFileName(context.Request.Form["filename"]);
var controlId = context.Request.Form["controlId"];
var path = context.Session[controlId + "_TargetFolder"].ToString();
var targetFilename = Path.Combine(path, filename);
if (File.Exists(targetFilename))
File.Delete(targetFilename);
}
private void UploadFile(HttpContext context)
{
Parse(context.Request.InputStream, Encoding.UTF8);
var controlId = context.Request.Form["controlId"];
var fileId = context.Request.Form["fileId"];
var path = context.Session[controlId + "_TargetFolder"].ToString();
var targetFolder = Directory.CreateDirectory(path).FullName;
var targetFilename = Path.Combine(targetFolder, _filename);
// Handle existing files by incrementing counter
int counter = 1;
while (File.Exists(targetFilename))
{
counter++;
targetFilename = Path.Combine(targetFolder,
Path.GetFileNameWithoutExtension(_filename) + counter + Path.GetExtension(_filename));
}
using (var file = File.Create(targetFilename))
{
file.Write(_fileContents, 0, _fileContents.Length);
}
context.Response.Write(new JavaScriptSerializer()
.Serialize(new
{
OriginalFile = _filename,
SavedFile = Path.GetFileName(targetFilename),
FileId = fileId
}));
}
public void ProcessRequest(HttpContext context)
{
var sessionId = context.Request.Form["sessionId"];
if (context.Session == null || context.Session.SessionID != sessionId)
throw new InvalidOperationException("Wrong session state during the file upload operation");
if (context.Request.Form["deleteRequest"] != null)
{
DeleteFile(context);
}
else
{
UploadFile(context);
}
}
private byte[] ToByteArray(Stream stream)
{
byte[] buffer = new byte[32768];
using (MemoryStream ms = new MemoryStream())
{
while (true)
{
int read = stream.Read(buffer, 0, buffer.Length);
if (read <= 0)
return ms.ToArray();
ms.Write(buffer, 0, read);
}
}
}
private int IndexOf(byte[] searchWithin, byte[] serachFor, int startIndex)
{
int index = 0;
int startPos = Array.IndexOf(searchWithin, serachFor[0], startIndex);
if (startPos != -1)
{
while ((startPos + index) < searchWithin.Length)
{
if (searchWithin[startPos + index] == serachFor[index])
{
index++;
if (index == serachFor.Length)
{
return startPos;
}
}
else
{
startPos = Array.IndexOf(searchWithin, serachFor[0], startPos + index);
if (startPos == -1)
{
return -1;
}
index = 0;
}
}
}
return -1;
}
private void Parse(Stream stream, Encoding encoding)
{
// Read the stream into a byte array
byte[] data = ToByteArray(stream);
// Copy to a string for header parsing
string content = encoding.GetString(data);
// The first line should contain the delimiter
int delimiterEndIndex = content.IndexOf("\r\n");
if (delimiterEndIndex > -1)
{
string delimiter = content.Substring(0, content.IndexOf("\r\n"));
// Look for Content-Type
Regex re = new Regex(@"(?<=Content\-Type:)(.*?)(?=\r\n\r\n)");
Match contentTypeMatch = re.Match(content);
// Look for filename
re = new Regex(@"(?<=filename\=\"")(.*?)(?=\"")");
Match filenameMatch = re.Match(content);
// Did we find the required values?
if (contentTypeMatch.Success && filenameMatch.Success)
{
// Set properties
this._contentType = contentTypeMatch.Value.Trim();
this._filename = filenameMatch.Value.Trim();
// Get the start & end indexes of the file contents
int startIndex = contentTypeMatch.Index + contentTypeMatch.Length + "\r\n\r\n".Length;
byte[] delimiterBytes = encoding.GetBytes("\r\n" + delimiter);
int endIndex = IndexOf(data, delimiterBytes, startIndex);
int contentLength = endIndex - startIndex;
// Extract the file contents from the byte array
byte[] fileData = new byte[contentLength];
Buffer.BlockCopy(data, startIndex, fileData, 0, contentLength);
this._fileContents = fileData;
}
}
}
private string _contentType;
public string _filename;
public byte[] _fileContents;
public bool IsReusable { get { return false; } }
}
Here I`ve used the Session to store the target folder for files as this is safer. Hope this will help someone :)