Showing posts with label Bilgisayar Bilimleri. Show all posts
Showing posts with label Bilgisayar Bilimleri. Show all posts

Saturday, November 25, 2023

DTO projections via JPQL with Hibernate lazy initialization

DTO projections are very important to doing abstraction over model and data processing. Most common interests are fecthing data with a spesific partition and/or different format other than model. Also DTOs can be very usefull for partial database updates. In JPQL, query results can be transfered into DTOs with new keyword as following:
 
SELECT new org.module.submodule.ExampleDTO(b) FROM Book b
Also you can send parameters via constructor separately. But this approach provide you to control DTO flow and lazy initializations.
 
@Getter
@Setter
public class ExampleDTO {

    private String bookName;
    private Set<BookOwner> bookOwners;

    public ExampleDTO (Boob b) {
    
    	this.bookName = b.getName();
        this.bookOwners = b.getBookOwners();
        
        Hibernate.initialize(b.getBookOwners());
    }
}
In the constructor block, you can manage all initialization, property inclusions, property exclusions and customizations.

Saturday, May 14, 2022

Generating GUID in excel using macro

For some reason, you may need to generate GUID in an excel document. For me, because of using GUID as primary keys in database, I have needed to generate insert scripts and GUIDs as primary keys. First of all you need to activate developer options of excel. After that, you are able to write macros. Finally, create a module and create function given below:

Function createguid()
Dim guid As String
guid = CreateObject("WScript.Shell").Exec("powershell -command [guid]::newGuid().ToString()").StdOut.ReadAll
createguid = Application.WorksheetFunction.Clean(guid)
End Function
In this approach, powershell is used for GUID generation. You can use this function in formulas after creation. Only drawback of this method is powershell console is opened and closed as many as generated GUIDs. 

Saturday, December 25, 2021

Dynamically rendering HTML to image with javascript and SVG without using any external library

In this article, we render CSS styled HTML to image. With this method, HTML can be exported to png, jpeg or bmp without using any external js library. SVG 2 is a new recommendation that is exposed by w3 consortium. SVG 2 has a new feature, foreign object, that enables us to use different rendering engines in a SVG representation. So, we use HTML rendering engine to render image representation of HTML. Example codes are at below:

<div id="export_html_to_svg_container" style="display: flex;flex-direction: row;width: 300px; height: 100px;flex-grow: 1">
    <div style="width: 100%; background-color: #ff7070;"></div>
    <div style="display: flex;justify-content: center;text-align: center;align-items: center;width: 100%;background: #a624a8;font-weight: 500;color: white;">OUTPUT</div>
    <div style="width: 100%; background-color: #7070ff;"></div>
</div>

<div style="margin-top:10px">
    <button onclick="exportToImage('png')">PNG</button>
    <button onclick="exportToImage('jpeg')">JPEG</button>
    <button onclick="exportToImage('bmp')">BMP</button>
</div>

<script>

    function exportToImage(imageType) {

        let content = document.getElementById("export_html_to_svg_container");
        let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        let foreignObject = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");      

        svg.appendChild(foreignObject);
        foreignObject.innerHTML = content.outerHTML;
        svg.setAttribute("width", "300px");
        svg.setAttribute("height", "100px");
        foreignObject.setAttribute("width", "100%");
        foreignObject.setAttribute("height", "100%");

        let serializer = new XMLSerializer();
        let serializedContent = serializer.serializeToString(svg);
        let base64Content = btoa(serializedContent);
        let imageUrl = "data:image/svg+xml;base64," + base64Content;
        let img = document.createElement("img");
        
        img.onerror = function (a, b) {
            alert("error");
        }

        img.onload = function () {

            let canvas = document.createElement("canvas");
            let context = canvas.getContext("2d");
            context.drawImage(img, 0, 0);
            let dataUrl = canvas.toDataURL("image/" + imageType);
            simulateDownload("output." + imageType, dataUrl);
        }

        img.src = imageUrl;
    }

    function simulateDownload(download, href) {
        const a = document.createElement('a');
        a.download = download;
        a.href = href;
        a.click();
        a.remove();
    }

</script>
In example, we render div (export_html_to_svg_container) object with its content to image.  First of all, we create an SVG object. We need a foreign object to place the HTML in, so we create one. We put the HTML in the foreign object and the foreign object in the SVG object. Before rendering, we must set the SVG object's width and height attributes. Finally, we serialize the SVG object to string with XMLSerializer and generate an image url using that string. With this url, we render the output image with the help of the image(img) object and canvas.

This method is compatible with many modern browsers like Chrome, Firefox and Edge.

Working Example:

OUTPUT

Monday, February 22, 2021

k Nearest Neighbours Classifier (kNN) with Javascript Implementation for Beginners

k Nearest Neighbours Classifier (kNN) is a powerful but computational costly nonlinear classifier. Basically, kNN works based on distance calculation and finding of closest neighbour vectors according to a distance function. Distance function can be l1 (Manhattan distance), l2 (Euclidean distance) or a different available appropriate distance function. kNN uses a hyper parameter (k) that implies count of closest neighbours. 

Algorithm find closest k vector to current vector, and returns classification result as class label of the largest matched number of class between these k closest vectors.




In this example; classes 1,2,3 corresponds to red, green and purple respectively. Algorithm takes k, x and y as input and return class label as color and draw last input point as a orange point. 


->k
->X
->Y
Javascript Codes:
document.addEventListener('DOMContentLoaded', (e) => {

            drawPoints();
        });

        var point = { x: 0, y: 0 };
        var k = 3;

        var datas = [

            { c: 1, x: 10, y: 100 },
            { c: 1, x: -10, y: 50 },
            { c: 1, x: 50, y: 150 },
            { c: 2, x: 100, y: 80 },
            { c: 1, x: 30, y: 200 },
            { c: 2, x: 200, y: 50 },
            { c: 2, x: 120, y: 30 },
            { c: 2, x: 100, y: 150 },
            { c: 3, x: 300, y: 50 },
            { c: 1, x: -150, y: 200 },
            { c: 3, x: -300, y: 400 },
            { c: 3, x: 233, y: 333 },
            { c: 2, x: 127, y: 190 },
            { c: 3, x: 100, y: -32 },
            { c: 1, x: -50, y: -12 },
            { c: 1, x: -101, y: -50 },
            { c: 1, x: -301, y: -19 },
            { c: 2, x: 127, y: -22 },
            { c: 3, x: 120, y: -100 },
            { c: 2, x: -50, y: -200 },
            { c: 3, x: 800, y: 100 },
            { c: 3, x: 150, y: -150 },
            { c: 3, x: 70, y: -80 },
            { c: 3, x: 70, y: -500 },
            { c: 3, x: 200, y: -300 },
            { c: 2, x: -50, y: -150 },
            { c: 3, x: 111, y: -200 },
            { c: 3, x: 301, y: -310 },
            { c: 3, x: 200, y: -151 },
            { c: 1, x: -122, y: -99 },
            { c: 1, x: -301, y: -499 },
            { c: 1, x: 401, y: -1 },
        ];

        function start() {

            let classColor = "";

            setValues();

            drawCurrentPoint();

            let c = knnClassify();

            if (c == 1)
                classColor = "red";
            else if (c == 2)
                classColor = "green";
            else
                classColor = "purple";

            alert(classColor);
        }

        function knnClassify() {

            var classificationResults = [

                { c: 1, count: 0 },
                { c: 2, count: 0 },
                { c: 3, count: 0 }
            ];

            let distances = [];

            for (let i = 0; i < datas.length; i++) {

                let distance = l2Distance(datas[i], point);
                let distanceToClass = { c: datas[i].c, d: distance };

                distances.push(distanceToClass);
            }

            distances.sort(function (a, b) {

                return a.d - b.d;
            });

            for (let i = 0; i < k; i++) {

                for (let k = 0; k < classificationResults.length; k++) {

                    if (classificationResults[k].c == distances[i].c) {

                        classificationResults[k].count++;
                    }
                }
            }

            classificationResults.sort(function (a, b) {

                return b.count - a.count;
            });

            return classificationResults[0].c;
        }

        function l2Distance(point1, point2) {

            return Math.sqrt(Math.pow(point1.x - point2.x, 2) + Math.pow(point1.y - point2.y, 2));
        }

        function drawPoints() {

            var canvas = document.getElementById("g");

            for (let i = 0; i < datas.length; i++) {

                let circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');;
                circle.setAttributeNS(null, "cx", datas[i].x);
                circle.setAttributeNS(null, "cy", datas[i].y);
                circle.setAttributeNS(null, "r", 3);

                if (datas[i].c == 1)
                    circle.setAttributeNS(null, "fill", "red");
                else if (datas[i].c == 2)
                    circle.setAttributeNS(null, "fill", "green");
                else
                    circle.setAttributeNS(null, "fill", "purple");

                canvas.appendChild(circle);
            }
        }

        function drawCurrentPoint() {

            var canvas = document.getElementById("g");

            let circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
            circle.setAttributeNS(null, "cx", point.x);
            circle.setAttributeNS(null, "cy", point.y);
            circle.setAttributeNS(null, "r", 3);

            circle.setAttributeNS(null, "fill", "orange");

            canvas.appendChild(circle);
        }
                   
        function setValues() {

            k = parseFloat(document.getElementById("pk").value);
            point.x = parseFloat(document.getElementById("pX").value);
            point.y = parseFloat(document.getElementById("pY").value);
        }
HTML Codes:
    <div>
        <input id="pk" type="text" value="3" />->k
    </div>
    <div>
        <input id="pX" type="text" value="0" />->X
    </div>
    <div>
        <input id="pY" type="text" value="0" />->Y
    </div>

    <div id="resultDiv" style="margin-top: 20px;"></div>
    <div style="margin-top: 20px;">
        <button onclick="start()" type="button">CLASSIFY</button>
    </div>

    <div style="margin-top: 50px;">
        <svg height="500" id="canvas" width="500">
            <g id="g" transform="translate(250 250) scale(1,-1)">
                <line style="stroke-width: 1; stroke: rgb(0, 0, 255);" x1="-1000" x2="1000" y1="0" y2="0"></line>
                <line style="stroke-width: 1; stroke: rgb(0, 0, 255);" x1="0" x2="0" y1="-1000" y2="1000"></line>

            </g>
        </svg>
    </div>

Friday, February 12, 2021

Devextreme Toast Wrapper for Global Widget Configuration

While using Devextreme toast widget, you may need to configure it. By default, toast widget placed bottom middle of window. In this article, I write a wrapper javascript code to configure widget globally, and I provide to show widged at bottom right of window and widget will be continuing to display content while mouse hover on it. This wrapper code is also compatible with older browsers. 

Wrapper code is dependent to Jquery.

dx-toast-wrapper.js:
var toastDefault = {

            width: "500px",
            displayTime: 4000,
            cancelHiding: false,

            contentTemplate: null,

            position: {

                at: "right bottom",
                collision: "fit",
                boundaryOffset: "20 20",

            },

            onContentReady: function (e) {

                $(e.element).on("mouseenter", function () {

                    this.cancelHiding = true;

                }.bind(this));

                $(e.element).on("mouseleave", function () {

                    this.cancelHiding = false;
                    e.component.hide();

                }.bind(this));
            }.bind(this),

            onHiding: function (e) {

                e.cancel = this.cancelHiding;

            }.bind(this)
        };

        var toast = {

            success: function (message) {

                toastDefault.contentTemplate = function () {

                    return $("</p>").html(message);
                };

                toastDefault.type = "success";

                DevExpress.ui.notify(toastDefault);
            },
            error: function (message) {

                toastDefault.contentTemplate = function () {

                    return $("</p>").html(message);
                };

                toastDefault.type = "error";

                DevExpress.ui.notify(toastDefault);
            },
            info: function (message) {

                toastDefault.contentTemplate = function () {

                    return $("</p>").html(message);
                };

                toastDefault.type = "info";

                DevExpress.ui.notify(toastDefault);
            },
            warning: function (message) {

                toastDefault.contentTemplate = function () {

                    return $("</p>").html(message);
                };

                toastDefault.type = "warning";

                DevExpress.ui.notify(toastDefault);
            }
        }
You can use Devextreme toast like that:

index.html:
<script>
toast.success("Success message");
toast.error("Error message");
toast.info("Info message");
toast.warning("Warning message");
</script>

Sunday, February 7, 2021

Using ProjectBaseCore without Dependency Injection

We can use ProjectBaseCore(PBC) without dependency injection(DI) by instantiating DatabaseFactory class with new keyword. We can give to factory class's constructor function a object that is implemented IConfiguration interface, or we can create database object by simply giving connection string and provider parameters to GetDbObject function. I give a console application example of how to use PBC without DI.

Program.cs:
class Program
{
   static void Main(string[] args)
   {
        DatabaseFactory databaseFactory = new DatabaseFactory(GetConfiguration());
        IDatabase2 db = databaseFactory.GetDbObject();
        var dt = db.ExecuteQueryDataTable("select * from product");
   }

   static IConfigurationRoot GetConfiguration()
   {
         var builder = new ConfigurationBuilder()
           .SetBasePath(System.AppContext.BaseDirectory)
           .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);

         return builder.Build();
   }
}
appsettings.json:
{
  "DefaultDb": "Context",
  "ContextProviderName": "MySql.Data.MySqlClient",
  "ConnectionStrings": {
    "Context": "Server=localhost;Database=db;Uid=root;Pwd=1234;"
  }
}

Saturday, February 6, 2021

Injecting ProjectBaseCore in .Net Core Project

ProjectBaseCore (PBC) has to be injected to use in .net core project with version 2.x and later. After version 2.x, DatabaseFactory and QueryGeneratorFactory classes are no longer static classes. As a result, PBC version 2.x is not compatible with older versions, because you must inject DatabaseFactory and QueryGeneratorFactory classes or instantiate them with using new keyword.

First of all, in startup.js file or in a dependency injection container, injection must be defined:

services.AddSingleton<IDatabaseFactory, DatabaseFactory>();
services.AddSingleton<IQueryGeneratorFactory, QueryGeneratorFactory>();
In startup file, we write these codes in "ConfigureServices" function. After these declarations, we can use these services in controllers or middleware classes.

public class HomeController : Controller
{
     private readonly IDatabaseFactory _databaseFactory;
     private readonly IQueryGeneratorFactory _queryGeneratorFactory;

     public HomeController(IDatabaseFactory databaseFactory, IQueryGeneratorFactory queryGeneratorFactory)
     {
         _databaseFactory = databaseFactory;
         _queryGeneratorFactory = queryGeneratorFactory;
     }

     public IActionResult Index()
     {
         var db = _databaseFactory.GetDbObject();
         var qg = _queryGeneratorFactory.GetDbObject();
         qg.SelectText = "select * from product";

         var dt = db.ExecuteQueryDataTable(qg.GetSelectCommandBasic());
         return View();
     }
}

Saturday, January 23, 2021

Support Vector Machines (SVM) with Javascript Implementation for Beginners

I give an example of javascript implementation of support vector machines (SVM) and I explain SVM algorithm with hinge loss function in this article.

SVM algorithm aims to find a hyperplane that separates dataset in two classes:



For SVM, classes take values {1,-1}. Algorithm tries to find a hyperplane by maximizing margin between support vectors and hyperplane. We can describe support vectors as closest vectors to hyperplane. In 2 dimensional space, a hyperplane corresponds to a line.

So, to calculate a hyperplane, we must define hyperplane function:

f(x) = wx + b;

In this example, our space is 2 dimensional, so we have a coordinate point (x,y) and a class label (c).

As a result, we have a dataset that consists of javascript objects that includes {c,x,y} properties. Before going to optimization step, we must define SVM loss function. In this loss function, yi corresponds to c and xi corresponds to x,y for our case.


SVM algorithm uses hinge loss function. This loss function penalizes incorrect classifications. Max function is a convex function, so we can use gradient descent algorithm (GDA) as optimization algorithm. In 2 dimension, we have w1, w2 and b as optimization parameters. So we calculate partial derivatives:

d/dw1 Loss(w1,w2,c,x,y,b) = -1 * c * x

d/dw2 Loss(w1,w2,c,x,y,b) = -1 * c * y

d/b Loss(w1,w2,c,x,y,b) = -1 * c

While calculating loss, we calculate all losses for all points and take their mean value. As a result, we can calculate w1,w2 and b by using GDA. In our example, 1 labeled class and -1 labeled class are drawn red and blue respectively.


->alfa (learning parameter)

->maxIteration
Javascript codes:
<script>

        var alfa = 0.001;
        var maxIteration = 300000;

        var w1 = 1;
        var w2 = 1;
        var b = 1;
        var dw1 = 0;
        var dw2 = 0;
        var db = 0;

        var datas = [

            { c: 1, x:10, y: 100 },
            { c: 1, x:-10, y: 50 },
            { c: 1, x:50, y: 150 },
            { c: 1, x:100, y: 80 }, 
            { c: 1, x:30, y: 200 },
            { c: 1, x:200, y: 50 },
            { c: 1, x: 120, y: 30 },
            { c: 1, x: 100, y: 150 },
            { c: 1, x: 300, y: 50 },
            { c: 1, x: -150, y: 200 },
            { c: 1, x: -300, y: 400 },
            { c: 1, x: 233, y: 333 },
            { c: 1, x: 127, y: 190 },
            { c: 1, x: 100, y: -32},
            { c: 1, x: -50, y: -12 },
            { c: 1, x: -101, y: -50 },
            { c: 1, x: -301, y: -19 },
            { c: 1, x: 127, y: -22 },
            { c: -1, x:120, y: -100 },
            { c: -1, x:-50, y: -200 },
            { c: -1, x:800, y: 100 },
            { c: -1, x:150, y: -150 },
            { c: -1, x:70, y: -80 },
            { c: -1, x: 70, y: -500 },
            { c: -1, x: 200, y: -300 },
            { c: -1, x: -50, y: -150 },
            { c: -1, x: 111, y: -200 },
            { c: -1, x: 301, y: -310 },
            { c: -1, x: 200, y: -151 },
            { c: -1, x: -122, y: -99 },
            { c: -1, x: -301, y: -499 },
            { c: -1, x: 401, y: -1 },
        ]

        function getHingeLoss(data) {

            let loss = 1 - (w1 * data.x + w2 * data.y + b) * data.c;

            if (loss > 0)
                return loss;
            else
                return 0;
        }

        function setGradients() {

            let cost = 0;

            for (let i = 0; i < datas.length; i++) {

                let loss = getHingeLoss(datas[i]);
                cost += loss;

                if (loss > 0) {

                    dw1 += (-1 * datas[i].x * datas[i].c);
                    dw2 += (-1 * datas[i].y * datas[i].c);
                    db += (-1 * datas[i].c);
                }
            }

            cost /= datas.length;
            dw1 /= datas.length;
            dw2 /= datas.length;
            db /= datas.length;

            return cost;
        }

        function train() {

            setValues();
            drawPoints();

            for (var i = 0; i < maxIteration; i++) {

                setGradients();

                w1 -= alfa * dw1;
                w2 -= alfa * dw2;
                b -= alfa * db;             
            }

            drawLine();

            let resultDiv = document.getElementById("resultDiv");
            resultDiv.innerHTML = "w1:" + w1 + ", w2:" + w2 + ",b:" + b;
        }

        function drawPoints() {

            var canvas = document.getElementById("g");

            for (let i = 0; i < datas.length; i++) {

                let circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');;
                circle.setAttributeNS(null, "cx", datas[i].x);
                circle.setAttributeNS(null, "cy", datas[i].y);
                circle.setAttributeNS(null, "r", 1);

                if (datas[i].c == 1)
                    circle.setAttributeNS(null, "fill", "red");
                else
                    circle.setAttributeNS(null, "fill", "blue");

                canvas.appendChild(circle);
            }
        }

        function drawLine() {

            var canvas = document.getElementById("g");

            let point1x = 1000;
            let point1y = (w1 * point1x + b) / (-1 * w2);
            let point2x = -1000;
            let point2y = (w1 * point2x + b) / (-1 * w2);

            let line = document.createElementNS('http://www.w3.org/2000/svg', 'line');
            line.setAttributeNS(null, "x1", point1x);
            line.setAttributeNS(null, "y1", point1y);
            line.setAttributeNS(null, "x2", point2x);
            line.setAttributeNS(null, "y2", point2y);
            line.setAttributeNS(null, "style", "stroke: rgb(0, 255, 0); stroke-width:1");
            canvas.appendChild(line);
        }

        function setValues() {

            alfa = parseFloat(document.getElementById("alfa").value);
            maxIteration = parseFloat(document.getElementById("maxIteration").value);
        }

    </script>
HTML codes:
<div>
        <input id="alfa" type="text" value="0.001" />->alfa
    </div>
    <div>
        <input id="maxIteration" type="text" value="300000" />->maxIteration
    </div>

    <div id="resultDiv" style="margin-top: 20px;"></div>
    <div style="margin-top: 20px;">
        <button onclick="train()" type="button">START</button>
    </div>

    <div style="margin-top: 50px;">
        <svg height="500" id="canvas" width="500">
            <g id="g" transform="translate(250 250) scale(1,-1)">
                <line style="stroke-width: 1; stroke: rgb(0, 0, 255);" x1="-1000" x2="1000" y1="0" y2="0"></line>
                <line style="stroke-width: 1; stroke: rgb(0, 0, 255);" x1="0" x2="0" y1="-1000" y2="1000"></line>

            </g>
        </svg>
    </div>

Sunday, January 17, 2021

Gradient Descent Algorithm for Beginners (Quadratic Function Example)

In this article, I explain how gradient descent algorithm (GDA) works. I use a quadratic function for explanation:

f(x) = ax^2+bx+c

As you know, gradient of a function is first derivative of function (f'(x)). First derivative of f(x):

f'(x) = 2ax + b;

GDA is a iterative algorithm that calculates gradient of function f(x) for x in each iteration, and calculates next point by substracting gradient from x. GDA uses a learning parameter (alfa) that regularizes changings of x. So in each iteration, GDA calculates next x value:

x(t+1) = x(t) - alfa*f'(x(t))

So, why does GDA use gradient of function?

Think yourself on a hill and you want to get down. If slope is steep, you take a big step, otherwise a small step. Because while coming to end of hill, slope will be smaller until it is zero.

If you think of gradient as a vector, gradient's direction always guide you to end of hill or top of hill.

Note that, we can use GDA to find minimum point of a convex function, and gradient ascent algorithm to find maximum point of a concave function.

I prepared a working javascript example. You can calculate minimum point of quadratic function and visualize calculated point and function with this code.

In each iteration, calculated point (x,y) is displayed as a red dot.


->x (starting value of x)
->alfa (learning parameter)
->maxIteration (maximum iteration count)
->a
->b
->c

Javascript Codes:

<script>

        var x = 200;
        var alfa = 0.01;
        var maxIteration = 5000;

        var a = 1;
        var b = -5;
        var c = 6;

        var fx = (x) => a * x * x + b * x + c;

        var dxfx = (x) => 2 * a * x + b;

        function start() {

            setValues();
            drawCurve();

            var canvas = document.getElementById("g");
            let y = 0;

            for (var i = 0; i < maxIteration; i++) {

                x = x - alfa * dxfx(x);
                y = fx(x);

                let circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');;
                circle.setAttributeNS(null, "cx", x);
                circle.setAttributeNS(null, "cy", y);
                circle.setAttributeNS(null, "r", 1);
                circle.setAttributeNS(null, "fill", "red");

                canvas.appendChild(circle);
            }

            let resultDiv = document.getElementById("resultDiv");
            resultDiv.innerHTML = "x:" + x + ", y:" + y;
        }

        function setValues() {

            x = parseFloat(document.getElementById("x").value);
            alfa = parseFloat(document.getElementById("alfa").value);
            maxIteration = parseFloat(document.getElementById("maxIteration").value);
            a = parseFloat(document.getElementById("a").value);
            b = parseFloat(document.getElementById("b").value);
            c = parseFloat(document.getElementById("c").value);
        }

        function drawCurve() {

            var canvas = document.getElementById("g");

            for (var i = 5000; i >= -5000; i -= 1) {

                let line = document.createElementNS('http://www.w3.org/2000/svg', 'line');     
                line.setAttributeNS(null, "x1", i);
                line.setAttributeNS(null, "y1", fx(i));
                line.setAttributeNS(null, "x2", i - 1);
                line.setAttributeNS(null, "y2", fx(i - 1));
                line.setAttributeNS(null, "style", "stroke: rgb(0, 255, 0); stroke-width:1");
                canvas.appendChild(line);
            }
        }

    </script>
HTML Codes:
<div>
        <input id="x" type="text" value="200" />->x
    </div>
    <div>
        <input id="alfa" type="text" value="0.01" />->alfa
    </div>
    <div>
        <input id="maxIteration" type="text" value="5000" />->maxIteration
    </div>
    <div>
        <input id="a" type="text" value="1" />->a
    </div>
    <div>
        <input id="b" type="text" value="-5" />->b
    </div>
    <div>
        <input id="c" type="text" value="6" />->c
    </div>

    <div style="margin-top:20px" id="resultDiv"></div>
    <div style="margin-top:20px">
        <button type="button" onclick="start()">START</button>
    </div>

    <div style="margin-top:50px">
        <svg id="canvas" width="500" height="500">
            <g id="g" transform="translate(250 250) scale(1,-1)">
                <line x1="-1000" y1="0" x2="1000" y2="0" style="stroke: rgb(0, 0, 255); stroke-width:1"></line>
                <line x1="0" y1="-1000" x2="0" y2="1000" style="stroke: rgb(0, 0, 255); stroke-width:1"></line>

            </g>
        </svg>
    </div>

Sunday, January 3, 2021

x-comment jquery plugin: Jquery plugin for commenting or discussion

With x-comment, users' comments can be displayed hierarchically and users can respond to each other. There is no level limit while answering. x-comment allows adding new comments, reply to comments, and runs entirely on json and javascript objects.

Github:

https://github.com/vyigity/x-comment

Dependency

  • Jquery
  • Bootstrap
  • Fontawesome
  • Globalize

Configuration- Plugin

  • mode: Can be set as "array" or "remote". With "array" value, data is processed locally by using the javascript array given to the plugin. This method can be used for batch input and output operations. The data is accessed by running the function that returns Jquery Deferred in the dataSource field with "remote" value. (required)
  • items: In "array" mode, data is given to the plugin here. Also, after the data is loaded in "remote" mode, it can be read as a javascript array.(required in array mode)
  • dataSource: Can be set as a function that returns a jquery Deferred object. Data is obtained by running this function by the plugin. Data must be sent as parameter to promise resolve function.(required in remote mode)
  • authorName: The name written here is used by the plugin when responding. (required)
  • width: It allows the size of the plugin to be set to px. (optional)
  • allowInsert: Can be set as "true/false". It allows to show the form that enables inserting. (optional - Default:true)
  • allowReply: Can be set as "true/false". Ensures that comments can be answered or not. (optional - Default:true)
  • allowDelete: Can be set as "true/false". Ensures that comments can be deleted or not. (optional - Default:true)
  • onInserting: Can be set as a function that returns a jquery Deferred object. Is triggered when adding a new record. 
    • Parameters: 
      • instance: Instance of plugin. For example, it can be used to access the items or the plugin can be refreshed. 
      • value: Value that is typed by user.
  • onReplied: Can be set as a function that returns a jquery Deferred object. Is triggered when replying to a comment. 
    • Parameters: 
      • instance: Instance of plugin. For example, it can be used to access the items or the plugin can be refreshed. 
      • value: Value that is typed by user. 
      • item: Comment that is answered to.
  • onDeleting: Can be set as a function that returns a jquery Deferred object. Is triggered when deleting a comment. 
    • Parameters: 
      • instance: Instance of plugin. For example, it can be used to access the items or the plugin can be refreshed. 
      • item: Comment about to be deleted.
  • onError: Is triggered on error that occurs in functions that return a jquery Deferred object. Deferred object must be used with reject function. 
    • Parameters: 
      • data: The value sent through the reject function of the Deferred object.
  • texts: Can be configured text in plugin. A javascript object. (optional)

Configuration - texts (optional)

  • sendButtonText: Determines the text on the button in the new comment creation section.
  • inputTextAreaPlaceHolder: Allows editing of placeholder that appears in comment text area.
  • sendReplyButtonText: Determines the text on the reply send button.
  • cancelButtonText: Determines the text on the button that enables the cancellation of the response..
  • replyButtonText: Determines the text on the button that enables the answering process.
  • deleteButtonText: Determines the text on the button that allows the comments to be deleted.

Configuration - items

  • id: A unique value of a comment item.
  • parent: Parent value of a comment item.
  • deletable: Can be set as "true/false". The value that indicates the comment that can be deleted or not.
  • content: Content of a comment item.
  • created: Creation date time of a comment item. Value must be formatted as JSON. For example, "2020-12-31T20:52:00Z"
  • fullName: User name of owner of comment item.




Example (data.js):
var comments = [
    {
        id: 1,
        parent: null,
        deletable: true,
        content: "1. yorum",
        created: "2020-12-31T20:52:00Z",
        fullName: "Veli Yigit Yolcu"
    },
    {
        id: 2,
        parent: null,
        deletable: true,
        content: "2. yorum",
        created: "2020-12-31T21:52:00Z",
        fullName: "Veli Yigit Yolcu"
    },
    {
        id: 3,
        parent: 2,
        deletable: true,
        content: "2. yoruma cevap",
        created: "2020-12-31T22:52:00Z",
        fullName: "Veli Yigit Yolcu"
    }
];
Example (index.html):
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
    <link href="css/bootstrap.min.css" rel="stylesheet" />
    <link href="css/comment.css" rel="stylesheet" />
    <link href="css/fontawesome.min.css" rel="stylesheet" />
    <link href="css/brands.css" rel="stylesheet" />
    <link href="css/solid.css" rel="stylesheet" />

    <script src="js/jquery-3.5.1.min.js"></script>
    <script src="js/bootstrap.min.js"></script>
    <script src="js/x-comment.js"></script>

    <script src="cldr/cldr.js"></script>
    <script src="cldr/event.js"></script>
    <script src="cldr/supplemental.js"></script>

    <script src="globalize/globalize.js"></script>
    <script src="globalize/message.js"></script>
    <script src="globalize/number.js"></script>
    <script src="globalize/currency.js"></script>
    <script src="globalize/date.js"></script>

    <script src="cldr-data/supplemental.js"></script>
    <script src="cldr-data/tr.js"></script>

    <script src="data/data.js"></script>
</head>
<body>

    <script>

        Globalize.locale("tr");

        var currentId = 100;

        $(document).ready(function () {

            $("#xcomment").xcomment({

                mode: "array",
                items: comments,
                allowInsert: true,
                authorName: "Veli Yigit Yolcu",
                width: 600,
                onInserting: function (instance, value) {

                    let def = $.Deferred();

                    instance.items.splice(0, 0,
                        {
                            id: currentId,
                            parent: null,
                            deletable: true,
                            content: value,
                            created: new Date().toJSON(),
                            fullName: "Veli Yigit Yolcu"
                        });

                    currentId++;

                    def.resolve();

                    return def;
                },
                onReplied: function (instance, item, value) {

                    let def = $.Deferred();

                    instance.items.push(
                        {
                            id: currentId,
                            parent: item.id,
                            deletable: true,
                            content: value,
                            created: new Date().toJSON(),
                            fullName: "Veli Yigit Yolcu"
                        }
                    );

                    currentId++;

                    def.resolve();

                    return def;
                },
                onDeleting: function (instance, item) {

                    let def = $.Deferred();

                    for (var i = 0; i < instance.items.length; i++) {

                        if (instance.items[i].id === item.id) {

                            instance.items.splice(i, 1);
                        }
                    }

                    def.resolve();

                    return def;
                }
            });
        });

    </script>

    <div id="xcomment" ></div>

</body>
</html>

Friday, January 1, 2021

x-comment: Jquery için yorum-tartışma eklentisi - x-comment: Jquery plugin for commenting or discussion

For english:


Geçenlerde bir projede yorumlama eklentisi kullanma ihtiyacım oldu. İnternette araştırma yaptım ve ilginç bir şekilde iyi bir jquery eklentisi bulamadım. Bulduğum da (ismini burada vermeyeyim) kullanışlı olmadı ve hata verdi. İnternette bulduğum en iyi şey Semantic UI css dosyası oldu. Bende elimdekileri kullanarak bir jquery eklentisi olarak x-comment eklentisini yazdım. x-comment' i aslında sunucu odaklı çalışacak şekilde düşünmüştüm fakat sonradan lokal olarak da çalışabilecek şekilde geliştirdim. Bu yazıdaki örnek lokal kullanıma (array mod) örnektir.


x-comment ile kullanıcıların yorumları birbirlerine yanıt verecek şekilde hiyerarşik olarak gösterilebilmektedir. Yanıtlamada seviye sınırı yoktur. x-comment yeni yorum eklenmesine, yorumların yanıtlanmasına olanak verir ve tamamen json ve javascript nesneleri üzerinden çalışır. Aşağıdaki GitHub bağlantısından kodlara erişilebilir.

GitHub:


Bağımlılıklar
  • Jquery
  • Bootstrap
  • Fontawesome
  • Globalize
Ayarlar - Eklenti
  • mode: "array" veya "remote" değerlerini alabilir. "array" değerinde eklentiye verilen javascript array kullanılarak lokal olarak işlem yapılır. Toplu giriş çıkış işlemler için bu yöntem kullanılabilir. "remote" değerinde dataSource alanındaki Jquery Deferred döndüren fonksiyon çalıştırılarak veriye ulaşılır. (Zorunlu)
  • items: "array" modda eklentiye veri buradan verilir. Ayrıca "remote" modda veri yüklendikten sonra buradan javascript array olarak okunabilir.(Array modda zorunlu)
  • dataSource: Jquery Deffered nesnesi döndüren bir fonksiyon içerebilir. Eklenti tarafından bu fonksiyon çalıştırılarak veri elde edilir. Veri promise nesnesinin resolve fonksiyonuna parametre olarak gönderilmelidir. (Remote modda zorunlu)
  • authorName: Yanıtlama yapılırken eklenti tarafından burada yazan isim kullanılır. (Zorunlu)
  • width: Eklentinin boyutunun px olarak ayarlanması sağlar. (Zorunlu değil)
  • allowInsert: "true/false" değeri alabilir. Giriş yapılmasını sağlayan formun gösterilmesini sağlar. (Zorunlu değil - Default:true)
  • allowReply: "true/false" değeri alabilir. Yorumların yanıtlanabilir veya yanıtlamaz olmasını sağlar. (Zorunlu değil - Default:true)
  • allowDelete: "true/false" değeri alabilir. Yorumların silinebilir veya silinemez olmasını sağlar. (Zorunlu değil - Default:true)
  • onInserting: Jquery Deffered nesnesi döndüren bir fonksiyon içerebilir. Yeni kayıt eklenirken tetiklenir.
    • Parametreler: 
      • instance: Yorum eklentisinin instance değeridir. Örn. items alanına ulaşmak için kullanılabilir veya eklenti buradan yenilenebilir.
      • value: Kullanıcı tarafından girilen değerdir.
  • onReplied: Jquery Deffered nesnesi döndüren bir fonksiyon içerebilir. Bir yoruma cevap verildiğine tetiklenir.
    • Parametreler: 
      • instance: Yorum eklentisinin instance değeridir. Örn. items alanına ulaşmak için kullanılabilir veya eklenti buradan yenilenebilir.
      • value: Kullanıcı tarafından girilen değerdir.
      • item: Yanıt verilen yorumdur.
  • onDeleting: Jquery Deffered nesnesi döndüren bir fonksiyon içerebilir. Bir yorum silinirken tetiklenir.
    • Parametreler: 
      • instance: Yorum eklentisinin instance değeridir. Örn. items alanına ulaşmak için kullanılabilir veya eklenti buradan yenilenebilir.
      • item: Silinmek istenen yorumdur.
  • onError: Jquery Deffered nesnesi döndüren fonksiyonlar içinde hata olması durumunda tetiklenir. Deferred nesnesi reject fonksiyonu ile kullanılmalıdır.
    • Parametreler: 
      • data: Deferred nesnesinin reject fonksiyonu aracılığıyla gönderilen değerdir.
  • texts: Eklenti içindeki nesneler üzerinde yazıların ayarlanmasını sağlayan javascript nesnesini içerebilir. (Zorunlu değil)
Ayarlar - texts (Zorunlu değil)
  • sendButtonText: Yeni yorum oluşturma kısmında yer alan tuş üzerindeki yazıyı belirler.
  • inputTextAreaPlaceHolder: Yorum girilen yazı alanının üzerinde beliren yazının düzenlenmesini sağlar.
  • sendReplyButtonText: Yanıt gönderme tuşunun üzerindeki yazıyı belirler.
  • cancelButtonText: Yanıt verilme işleminin iptal edilmesini sağlayan tuş üzerindeki yazıyı belirler.
  • replyButtonText: Yanıtlama işleminin yapılmasını sağlayan tuş üzerindeki yazıyı belirler.
  • deleteButtonText: Yorumların silinmesini sağlayan tuş üzerindeki yazıyı belirler.
Ayarlar - items 
  • id: Yoruma ait eşsiz bir değerdir.
  • parent: Hangi yorumun alt yorumu (yanıtı) olduğunu belirten değerdir.
  • deletable: "true/false" değeri alabilir. Yorumun silinebilir olmasını veya olmamasını sağlayan değerdir.
  • content: Yorumun içeriğini belirtir.
  • created: Yorumun oluşturulma zamanını belirtir. JSON formatlı tarih değeri içermelidir. Örn. "2020-12-31T20:52:00Z"
  • fullName: Yorumu yapan kullanıcının adını içerir.

Örnek (data.js):
var comments = [
    {
        id: 1,
        parent: null,
        deletable: true,
        content: "1. yorum",
        created: "2020-12-31T20:52:00Z",
        fullName: "Veli Yigit Yolcu"
    },
    {
        id: 2,
        parent: null,
        deletable: true,
        content: "2. yorum",
        created: "2020-12-31T21:52:00Z",
        fullName: "Veli Yigit Yolcu"
    },
    {
        id: 3,
        parent: 2,
        deletable: true,
        content: "2. yoruma cevap",
        created: "2020-12-31T22:52:00Z",
        fullName: "Veli Yigit Yolcu"
    }
];
Örnek (index.html):
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title></title>
    <link href="css/bootstrap.min.css" rel="stylesheet" />
    <link href="css/comment.css" rel="stylesheet" />
    <link href="css/fontawesome.min.css" rel="stylesheet" />
    <link href="css/brands.css" rel="stylesheet" />
    <link href="css/solid.css" rel="stylesheet" />

    <script src="js/jquery-3.5.1.min.js"></script>
    <script src="js/bootstrap.min.js"></script>
    <script src="js/x-comment.js"></script>

    <script src="cldr/cldr.js"></script>
    <script src="cldr/event.js"></script>
    <script src="cldr/supplemental.js"></script>

    <script src="globalize/globalize.js"></script>
    <script src="globalize/message.js"></script>
    <script src="globalize/number.js"></script>
    <script src="globalize/currency.js"></script>
    <script src="globalize/date.js"></script>

    <script src="cldr-data/supplemental.js"></script>
    <script src="cldr-data/tr.js"></script>

    <script src="data/data.js"></script>
</head>
<body>

    <script>

        Globalize.locale("tr");

        var currentId = 100;

        $(document).ready(function () {

            $("#xcomment").xcomment({

                mode: "array",
                items: comments,
                allowInsert: true,
                authorName: "Veli Yigit Yolcu",
                width: 600,
                onInserting: function (instance, value) {

                    let def = $.Deferred();

                    instance.items.splice(0, 0,
                        {
                            id: currentId,
                            parent: null,
                            deletable: true,
                            content: value,
                            created: new Date().toJSON(),
                            fullName: "Veli Yigit Yolcu"
                        });

                    currentId++;

                    def.resolve();

                    return def;
                },
                onReplied: function (instance, item, value) {

                    let def = $.Deferred();

                    instance.items.push(
                        {
                            id: currentId,
                            parent: item.id,
                            deletable: true,
                            content: value,
                            created: new Date().toJSON(),
                            fullName: "Veli Yigit Yolcu"
                        }
                    );

                    currentId++;

                    def.resolve();

                    return def;
                },
                onDeleting: function (instance, item) {

                    let def = $.Deferred();

                    for (var i = 0; i < instance.items.length; i++) {

                        if (instance.items[i].id === item.id) {

                            instance.items.splice(i, 1);
                        }
                    }

                    def.resolve();

                    return def;
                }
            });
        });

    </script>

    <div id="xcomment" ></div>

</body>
</html>

Tuesday, March 24, 2020

.NET Core React Projesinin Docker ile Linux Üzerinde Çalıştırılması - Running .NET Core React Project on Linux with Docker

Bildiğimiz üzere .NET Core projelerinin en büyük avantajlarından biri her türlü işletim sisteminde çalıştırılabilmesidir. Bunların yanında "Dependency Injection" vb. bir çok mimari geliştirmesi de geliştiricilere .NET Core ile sunulmaktadır. React ise front-end olarak kullanılan ve Babel derleyicisi (compiler) ile derlenen ve bir front-end teknolojisidir. React uygulamasını derlemek için NodeJs kullanmamız gerekir. JSX ise basitçe javascript içine HTML kodları yazmamızı sağlayan bir javascript uzantısıdır. Docker ise bir işletim sistemi üzerinde birbirinden izole, mikro servis mimarisine destek veren, her bir uygulamanın kendi çalışma ortamına sahip olduğu konteynerler  oluşturmamızı sağlayan teknolojidir. Bu yazıda konumuz bunlar olmadığı için bunlardan derinlemesine bahsedilmeyecektir.

Burada anlatacağımız örnek React için olsa da  Dockerfile içinde ilgili kısımlar güncellenerek diğer front-end teknojileri içinde çalıştırılabilir. Dockerfile konteynerlerimizi nasıl oluşturacağımızı belirttiğimiz dosyadır. Docker file Visual Studio (VS) üzerinden projenin üzerine sağ tıklanarak açılan menüden Add->Docker Support üzerinden oluşturulabilir.


Fakat buradan oluşturduğumuz Dockerfile içinde NodeJs kurumu içermediği için çalışmayacaktır. Bu sebeple imajlar üzerinde işlem yaparken aşağıdaki kod bloğu ile NodeJs kurulumlarının da yapılması gerekmektedir:

RUN apt-get update -yq \
    && apt-get install curl gnupg -yq \
    && curl -sL https://deb.nodesource.com/setup_10.x | bash \
    && apt-get install nodejs -yq

Yukarıda kod bloğu VS üzerinden oluşturduğumuz Dockerfile üzerine eklendiğinde dosyamızın son hali aşağıdaki gibi olacaktır:

#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.

FROM microsoft/dotnet:2.1-aspnetcore-runtime AS base

RUN apt-get update -yq \
    && apt-get install curl gnupg -yq \
    && curl -sL https://deb.nodesource.com/setup_10.x | bash \
    && apt-get install nodejs -yq

WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM microsoft/dotnet:2.1-sdk AS build

RUN apt-get update -yq \
    && apt-get install curl gnupg -yq \
    && curl -sL https://deb.nodesource.com/setup_10.x | bash \
    && apt-get install nodejs -yq

WORKDIR /src
COPY ["TuringApp/TuringApp.csproj", "TuringApp/"]
RUN dotnet restore "TuringApp/TuringApp.csproj"
COPY . .
WORKDIR "/src/TuringApp"
RUN dotnet build "TuringApp.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "TuringApp.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .

ENTRYPOINT ["dotnet", "TuringApp.dll"]

Dockerfile hazır olduğuna göre sıra projemizi çalıştırmaya geldi. Bunun için proje kodlarımızı (kaynak kodlarını) derleme yapacağımız Linux üzerindeki bir klasöre kopyalıyoruz. Proje klasörümüzün hemen yanına da Dockerfile kopyalıyoruz. Daha açıklayıcı olması bakımından dosyaları kopyadıktan sonra klasör şöyle görünmelidir:


Bu aşamadan sonra Dockerfile kullanarak imaj oluşturmamız gerekiyor. Linux terminali açıyoruz ve ilk olarak aşağıdaki kodu çalıştırıyoruz:

sudo docker build -t firstcore .

Burada "firstcore" yeni oluşturacak imaja verdiğim isimdir. Siz kendinize göre isimlendirebilirsiniz.


İmajımız başarılı bir şekilde oluşturulursa terminalin son hali aşağıdaki gibi olacaktır:


Ayrıca aşağıdaki komutu kullanarak imajımızın docker tarafından listelendiğini görebiliriz:

sudo docker image ls


Bu aşamadan sonra sıra imajımızı çalıştırıp projemizi host etmeye geldi. Bunun için terminalde aşağıdaki komutu çalıştırıyoruz:

sudo docker run --name firstproj -p 8080:80 firstcore:latest


Run komutunda yer alan -p 8080:80 komutuyla 8080 portuna gelen istekleri uygulamamızın dinlediği 80 portuna yönlendiriyoruz. Name komutuyla konteyner ismini firstproj olarak verdik. Uygulamanın hangi portu dinlediği docker run komutunu çalıştırdıktan sonra teminalde görünmektedir. Bu aşamadan sonra tarayıcı üzerinden localhost:8080 üzerine istek yapıldığında projemiz tarayıcıda çalıştırılacaktır.



İşletim Sistemi:

Ubuntu 18.04.4 LST (Bionic Beaver)

Proje (Turing):

https://github.com/vyigity/TuringApp

Veri tabanı:

MySQL

Thursday, December 13, 2018

ProjectBase ile Transaction Üzerinden Dış Veri Erişim Yapıları ile Entegrasyon (Entity Framework 6 Örneği)

ProjectBase (PB) kütüphanesi veri erişim mantalitesi olarak ORM (Object Relational Mapping) ile provider seviyesi arasında bir konumda bulunmaktadır. PB dış bir veri erişim yapısı ile beraber kullanılmak istendiğinde ortak transaction kullanım gerekliliği doğabilmektedir. Örneğin ORM olarak Entity Framework 6 (EF) kullanıyoruz ve bazı işlemleri EF ile bazılarını PB ile yapacağız fakat bu işlemlerin aynı transaction üzerinde yapılması gerekmekte. Bu durumda EF üzerinde aşağıdaki kod ile bir transaction başlatabiliriz:

DataBaseContext context = new DataBaseContext();
DbTransaction transaction =  context.Database.BeginTransaction().UnderlyingTransaction

Burada elde ettiğimiz transaction nesnesi veri tabanı bağlantısı açık ve işlem yapmaya hazır olan bir nesnedir. Bu işlemden sonra bu transaction nesnesini PB de kullanabiliriz:

IDatabase2 db = DatabaseFactory.GetDbObject(DbSettings.TransactionMode);
db.UseExternalTransaction(transaction);

Buradan sonra PB ile yaptığımız işlemler EF ile oluşturduğumuz transaction üzerinden yapılacaktır. Normal EF kullanımından farklı olarak EF 6'da transaction bu yazıda bahsettiğimiz yöntemle başlatıldığında Commit edilmesi gerekmektedir:

context.Save();
context.Database.CurrentTransaction.Commit();

Bu işlem sırasında aşağıdaki hususlara dikkat edilmesi gerekir:

1. Dışarıda oluşturulan transaction geçerli bir durumda olmalı ve veri tabanı bağlantısı açık olmalıdır.
2. PB ile Commit veya RollBack yapıldığında PB sıralı yeni işlemleri yeni transaction kullanarak yapacaktır. Yani mevcut transaction geçerliliğini yitirecektir.
3. Bu işlem yapılırken PB transaction modda çalışır durumda olmalıdır.

Sunday, March 18, 2018

ProjectBase (PB) ile Asenkron Programlama - Asynchronous Programming with ProjectBase (PB)

PB v4.0.0-beta ile veri tabanı işlemlerinin asenkron olarak yapılabilmesi amacıyla PB içine asenkron mimari eklendi. Asenkron programlama (AP) .NET 4.5 ile ortaya çıktığı için PB'nin kullanılması için minumum .NET 4.5 kullanılması gerekmektedir. AP'de yapılacak yan işler ana işten ayrılarak asenkron bir şekilde halledilmekte ve ana işin bu yan işler sebebiyle meşgul edilmesi engellenebilmektedir. AP bir çok amaçla kullanılabilmekle beraber Forms uygulamalarında UI (User Interface) thread, yani arayüz elemanlarını oluşturan thread, genellikle ana thread olmakla beraber tamamlanması uzun işlerde bu thread uzun süre meşgul edilirse arayüzün cevap vermemesine neden olmaktadır. Ayrıca form yapısında bir arayüz elemanını oluşturan thread'den başka bir thread bu elemana ulaşmaya kalkıştığında ön tanımlı (default) olarak uygulama hata verecektir. AP form uygulamalarında kullanıldığında UI thread sürekli meşgul edilmediği için arayüz sürekli aktif kalacaktır.

AP ayrıca işi birden fazla Thread olarak yaptığı için bu işlerin işlemcilere dağıtımı kolay olmakta ve işlemci sayısına göre hız kazanımı elde edilebilmektedir.

Bu amaçla bu yazıda PB v4.0.0-beta kullanan bir örnek form uygulaması hazırlandı. Burada PB'nin kodlarını Github üzerinde indirip ilgili fonksiyonları Thread.Sleep() fonksiyonu kullanıp geciktirerek AP'nin etkisini daha rahat gözlemleyebilirsiniz. Oluşturulan forms arayüzü aşağıda verilmiştir:



Arayüzde 2 tane GridView, bir button ve bir label kullanılmıştır. DOLDUR tuşuna basıldığında label üzerine yazı yazılmadan önce veri çekme işlemleri başlatılmakta, yazı yazdırılmakta ve veriler GridView'lar içine yüklenmektedir. GridView'lar içine veriler kendi aralarında da eş zamansız yüklendiği için aynı anda yüklenmemektedir. Bu işlemler esnasında arayüz sürekli cevap verir durumda olacaktır.

PB v4.0.0-beta bağlantı yönetimini (Connection Management) eş zamansız (asenkron) yapmamaktadır. Yani bağlantı işlemleri gerçekleştirmek için işlemleri yürüten ana thread kullanılmaktadır. Bu yüzden bağlantı sırasında kısa süreli bir meşguliyet meydana gelebilmektedir. Aşağıda forms uygulamasının kodları verilmiştir:


public class EMPLOYEE
{
    public string FIRST_NAME { get; set; }
    public string LAST_NAME { get; set; }
    public decimal SALARY { get; set; }
    public DateTime? HIRE_DATE { get; set; }

}

private async void BtIslem_Click(object sender, EventArgs e)
{
    string sql = "select * from employees";
    string sql2 = "update employees set first_name = 'VELI YIGIT' where employee_id = 101";

    var db = DatabaseFactory.GetDbObjectAsync(DbSettings.ManuelConnectionManagement);

    var task = db.GetObjectListAsync(sql);
    var task2 = db.ExecuteQueryAsync(sql2);
    var task3 = db.GetObjectListAsync(sql);

    LbBilgi.Text = "Verilerin gelmesini beklememe gerek yok!";

    GwData.DataSource = await task;
    await task2;
    GwData2.DataSource = await task3;

    db.CloseConnection();
}

Tuşun basılma (click) olayında (event) "async" anahtar kelimesi ile asenkron kod içerdiği belirtilmektedir. Asenkron işlemlerin sonuçları "await" anahtar kelimesi kullanılarak beklenmektedir. Bir noktada await kullanıldı ise ana thread beklenen işlem sonuçlanana kadar alt satırları işlememektedir. Yukarıdaki kod bloğunda görüldüğü üzere 2 veri çekme işlemi ve bir güncelleme (update) işlemi asenkron olarak başlatılmaktadır. Label üzerine yazı yazdırıldıktan sonra (bu arada veritabanı işlemleri asenkron olarak devam etmekte), daha sonra sonuçlar beklenerek ilgili arayüz elemanları güncellenmektedir. Bu bekleme işlemi sırasında ana thread bloklanmadığı için arayüz sürekli cevap verir durumda olacaktır.

Burada bir diğer önemli nokta "Manuel Connection Management" modunun kullanılmasıdır. Otomatik yönetimde yan thread bağlantıyı diğerleri işlemler sonuçlanmadan kesebilmekte ve bu durum işlemlerin hatalı sonuçlanmasına neden olabilmektedir. Bu sebeple AP kullanılacak ise Manuel mod veya Transaction mod kullanılması gerekmektedir.

Bütün veritabanı işlemleri sonuçlandıktan sonra bağlandı kesilmektedir.

"Bu yazıda "Oracle Managed Provider", Oracle 11g Express Edition ve HR şeması test verileri kullanılmıştır."

 "https://github.com/vyigity/ProjectBase"


Saturday, December 16, 2017

ProjectBase kütüphanesi ile evrensel (global) parametre kullanımı

ProjectBase (PB) v2.5.0 ile birçok QueryGenerator (QG) sıkıntısı çözüldü ve global parametre özelliği kütüphaneye eklendi. Eski usülde parametre kullanımları eğer veri tabanı bağımsız tanımlanmak isteniyorsa FilterText özelliği içinde verilmeliydi. Bu yöntemde eğer parametre karakteri veri tabanı ile uyumsuz ise değiştiriliyordu. Bu yöntem lokal parametre kullanımı olarak tanımlanmaktadır. Global parametre kullanımında parametre SelectText, FilterText veya SelectTail içinde kullanılabilmektedir. Bu özellik ayrıca karmaşık sql komutlarının parametrik olarak QG tarafından işlenebilmesine de olanak sağlamaktadır. Aşağıdaki örneği inceleyelim:

var db = DatabaseFactory.GetDbObject();
var gen = QueryGeneratorFactory.GetDbObject(ParameterMode.Global);

string sql = @"
               select * from employees e
                            
               where e.employee_id =
 
               (select max(employee_id) from employees 
                where salary > .p.SALARY and commission_pct > .p.COMMISSION)
              ";

gen.SelectText = sql;
gen.AddFilterParameter("SALARY", 8000);
gen.AddFilterParameter("COMMISSION", 0.1d);

var dt = db.ExecuteQueryDataTable(gen.GetSelectCommandBasic());

Burada karmaşıklığı artırmak için iç içe select cümlecikleri kullandık ve iç cümleciğe parametrik olarak bir değer gönderdik. Global parametre tanımında parametre tanımlaması ".p." prefiksi kullanılarak yapılmaktadır. Örnekte gösterildiği gibi "SALARY" isimli parametre ".p.SALARY" olarak tanımlanmıştır. Neden ".p." gibi bir şey prefiks olarak tercih edildi diye soracak arkadaşlar için burada karmaşık bir derleyiciye sahip olmadığımız için örüntü (pattern) olarak yazılabilecek sql cümlecikleri içinde en az denk gelebilecek bir yapıyı tercih etmemiz gerekiyordu diye soruyu cevaplayabilirim. Yukarıdaki kod çalıştırıldığında sonuç DataTable olarak elde edilecektir.

"Bu yazıda "Oracle Managed Provider", Oracle 11g Express Edition ve HR şeması test verileri kullanılmıştır."

 "https://github.com/vyigity/ProjectBase"

Saturday, November 11, 2017

FluentGenerator kullanarak Oracle Entity Framework 6.x sınıflarının oluşturulması - Generating Oracle Entity Framework classes with FluentGenerator

FluentGenerator (FG) Entity Framework 6.x (EF) code first örüntüsüne göre sınıfların oluşturulması için yazılmış bir masaüstü uygulamasıdır. Oluşturulan sınıflar "fluent" mimariye göre oluşturulur ve POCO ve bağlantı (mapping) sınıfları ayrı ayrı oluşturulur.

FG, ProjectBase (PB) kütüphanesini kullandığı için uygulamanın çalıştırılması için PB' nin eklenmesi ve provider ayarlamalarının yapılması gerekir. Eğer Oracle unmanaged provider kullanılıyorsa "Oracle Client" kurulmalı ve ilgili ayarlamalar yapılmalıdır.

FG ve PB'yi aşağıdaki adresten indirebilirsiniz:

https://github.com/vyigity

Test veri tabanı olarak Oracle 11g express edition ve HR şeması içinde yer alan ögeler kullanılacaktır.



FG çalıştırıldığında config ayarlamalarına göre bağlantı cümleciği arayüzde görünecektir. Cümlecik değiştirilmek istenirse istenen yeni cümlecik girilir ve "CHANGE" tuşu ile yeni cümlecik geçerli hale getirilir. Namespace ismini kendi projenize göre belirledikten sonra "DIR" tuşu ile EF sınıflarının hangi klasör içinde oluşturulacağı seçilmelidir. Capitalization kısmında sınıf isimlerinin nasıl formatlanacağı seçilebilmektedir. Bu örnekte "Pascal" kullanılacaktır. Üstte yer alan TABLE_NAME kısmında tablo ismine göre, alttaki VIEW_NAME kısmından view ismine göre arama yapılabilir.

Örnek olarak görselde de görüleceği üzere "Employees" tablosuna ilişkin sınıfları oluşturmak istiyoruz. Arama kısmına "EMPL" yazarak istediğimiz tabloya ulaştık ve yön tuşları ile tabloyu sağ tarafta yer alan grid içine alarak bu tablonun oluşturulacağını belirttik. Son olarak "GENERATE" tuşu nu kullandığımızda önceden seçtiğimiz klasör içinde sınıflar aşağıdaki gibi oluşturulacaktır:

.\Generate\Context.cs

using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using YourNameSpace.Models.Mapping;

namespace YourNameSpace.Models
{
    public partial class DatabaseContext : DbContext
    {
        static DatabaseContext()
        {
            Database.SetInitializer<DatabaseContext>(null);
        }

        public DatabaseContext()
            : base("Name=Context")
        {
        }

        public DbSet<Employees> Employeess { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Configurations.Add(new EmployeesMap());
        }
    }
}

Context sınıfı EF'nin temel sınıfıdır ve FG foreign key (FK) ilişkileri oluşturmadığı için bu konuyla ilgili kodlar elden bu sınıf içine eklenmelidir. Map sınıfları "OnModelCreating" fonksiyonu içine yukarıdaki gibi eklenmeli ve DbSet tanımlaması sonraki eklentiler için elden yapılmalıdır.

.\Generate\Employees.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace YourNameSpace.Models
{
    public partial class Employees
    {
        public int EmployeeId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public string PhoneNumber { get; set; }
        public DateTime HireDate { get; set; }
        public string JobId { get; set; }
        public Nullable<decimal> Salary { get; set; }
        public Nullable<decimal> CommissionPct { get; set; }
        public Nullable<int> ManagerId { get; set; }
        public Nullable<short> DepartmentId { get; set; }

    }
}

Yukarıda EF Employees POCO sınıfının nasıl göründüğü verilmiştir.

.\Generate\Mapping\EmployeesMap.cs

using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;

namespace YourNameSpace.Models.Mapping
{
    public class EmployeesMap : EntityTypeConfiguration<Employees>
    {
        public EmployeesMap()
        {
            // Primary Key
            this.HasKey(t => new { t.EmployeeId });

            // Properties
            this.Property(t => t.EmployeeId)
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);

            this.Property(t => t.FirstName)
                .HasMaxLength(20);

            this.Property(t => t.LastName)
                .IsRequired()
                .HasMaxLength(25);

            this.Property(t => t.Email)
                .IsRequired()
                .HasMaxLength(25);

            this.Property(t => t.PhoneNumber)
                .HasMaxLength(20);

            this.Property(t => t.JobId)
                .IsRequired()
                .HasMaxLength(10);

            // Table & Column Mappings
            this.ToTable("EMPLOYEES", "HR");
            this.Property(t => t.EmployeeId).HasColumnName("EMPLOYEE_ID");
            this.Property(t => t.FirstName).HasColumnName("FIRST_NAME");
            this.Property(t => t.LastName).HasColumnName("LAST_NAME");
            this.Property(t => t.Email).HasColumnName("EMAIL");
            this.Property(t => t.PhoneNumber).HasColumnName("PHONE_NUMBER");
            this.Property(t => t.HireDate).HasColumnName("HIRE_DATE");
            this.Property(t => t.JobId).HasColumnName("JOB_ID");
            this.Property(t => t.Salary).HasColumnName("SALARY");
            this.Property(t => t.CommissionPct).HasColumnName("COMMISSION_PCT");
            this.Property(t => t.ManagerId).HasColumnName("MANAGER_ID");
            this.Property(t => t.DepartmentId).HasColumnName("DEPARTMENT_ID");
        }
    }
}

EmployeesMap sınıfı EF haritalama ve bazı validation kodlarını içeren sınıftır. Validation kodlarının burada bulunması zorunlu değildir. İstenmesi durumunda etiket (Attribute) usulü de uygulanabilir. Bütün sınıfları projenize eklediğinizde EF ile ilgili veri tabanı nesneleri üzerinde işlem yapmaya başlayabilirsiniz.

Monday, October 16, 2017

ProjectBase ile DataTable Yerine Önceden Tanımlı Nesne Kullanarak Veri Çekme (Tip Belirtimli - Typed)

Daha önceki yazılarda DataTable üzerinde veri çekme örneği yapmıştık. DataTable ile yapılan işlemler "Typed" yani belirli türlü olmadığı için kodlamayı zorlaştırmaktadır. Şimdi kendi oluşturduğumuz bir sınıfa ait nesneyi verileri tutmak için kullanacağız. Yine "EMPLOYEES" tablosunu kullanalım ve önce sınıfı aşağıdaki gibi oluşturalım:

public class EMPLOYEE
{
    public string FIRST_NAME { get; set; }
    public string LAST_NAME { get; set; }
    public decimal SALARY { get; set; }
    public DateTime? HIRE_DATE { get; set; }

}

Burada dikkat edilmesi gereken husus yazacağımız sorgudan gelecek olan sütun isimlerinin "Property" isimleri ile eşleşmesidir. Eşleme büyük-küçük harfe duyarlı değildir. Şimdi bu sınıfı kullanacak veri çekme kodlarını yazalım:

var db = DatabaseFactory.GetDbObject();
var gen = QueryGeneratorFactory.GetDbObject();

gen.SelectText = "select * from employees where employee_id < 112";

List<EMPLOYEE> employees = db.GetObjectList<EMPLOYEE>(gen.GetSelectCommandBasic());

Yukarıdaki kod çalıştırıldığında id numarası 112'den küçük kayıtları yazdığımız sınıfın bir listesi olarak elde edeceğiz.

"Bu yazıda "Oracle Managed Provider", Oracle 11g Express Edition ve HR şeması test verileri kullanılmıştır."

"https://github.com/vyigity/ProjectBase"