Thursday, August 4, 2016

Using Entity Framework 6 and MySQL on ASP.NET 5

Taken from:

http://dan.cx/2015/08/entity-framework-6-mysql-aspnet


Visual Studio 2015 was recently released, and with it came a newer beta of ASP.NET 5 (formerly referred to as "ASP.NET vNext"). ASP.NET 5 is a complete rewrite of ASP.NET, focusing on being lightweight, composible, and cross-platform. It also includes an alpha version of Entity Framework 7. However, EF7 is not yet production-ready and does not support all features of EF6. One feature that is missing from EF6 is support for other database providers - Only SQL Server and SQLite are supported at this time.
I wanted to transition a site over to ASP.NET 5, but needed to continue using MySQL as a data source. This meant getting Entity Framework 6 running on ASP.NET 5, which is pretty much undocumented right now. All the documentation and tutorials for EF6 heavily relies on configuration in Web.config, which no longer exists in ASP.NET 5. In this post I'll discuss the steps I needed to take to get it running. An example project containing all the code in this post can be found at https://github.com/Daniel15/EFExample.
Since EF6 does not support .NET Core, we need to remove .NET Core support (delete "dnxcore50": { } from project.json). Once that's done, install the EntityFramework and MySql.Data.Entity packages, and add references to System.Data and System.Configuration. For this post, I'll be using this basic model and DbContext, and assume you've already created your database in MySQL:
1
2
3
4
5
6
7
8
9
10
11
12
public class MyContext : DbContext
{
    public virtual DbSet<Post> Posts { get; set; }
}
  
public class Post
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
}
Entity Framework 6 relies on the provider and connection string being configured in Web.config. Since Web.config is no longer used with ASP.NET 5, we need to use code-based configuration to configure it instead. To do so, create a new class that inherits from DbConfiguration:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class MyDbConfiguration : DbConfiguration
{
    public MyDbConfiguration()
    {
        // Attempt to register ADO.NET provider
        try {
            var dataSet = (DataSet)ConfigurationManager.GetSection("system.data");
            dataSet.Tables[0].Rows.Add(
                "MySQL Data Provider",
                ".Net Framework Data Provider for MySQL",
                "MySql.Data.MySqlClient",
                typeof(MySqlClientFactory).AssemblyQualifiedName
            );
        }
        catch (ConstraintException)
        {
            // MySQL provider is already installed, just ignore the exception
        }
  
        // Register Entity Framework provider
        SetProviderServices("MySql.Data.MySqlClient", new MySqlProviderServices());
        SetDefaultConnectionFactory(new MySqlConnectionFactory());
    }
}
The first part of the configuration is a hack to register the ADO.NET provider at runtime, by dynamically adding a new configuration entry to the system.data section. The second part registers the Entity Framework provider. We also need to modify the configuration file to include the connection string. You can use any configuration provider supported by ASP.NET 5, I'm using config.json here because it's the default provider.
1
2
3
4
5
6
7
8
{
  "Data": {
    "DefaultConnection": {
      "ConnectionString": "Server=localhost; Database=test; Uid=vmdev; Pwd=password;"
    }
  }
}
Now that we have the configuration, we need to modify the context to use it:
1
2
3
4
5
6
7
8
9
10
[DbConfigurationType(typeof(MyDbConfiguration))]
public class MyContext : DbContext
{
    public MyContext(IConfiguration config)
        : base(config.Get("Data:DefaultConnection:ConnectionString"))
    {
    }
    // ...
}
An instance of IConfiguration will be automatically passed in by ASP.NET 5's dependency injection system. The final step is to register MyContext in the dependency injection container, which is done in your Startup.cs file:
1
2
3
4
5
6
public void ConfigureServices(IServiceCollection services)
{
    // ...
    services.AddScoped<MyContext>();
}
AddScoped specifies that one context should be created per request, and the context will automatically be disposed once the request ends. Now that all the configuration is complete, we can use MyContext like we normally would:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class HomeController : Controller
{
    private readonly MyContext _context;
  
    public HomeController(MyContext context)
    {
        _context = context;
    }
  
    public IActionResult Index()
    {
        return View(_context.Posts);
    }
}

Thursday, May 19, 2016

HTTP 206 Partial Content In ASP.NET Web API - Video File Streaming

http://www.codeproject.com/Articles/820146/HTTP-Partial-Content-In-ASP-NET-Web-API-Video

HTTP 206 Partial Content In ASP.NET Web API - Video File Streaming


, 25 Nov 2014 CPOL
A step-by-step walkthrough of implementing the HTTP 206 Partial Content.

Table of content

Introduction

This article focuses on the implementation of HTTP 206 Partial Content in ASP.NET Web API. I would like to describe how I work on it with ApiController and deal with some potential performance issues. Our goal is to create a video file streaming service and an HTML5 page to play them.

Background

In my last article, We have discussed the characteristic of HTTP 206 and its related headers. Also we had a showcase of video streaming in Node.js and HTML5. This time we will move to ASP.NET Web API and will have some discussions regarding our implementation. If you would like to learn more details of this HTTP status code, last article could be a good reference to you. And we will not repeat it in this article.

Prerequisites

Get started to implement

First of all, we expect the URL for video streaming shall be like this:
http://localhost/movie/api/media/play?f=praise-our-lord.mp4
where movie is our application name in IIS, media is the controller name, play is its action name and parameter frepresents the video file we would like to play.
Based on this URL, we will start from the MediaController under the namespace Movie.Controllers, a class derived from ApiController. Before we work on its actual action, we need several static fields and methods to help us in upcoming steps.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Net.Mime;
using System.Web.Configuration;
using System.Web.Http;

namespace Movie.Controllers
{
    public class MediaController : ApiController
    {
        #region Fields

        // This will be used in copying input stream to output stream.
        public const int ReadStreamBufferSize = 1024 * 1024;
        // We have a read-only dictionary for mapping file extensions and MIME names. 
        public static readonly IReadOnlyDictionary<string, string> MimeNames;
        // We will discuss this later.
        public static readonly IReadOnlyCollection<char> InvalidFileNameChars;
        // Where are your videos located at? Change the value to any folder you want.
        public static readonly string InitialDirectory;

        #endregion

        #region Constructors

        static MediaController()
        {
            var mimeNames = new Dictionary<string, string>();
   
            mimeNames.Add(".mp3", "audio/mpeg");    // List all supported media types; 
            mimeNames.Add(".mp4", "video/mp4");
            mimeNames.Add(".ogg", "application/ogg");
            mimeNames.Add(".ogv", "video/ogg");
            mimeNames.Add(".oga", "audio/ogg");
            mimeNames.Add(".wav", "audio/x-wav");
            mimeNames.Add(".webm", "video/webm");

            MimeNames = new ReadOnlyDictionary<string, string>(mimeNames);

            InvalidFileNameChars = Array.AsReadOnly(Path.GetInvalidFileNameChars());
            InitialDirectory = WebConfigurationManager.AppSettings["InitialDirectory"];
        }

        #endregion

        #region Actions

        // Later we will do something around here.

        #endregion

        #region Others

        private static bool AnyInvalidFileNameChars(string fileName)
        {
            return InvalidFileNameChars.Intersect(fileName).Any(); 
        }

        private static MediaTypeHeaderValue GetMimeNameFromExt(string ext)
        {
            string value;

            if (MimeNames.TryGetValue(ext.ToLowerInvariant(), out value))
                return new MediaTypeHeaderValue(value);
            else
                return new MediaTypeHeaderValue(MediaTypeNames.Application.Octet);
        }

        private static bool TryReadRangeItem(RangeItemHeaderValue range, long contentLength, 
            out long start, out long end)
        {
            if (range.From != null)
            {
                start = range.From.Value;
                if (range.To != null)
                    end = range.To.Value;
                else
                    end = contentLength - 1;
            }
            else
            {
                end = contentLength - 1;
                if (range.To != null)
                    start = contentLength - range.To.Value;
                else
                    start = 0;
            }
            return (start < contentLength && end < contentLength);
        }

        private static void CreatePartialContent(Stream inputStream, Stream outputStream,
            long start, long end)
        {
            int count = 0;
            long remainingBytes = end - start + 1;
            long position = start;
            byte[] buffer = new byte[ReadStreamBufferSize];
            
            inputStream.Position = start;
            do
            {
                try
                {
                    if (remainingBytes > ReadStreamBufferSize)
                        count = inputStream.Read(buffer, 0, ReadStreamBufferSize);
                    else
                        count = inputStream.Read(buffer, 0, (int)remainingBytes); 
                    outputStream.Write(buffer, 0, count);
                }
                catch (Exception error)
                {
                    Debug.WriteLine(error);
                    break;
                }
                position = inputStream.Position;
                remainingBytes = end - position + 1;
            } while (position <= end);
        }
         
        #endregion
    } 
}
And we have:
  • AnyInvalidFileNameChars() helps us to check if there is any invalid file name character in URL parameter f (by the way, this is a good example of using LINQ on string). This can prevent some unnecessary file system accesses because a file with invalid file name won't exist at all.
  • GetMimeNameFromExt() helps us to get the corresponding Content-Type header value with file extension from the read-only dictionary MimeNames. If value can not be found, the default one isapplication/oct-stream.
  • TryReadRangeItem() helps us to read Range header from current HTTP request. Returned boolean value represents if range is available. If start or end position is greater than file length (parametercontentLength), it returns false.
  • CreatePartialContent() helps us to copy content from file stream to response stream with indicated range.
With these tools, to implement the action Play() method will be much easier. The prototype is:
[HttpGet]
public HttpResponseMessage Play(string f) { }
where parameter f represents the URL parameter f. HttpGetAttribute declares that GET is the only acceptable HTTP method. The response headers and content are sent in HttpResponseMessage class. The logic flow behind this method can be described with following chart.

Naturally, our first job is to see if the file exists. If not, will result in HTTP 404 Not Found status. Next is to check if Range header is present in current request. If not, the request will be treated as normal request and will result in HTTP 200 OK status. Third step is to determine if Range header can be fulfilled according to target file. If range is not present within file length, HTTP 416 Requested Range Not Satisfiable status will be responded to browser. After these steps, last one is to transmit target file with indicated range, and the story ends with HTTP 206 Partial Content status.
Here is complete code of Play() action.
[HttpGet]
public HttpResponseMessage Play(string f)
{
    // This can prevent some unnecessary accesses. 
    // These kind of file names won't be existing at all. 
    if (string.IsNullOrWhiteSpace(f) || AnyInvalidFileNameChars(f))
        throw new HttpResponseException(HttpStatusCode.NotFound);

    FileInfo fileInfo = new FileInfo(Path.Combine(InitialDirectory, f));
    
    if (!fileInfo.Exists)
        throw new HttpResponseException(HttpStatusCode.NotFound);

    long totalLength = fileInfo.Length;

    RangeHeaderValue rangeHeader = base.Request.Headers.Range;
    HttpResponseMessage response = new HttpResponseMessage();

    response.Headers.AcceptRanges.Add("bytes");

    // The request will be treated as normal request if there is no Range header.
    if (rangeHeader == null || !rangeHeader.Ranges.Any())
    {
        response.StatusCode = HttpStatusCode.OK;
        response.Content = new PushStreamContent((outputStream, httpContent, transpContext)
  =>
            {
                using (outputStream) // Copy the file to output stream straightforward. 
                using (Stream inputStream = fileInfo.OpenRead())
                {
                    try
                    {
                        inputStream.CopyTo(outputStream, ReadStreamBufferSize);
                    }
                    catch (Exception error)
                    {
                        Debug.WriteLine(error);
                    }
                }
            }, GetMimeNameFromExt(fileInfo.Extension));

        response.Content.Headers.ContentLength = totalLength;
        return response;
    }

    long start = 0, end = 0;

    // 1. If the unit is not 'bytes'.
    // 2. If there are multiple ranges in header value.
    // 3. If start or end position is greater than file length.
    if (rangeHeader.Unit != "bytes" || rangeHeader.Ranges.Count > 1 ||
        !TryReadRangeItem(rangeHeader.Ranges.First(), totalLength, out start, out end))
    {
        response.StatusCode = HttpStatusCode.RequestedRangeNotSatisfiable;
        response.Content = new StreamContent(Stream.Null);  // No content for this status.
        response.Content.Headers.ContentRange = new ContentRangeHeaderValue(totalLength);
        response.Content.Headers.ContentType = GetMimeNameFromExt(fileInfo.Extension);

        return response;
    } 

    var contentRange = new ContentRangeHeaderValue(start, end, totalLength);

    // We are now ready to produce partial content.
    response.StatusCode = HttpStatusCode.PartialContent;
    response.Content = new PushStreamContent((outputStream, httpContent, transpContext)
 =>
        {
            using (outputStream) // Copy the file to output stream in indicated range.
            using (Stream inputStream = fileInfo.OpenRead())
                CreatePartialContent(inputStream, outputStream, start, end);

        }, GetMimeNameFromExt(fileInfo.Extension));

    response.Content.Headers.ContentLength = end - start + 1;
    response.Content.Headers.ContentRange = contentRange;

    return response;
}

Play the video

Now it is time to play the video. We have a simple HTML5 page with a <video /> element and a <source /> element referring to the URL we have mentioned before.
<!DOCTYPE html>
<html>
    <head>
        <script type="text/javascript">

            function onLoad() {
                var sec = parseInt(document.location.search.substr(1));
                
                if (!isNaN(sec))
                    mainPlayer.currentTime = sec;
            }
        
        </script>
        <title>Partial Content Demonstration</title>
    </head>
    <body>
        <h3>Partial Content Demonstration</h3>
        <hr />
        <video id="mainPlayer" width="640" height="360" 
            autoplay="autoplay" controls="controls" onloadeddata="onLoad()">
            <source src="api/media/play?f=praise-our-lord.mp4" />
        </video>
    </body>
</html>
As you can see, the onLoad() function allows us skipping to indicated second by adding parameter. If parameter is omitted, the <video /> element plays the video from zero. For example, if we want to watch the video started from 120th second, then we have:
http://localhost/movie/index.html?120
Let us try this URL in Chrome.

Then we press F12 to open the development tool, switch to Network tab to see what happened behind the scenes.

These headers explain almost everything. Once onLoad() function gets triggered, the player sends a request including a Range header and the start position is exactly equal to the byte position of 120th second in this video. And the response header Content-Range describes start and end position, and total available bytes. This example shows the biggest benefit of Partial Content mechanism: when a video or audio file is too long, viewers can skip to any second they want.

Performance considerations

You have probably noticed that we are using PushStreamContent instead of StreamContent in Play() action (excepting empty content) to transfer file stream. Both of them are under the namespace System.Net.Http and derived from HttpContent class. The differences between them could be generally summarized as following points.

PushStreamContent vs. StreamContent

  • Sequence - For StreamContent, you have to generate content stream before action ends. ForPushStreamContent, you will generate it after exit from the action.
  • File Access - For StreamContent, you generate content stream from file before browser starts receiving. For PushStreamContent, you will do it after browser has received all HTTP headers and is ready to render content, which means if browser receives headers only but cancels rendering content, the file willnot be opened.
  • Memory Usage - For StreamContent, you have to generate partial content stream from file before action ends, which means it will be kept in memory temporarily until browser has received its all bytes. ForPushStreamContent, you can directly copy content from file to outgoing stream with specified range and without keeping content temporarily in memory.
Therefore, we choose PushStreamContent for video file streaming. It could reduce memory usage and work more efficiently.

History

2014-11-25
  • Fix a potential issue in CreatePartialContent() method when file size is greater thanInt32.MaxValue.
  • Add try-catch section in Play() method to prevent unnecessary exception message in debug mode.
  • Increase ReadStreamBufferSize value to improve performance.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Tuesday, May 17, 2016

Code Rafactoring

Первая часть серии обучающих видео про рефакторинг. 

В этой серии рассказывается о том, как сделать код более читаемым, как и когда можно применять те или иные шаблоны проектирования. 

1. Что такое рефакторинг?
2. О вынесении кода в отдельные методы.
3. Правильное использоваине переменных.
4. Обобщающий урок.
5. Замещение конструкторов фабричными методами.
6. Упрощение условных конструкций и паттерн «Стратегия» (2 урока).
7. Шаблонный метод.
8. Паттерн «Компоновщик».

https://www.youtube.com/watch?v=vhYK3pDUijk
https://www.youtube.com/watch?v=50miLglyGJQ
https://www.youtube.com/watch?v=a94IUcYJsbo
https://www.youtube.com/watch?v=BsJlP-X0WVg
https://www.youtube.com/watch?v=NhFmQl1FA5Y
https://www.youtube.com/watch?v=_7PI2LYAx-g
https://www.youtube.com/watch?v=owss5KuioFs
https://www.youtube.com/watch?v=1Q9ISmCTygU
https://www.youtube.com/watch?v=-qp5bMJ96oA


https://vk.com/wall-30666517_1360548

Вторая часть серии обучающих видео про рефакторинг. 
Первая часть: http://vk.cc/59wsFX 

В этой серии рассказывается о том, как сделать код более читаемым, о том, как и когда можно применять те или иные шаблоны проектирования. 

1. Паттерн «Строитель» (2 части)
3. Накапливающий параметр
4. Паттерн «Команда»
5. Паттерн «Адаптер»
6. Немного о безопасности типов
7. Паттерн «Декоратор»
8. Паттерн «Посетитель»
9. Паттерн «Абстрактная фабрика»

https://www.youtube.com/watch?v=ZBZmuVCTvIc
https://www.youtube.com/watch?v=m7eXOJofIuc
https://www.youtube.com/watch?v=jTQEb96XPSE
https://www.youtube.com/watch?v=hRHnHw2LVbM
https://www.youtube.com/watch?v=cBEUlEHZuqw
https://www.youtube.com/watch?v=EIsip10vrfk
https://www.youtube.com/watch?v=__-rstffCLs
https://www.youtube.com/watch?v=4tLSDAbe8QY
https://www.youtube.com/watch?v=riuRTxV01Vo

https://www.youtube.com/watch?v=vqEg37e4Mkw

https://vk.com/feed?w=wall-30666517_1362467

Wednesday, April 20, 2016

Что нужно знать при собеседовании на разработчика .NET

COMMON BACKGROUND:
Basic knowledge of Discrete Mathematics: Boolean arithmetics, Set theory, Automata theory (DFA, NFA), Regular expressions
OOP: Basic principles (Inheritance, Encapsulation, Polymorphism), SOLID principles, Design Patterns
Computer Architecture: Difference between x86 and x64 architectures

C# /.NET
Basics: What is .NET, CLR, IL? Program control flow, managed heap, managed stack, object allocation .NET; Object lifecycle, IDisposable, GC
C#: Basic OOP principles and how they are implemented in C#;Interfaces, Abstract classes, Static classes; Text and XML Processing; Events; Exception handling; Comparison of objects (operator == and Equals, IEqualityComparer); Linq, Fluent Syntax, yield keyword, IEnumerable; Lambda expressions
.NET Base Library: Collections (List, LinkedList, Dictionary, HashSet, SortedDictionary); I/O Streams; Serialization
Parallel Programming: Threads, Thread Pools, TPL, PLINQ; Synchronization primitives: Lock, Mutex, Semaphore, Event, ReaderWriterLock; Concurrent Collections; Task-based Async Pattern (TAP).

ALGORITHMS AND DATA STRUCTURES
For the following data structures & algorithms listed below: a concept of Big O notation;
asymptotic complexity of these algorithms; estimate asymptotic complexity of the code you write.
Data Structures: Array,  List, Linked list, Queue, Stack; Hash table; Binary search tree, Heap
Sorting Algorithms: Bubble sort, Insertion sort; Heap sort, Merge sort; Quick sort; Counting sort
Tree traversal algorithms: Depth first, Breadth first

Common algorithms: Binary search, Bit manipulation

Exceptions:
https://habrahabr.ru/post/178805/

Object lifecycle:
http://kavayii.blogspot.ru/2010/08/blog-post.html