برچسب: Operations

  • CRUD operations on PostgreSQL using C# and Npgsql | Code4IT

    CRUD operations on PostgreSQL using C# and Npgsql | Code4IT


    Once we have a Postgres instance running, we can perform operations on it. We will use Npgsql to query a Postgres instance with C#

    Table of Contents

    Just a second! 🫷
    If you are here, it means that you are a software developer.
    So, you know that storage, networking, and domain management have a cost .

    If you want to support this blog, please ensure that you have disabled the adblocker for this site.
    I configured Google AdSense to show as few ADS as possible – I don’t want to bother you with lots of ads, but I still need to add some to pay for the resources for my site.

    Thank you for your understanding.
    Davide

    PostgreSQL is one of the most famous relational databases. It has got tons of features, and it is open source.

    In a previous article, we’ve seen how to run an instance of Postgres by using Docker.

    In this article, we will learn how to perform CRUD operations in C# by using Npgsql.

    Introducing the project

    To query a Postgres database, I’ve created a simple .NET API application with CRUD operations.

    We will operate on a single table that stores info for my board game collection. Of course, we will Create, Read, Update and Delete items from the DB (otherwise it would not be an article about CRUD operations 😅).

    Before starting writing, we need to install Npgsql, a NuGet package that acts as a dataprovider for PostgreSQL.

    NpgSql Nuget Package

    Open the connection

    Once we have created the application, we can instantiate and open a connection against our database.

    private NpgsqlConnection connection;
    
    public NpgsqlBoardGameRepository()
    {
        connection = new NpgsqlConnection(CONNECTION_STRING);
        connection.Open();
    }
    

    We simply create a NpgsqlConnection object, and we keep a reference to it. We will use that reference to perform queries against our DB.

    Connection string

    The only parameter we can pass as input to the NpgsqlConnection constructor is the connection string.

    You must compose it by specifying the host address, the port, the database name we are connecting to, and the credentials of the user that is querying the DB.

    private const string CONNECTION_STRING = "Host=localhost:5455;" +
        "Username=postgresUser;" +
        "Password=postgresPW;" +
        "Database=postgresDB";
    

    If you instantiate Postgres using Docker following the steps I described in a previous article, most of the connection string configurations we use here match the Environment variables we’ve defined before.

    CRUD operations

    Now that everything is in place, it’s time to operate on our DB!

    We are working on a table, Games, whose name is stored in a constant:

    private const string TABLE_NAME = "Games";
    

    The Games table consists of several fields:

    Field name Field type
    id INTEGER PK
    Name VARCHAR NOT NULL
    MinPlayers SMALLINT NOT NULL
    MaxPlayers SMALLINT
    AverageDuration SMALLINT

    This table is mapped to the BoardGame class:

    public class BoardGame
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int MinPlayers { get; set; }
        public int MaxPlayers { get; set; }
        public int AverageDuration { get; set; }
    }
    

    To double-check the results, you can use a UI tool to access the Database. For instance, if you use pgAdmin, you can find the list of databases running on a host.

    Database listing on pgAdmin

    And, if you want to see the content of a particular table, you can select it under Schemas>public>Tables>tablename, and then select View>AllRows

    How to view table rows on pgAdmin

    Create

    First things first, we have to insert some data in our DB.

    public async Task Add(BoardGame game)
    {
        string commandText = $"INSERT INTO {TABLE_NAME} (id, Name, MinPlayers, MaxPlayers, AverageDuration) VALUES (@id, @name, @minPl, @maxPl, @avgDur)";
        await using (var cmd = new NpgsqlCommand(commandText, connection))
        {
            cmd.Parameters.AddWithValue("id", game.Id);
            cmd.Parameters.AddWithValue("name", game.Name);
            cmd.Parameters.AddWithValue("minPl", game.MinPlayers);
            cmd.Parameters.AddWithValue("maxPl", game.MaxPlayers);
            cmd.Parameters.AddWithValue("avgDur", game.AverageDuration);
    
            await cmd.ExecuteNonQueryAsync();
        }
    }
    

    The commandText string contains the full command to be issued. In this case, it’s a simple INSERT statement.

    We use the commandText string to create a NpgsqlCommandobject by specifying the query and the connection where we will perform that query. Note that the command must be Disposed after its use: wrap it in a using block.

    Then, we will add the parameters to the query. AddWithValue accepts two parameters: the first is the name of the key, with the same name defined in the query, but without the @ symbol; in the query, we use @minPl, and as a parameter, we use minPl.

    Never, never, create the query by concatenating the input params as a string, to avoid SQL Injection attacks.

    Finally, we can execute the query asynchronously with ExecuteNonQueryAsync.

    Read

    Now that we have some games stored in our table, we can retrieve those items:

    public async Task<BoardGame> Get(int id)
    {
        string commandText = $"SELECT * FROM {TABLE_NAME} WHERE ID = @id";
        await using (NpgsqlCommand cmd = new NpgsqlCommand(commandText, connection))
        {
            cmd.Parameters.AddWithValue("id", id);
    
            await using (NpgsqlDataReader reader = await cmd.ExecuteReaderAsync())
                while (await reader.ReadAsync())
                {
                    BoardGame game = ReadBoardGame(reader);
                    return game;
                }
        }
        return null;
    }
    

    Again, we define the query as a text, use it to create a NpgsqlCommand, specify the parameters’ values, and then we execute the query.

    The ExecuteReaderAsync method returns a NpgsqlDataReader object that we can use to fetch the data. We update the position of the stream with reader.ReadAsync(), and then we convert the current data with ReadBoardGame(reader) in this way:

    private static BoardGame ReadBoardGame(NpgsqlDataReader reader)
    {
        int? id = reader["id"] as int?;
        string name = reader["name"] as string;
        short? minPlayers = reader["minplayers"] as Int16?;
        short? maxPlayers = reader["maxplayers"] as Int16?;
        short? averageDuration = reader["averageduration"] as Int16?;
    
        BoardGame game = new BoardGame
        {
            Id = id.Value,
            Name = name,
            MinPlayers = minPlayers.Value,
            MaxPlayers = maxPlayers.Value,
            AverageDuration = averageDuration.Value
        };
        return game;
    }
    

    This method simply reads the data associated with each column (for instance, reader["averageduration"]), then we convert them to their data type. Then we build and return a BoardGame object.

    Update

    Updating items is similar to inserting a new item.

    public async Task Update(int id, BoardGame game)
    {
        var commandText = $@"UPDATE {TABLE_NAME}
                    SET Name = @name, MinPlayers = @minPl, MaxPlayers = @maxPl, AverageDuration = @avgDur
                    WHERE id = @id";
    
        await using (var cmd = new NpgsqlCommand(commandText, connection))
        {
            cmd.Parameters.AddWithValue("id", game.Id);
            cmd.Parameters.AddWithValue("name", game.Name);
            cmd.Parameters.AddWithValue("minPl", game.MinPlayers);
            cmd.Parameters.AddWithValue("maxPl", game.MaxPlayers);
            cmd.Parameters.AddWithValue("avgDur", game.AverageDuration);
    
            await cmd.ExecuteNonQueryAsync();
        }
    }
    

    Of course, the query is different, but the general structure is the same: create the query, create the Command, add parameters, and execute the query with ExecuteNonQueryAsync.

    Delete

    Just for completeness, here’s how to delete an item by specifying its id.

    public async Task Delete(int id)
    {
        string commandText = $"DELETE FROM {TABLE_NAME} WHERE ID=(@p)";
        await using (var cmd = new NpgsqlCommand(commandText, connection))
        {
            cmd.Parameters.AddWithValue("p", id);
            await cmd.ExecuteNonQueryAsync();
        }
    }
    

    Always the same story, so I have nothing to add.

    ExecuteNonQueryAsync vs ExecuteReaderAsync

    As you’ve seen, some operations use ExecuteNonQueryAsync, while some others use ExecuteReaderAsync. Why?

    ExecuteNonQuery and ExecuteNonQueryAsync execute commands against a connection. Those methods do not return data from the database, but only the number of rows affected. They are used to perform INSERT, UPDATE, and DELETE operations.

    On the contrary, ExecuteReader and ExecuteReaderAsync are used to perform queries on the database and return a DbDataReader object, which is a read-only stream of rows retrieved from the data source. They are used in conjunction with SELECT queries.

    Bonus 1: Create the table if not already existing

    Of course, you can also create tables programmatically.

    public async Task CreateTableIfNotExists()
    {
        var sql = $"CREATE TABLE if not exists {TABLE_NAME}" +
            $"(" +
            $"id serial PRIMARY KEY, " +
            $"Name VARCHAR (200) NOT NULL, " +
            $"MinPlayers SMALLINT NOT NULL, " +
            $"MaxPlayers SMALLINT, " +
            $"AverageDuration SMALLINT" +
            $")";
    
        using var cmd = new NpgsqlCommand(sql, connection);
    
        await cmd.ExecuteNonQueryAsync();
    }
    

    Again, nothing fancy: create the command text, create a NpgsqlCommand object, and execute the command.

    Bonus 2: Check the database version

    To check if the database is up and running, and your credentials are correct (those set in the connection string), you might want to retrieve the DB version.

    You can do it in 2 ways.

    With the following method, you query for the version directly on the database.

    public async Task<string> GetVersion()
    {
        var sql = "SELECT version()";
    
        using var cmd = new NpgsqlCommand(sql, connection);
    
        var versionFromQuery = (await cmd.ExecuteScalarAsync()).ToString();
    
        return versionFromQuery;
    }
    

    This method returns lots of info that directly depend on the database instance. In my case, I see PostgreSQL 14.1 (Debian 14.1-1.pgdg110+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 64-bit.

    The other way is to use PostgreSqlVersion.

    public async Task<string> GetVersion()
    {
        var versionFromConnection = connection.PostgreSqlVersion;
    
        return versionFromConnection;
    }
    

    PostgreSqlVersion returns a Version object containing some fields like Major, Minor, Revision, and more.

    PostgresVersion from connection info

    You can call the ToString method of that object to get a value like “14.1”.

    Additional readings

    In a previous article, we’ve seen how to download and run a PostgreSQL instance on your local machine using Docker.

    🔗 How to run PostgreSQL locally with Docker | Code4IT

    To query PostgreSQL with C#, we used the Npsgql NuGet package. So, you might want to read the official documentation.

    🔗 Npgsql documentation | Npsgql

    In particular, an important part to consider is the mapping between C# and SQL data types:

    🔗 PostgreSQL to C# type mapping | Npsgql

    When talking about parameters to be passed to the query, I mentioned the SQL Injection vulnerability. Here you can read more about it.

    🔗 SQL Injection | Imperva

    Finally, here you can find the repository used for this article.

    🔗 Repository used for this article | GitHub

    This article first appeared on Code4IT 🐧

    Wrapping up

    In this article, we’ve learned how to perform simple operations on a PostgreSQL database to retrieve and update the content of a table.

    This is the most basic way to perform those operations. You explicitly write the queries and issue them without much stuff in between.

    In future articles, we will see some other ways to perform the same operations in C#, but using other tools and packages. Maybe Entity Framework? Maybe Dapper? Stay tuned!

    Happy coding!

    🐧



    Source link

  • PostgreSQL CRUD operations with C# and Dapper | Code4IT


    Mapping every SQL result to a data type can be a pain. To simplify our life, we can use an ORM like Dapper to automatically map the data.

    Table of Contents

    Just a second! 🫷
    If you are here, it means that you are a software developer.
    So, you know that storage, networking, and domain management have a cost .

    If you want to support this blog, please ensure that you have disabled the adblocker for this site.
    I configured Google AdSense to show as few ADS as possible – I don’t want to bother you with lots of ads, but I still need to add some to pay for the resources for my site.

    Thank you for your understanding.
    Davide

    In a previous article, we’ve seen how to perform simple CRUD operations on a Postgres database by using Npgsql, a library that allows you to write and perform queries to be executed specifically on a PostgreSQL database.

    In this article, we will take a step further: we will perform the same operations using Dapper, one of the most popular ORM for .NET applications, and we will see how performing those operations becomes easier.

    Introducing the project

    For this article, I will reuse the project I used for the previous article.

    This project performs CRUD (Create, Read, Update, Delete) operations on a Postgres database with a single table: Games. All those operations (plus a bunch of other additional ones) are executed by a class that implements this interface:

    public interface IBoardGameRepository
    {
        Task<IEnumerable<BoardGame>> GetAll();
    
        Task<BoardGame> Get(int id);
    
        Task Add(BoardGame game);
    
        Task Update(int id, BoardGame game);
    
        Task Delete(int id);
    
        Task<string> GetVersion();
    
        Task CreateTableIfNotExists();
    }
    

    This allows me to define and use a new class without modifying too much the project: in fact, I simply have to replace the dependency in the Startup class to use the Dapper repository.

    But first…

    Dapper, a micro-ORM

    In the introduction, I said that we will use Dapper, a popular ORM. Let me explain.

    ORM stands for Object-relational mapping and is a technique that allows you to map data from one format to another. This technique simplifies developers’ lives since they don’t have to manually map everything that comes from the database to an object – the ORM takes care of this task.

    Dapper is one of the most popular ORMs, created by the Stack Overflow team. Well, actually Dapper is a Micro-ORM: it performs only a subset of the operations commonly executed by other ORMs; for example, Dapper allows you to map query results to objects, but it does not automatically generate the queries.

    To add Dapper to your .NET project, simply run this command:

    dotnet add package Dapper
    

    Or add the NuGet package via Visual Studio:

    Dapper Nuget Package

    Dapper will take care of only a part of the operations; for instance, it cannot open a connection to your DB. That’s why you need to install Npgsql, just as we did in a previous article. We can say the whole Dapper library is a set of Extension Methods built on top of the native data access implementation – in the case of PostgreSQL, on to op Npgsql.

    Now we have all the dependencies installed, so we can start writing our queries.

    Open the connection

    Once we have created the application, we must instantiate and open a connection against our database.

    private NpgsqlConnection connection;
    
    public DapperBoardGameRepository()
    {
        connection = new NpgsqlConnection(CONNECTION_STRING);
        connection.Open();
    }
    

    We will use the connection object later when we will perform the queries.

    CRUD operations

    We are working on a table, Games, whose name is stored in a constant:

    private const string TABLE_NAME = "Games";
    

    The Games table consists of several fields:

    Field name Field type
    id INTEGER PK
    Name VARCHAR NOT NULL
    MinPlayers SMALLINT NOT NULL
    MaxPlayers SMALLINT
    AverageDuration SMALLINT

    And it is mapped to the BoardGame class:

    public class BoardGame
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public int MinPlayers { get; set; }
        public int MaxPlayers { get; set; }
        public int AverageDuration { get; set; }
    }
    

    So, the main task of Dapper is to map the result of the queries performed on the Games table to one or more BoardGame objects.

    Create

    To create a new row on the Games table, we need to do something like this:

    public async Task Add(BoardGame game)
    {
        string commandText = $"INSERT INTO {TABLE_NAME} (id, Name, MinPlayers, MaxPlayers, AverageDuration) VALUES (@id, @name, @minPl, @maxPl, @avgDur)";
    
        var queryArguments = new
        {
            id = game.Id,
            name = game.Name,
            minPl = game.MinPlayers,
            maxPl = game.MaxPlayers,
            avgDur = game.AverageDuration
        };
    
        await connection.ExecuteAsync(commandText, queryArguments);
    }
    

    Since Dapper does not create any queries for us, we still need to define them explicitly.

    The query contains various parameters, marked with the @ symbol (@id, @name, @minPl, @maxPl, @avgDur). Those are placeholders, whose actual values are defined in the queryArguments anonymous object:

    var queryArguments = new
    {
        id = game.Id,
        name = game.Name,
        minPl = game.MinPlayers,
        maxPl = game.MaxPlayers,
        avgDur = game.AverageDuration
    };
    

    Finally, we can execute our query on the connection we have opened in the constructor:

    await connection.ExecuteAsync(commandText, queryArguments);
    

    Comparison with Npgsql library

    Using dapper simplifies our code. In fact, when using the native Npgsql library without Dapper, we have to declare every parameter explicitly.

    As a comparison, have a look at how we implemented the same operation using Npgsql:

    public async Task Add(BoardGame game)
    {
        string commandText = $"INSERT INTO {TABLE_NAME} (id, Name, MinPlayers, MaxPlayers, AverageDuration) VALUES (@id, @name, @minPl, @maxPl, @avgDur)";
        await using (var cmd = new NpgsqlCommand(commandText, connection))
        {
            cmd.Parameters.AddWithValue("id", game.Id);
            cmd.Parameters.AddWithValue("name", game.Name);
            cmd.Parameters.AddWithValue("minPl", game.MinPlayers);
            cmd.Parameters.AddWithValue("maxPl", game.MaxPlayers);
            cmd.Parameters.AddWithValue("avgDur", game.AverageDuration);
    
            await cmd.ExecuteNonQueryAsync();
        }
    }
    

    When using Dapper, we declare the parameter values in a single anonymous object, and we don’t create a NpgsqlCommand instance to define our query.

    Read

    As we’ve seen before, an ORM simplifies how you read data from a database by automatically mapping the query result to a list of objects.

    When we want to get all the games stored on our table, we can do something like that:

    public async Task<IEnumerable<BoardGame>> GetAll()
    {
        string commandText = $"SELECT * FROM {TABLE_NAME}";
        var games = await connection.QueryAsync<BoardGame>(commandText);
    
        return games;
    }
    

    Again, we define our query and allow Dapper to do the rest for us.

    In particular, connection.QueryAsync<BoardGame> fetches all the data from the query and converts it to a collection of BoardGame objects, performing the mapping for us.

    Of course, you can also query for BoardGames with a specific Id:

    public async Task<BoardGame> Get(int id)
    {
        string commandText = $"SELECT * FROM {TABLE_NAME} WHERE ID = @id";
    
        var queryArgs = new { Id = id };
        var game = await connection.QueryFirstAsync<BoardGame>(commandText, queryArgs);
        return game;
    }
    

    As we did before, you define the query with a placeholder @id, which will have the value defined in the queryArgs anonymous object.

    To store the result in a C# object, we map only the first object returned by the query, by using QueryFirstAsync instead of QueryAsync.

    Comparison with Npgsql

    The power of Dapper is the ability to automatically map query results to C# object.

    With the plain Npgsql library, we would have done:

    await using (NpgsqlDataReader reader = await cmd.ExecuteReaderAsync())
        while (await reader.ReadAsync())
        {
            BoardGame game = ReadBoardGame(reader);
            games.Add(game);
        }
    

    to perform the query and open a reader on the result set. Then we would have defined a custom mapper to convert the Reader to a BoardGame object.

    private static BoardGame ReadBoardGame(NpgsqlDataReader reader)
    {
        int? id = reader["id"] as int?;
        string name = reader["name"] as string;
        short? minPlayers = reader["minplayers"] as Int16?;
        short? maxPlayers = reader["maxplayers"] as Int16?;
        short? averageDuration = reader["averageduration"] as Int16?;
    
        BoardGame game = new BoardGame
        {
            Id = id.Value,
            Name = name,
            MinPlayers = minPlayers.Value,
            MaxPlayers = maxPlayers.Value,
            AverageDuration = averageDuration.Value
        };
        return game;
    }
    

    With Dapper, all of this is done in a single instruction:

    var games = await connection.QueryAsync<BoardGame>(commandText);
    

    Update and Delete

    Update and Delete operations are quite similar: just a query, with a parameter, whose operation is executed in an asynchronous way.

    I will add them here just for completeness:

    public async Task Update(int id, BoardGame game)
    {
        var commandText = $@"UPDATE {TABLE_NAME}
                    SET Name = @name, MinPlayers = @minPl, MaxPlayers = @maxPl, AverageDuration = @avgDur
                    WHERE id = @id";
    
        var queryArgs = new
        {
            id = game.Id,
            name = game.Name,
            minPl = game.MinPlayers,
            maxPl = game.MaxPlayers,
            avgDur = game.AverageDuration
        };
    
        await connection.ExecuteAsync(commandText, queryArgs);
    }
    

    and

    public async Task Delete(int id)
    {
        string commandText = $"DELETE FROM {TABLE_NAME} WHERE ID=(@p)";
    
        var queryArguments = new {  p = id  };
    
        await connection.ExecuteAsync(commandText, queryArguments);
    }
    

    Again: define the SQL operation, specify the placeholders, and execute the operation with ExecuteAsync.

    Further readings

    As always, the best way to get started with a new library is by reading the official documentation:

    🔗 Dapper official documentation

    To see the complete code for these examples, you can have a look at the related GitHub repository.

    🔗 PostgresCrudOperations repository | GitHub

    Dapper adds a layer above the data access. If you want to go a level below, to have full control over what’s going on, you should use the native PostgreSQL library, Npgsql, as I explained in a previous article.

    🔗CRUD operations on PostgreSQL using C# and Npgsql | Code4IT

    How to get a Postgres instance running? You can use any cloud implementation, or you can download and run a PostgreSQL instance on your local machine using Docker as I explained in this guide:

    🔗 How to run PostgreSQL locally with Docker | Code4IT

    This article first appeared on Code4IT 🐧

    Wrapping up

    In this article, we’ve seen how to use Dapper to simplify our data access. Dapper is useful for querying different types of RDBMS, not only PostgreSQL.

    To try those examples out, download the code from GitHub, specify the connection string, and make sure that you are using the DapperBoardGameRepository class (this can be configured in the Startup class).

    In a future article, we will use Entity Framework to – guess what? – perform CRUD operations on the Games table. In that way, you will have 3 different ways to access data stored on PostgreSQL by using .NET Core.

    Happy coding!

    🐧



    Source link

  • How to perform CRUD operations with Entity Framework Core and PostgreSQL &vert; Code4IT

    How to perform CRUD operations with Entity Framework Core and PostgreSQL | Code4IT


    With Entity Framework you can perform operations on relational databases without writing a single line of SQL. We will use EF to integrate PostgreSQL in our application

    Table of Contents

    Just a second! 🫷
    If you are here, it means that you are a software developer.
    So, you know that storage, networking, and domain management have a cost .

    If you want to support this blog, please ensure that you have disabled the adblocker for this site.
    I configured Google AdSense to show as few ADS as possible – I don’t want to bother you with lots of ads, but I still need to add some to pay for the resources for my site.

    Thank you for your understanding.
    Davide

    When working with relational databases, you often come across two tasks: writing SQL queries and mapping the results to some DTO objects.

    .NET developers are lucky to have an incredibly powerful tool that can speed up their development: Entity Framework. Entity Framework (in short: EF) is an ORM built with in mind simplicity and readability.

    In this article, we will perform CRUD operations with Entity Framework Core on a database table stored on PostgreSQL.

    Introduction EF Core

    With Entity Framework you don’t have to write SQL queries in plain text: you write C# code that gets automatically translated into SQL commands. Then the result is automatically mapped to your C# classes.

    Entity Framework supports tons of database engines, such as SQL Server, MySQL, Azure CosmosDB, Oracle, and, of course, PostgreSQL.

    There are a lot of things you should know about EF if you’re new to it. In this case, the best resource is its official documentation.

    But the only way to learn it is by getting your hands dirty. Let’s go!

    How to set up EF Core

    For this article, we will reuse the same .NET Core repository and the same database table we used when we performed CRUD operations with Dapper (a lightweight OR-M) and with NpgSql, which is the library that performs bare-metal operations.

    The first thing to do is, as usual, install the related NuGet package. Here we will need Npgsql.EntityFrameworkCore.PostgreSQL. Since I’ve used .NET 5, I have downloaded version 5.0.10.

    Npgsql.EntityFrameworkCore.PostgreSQL NuGet package

    Then, we need to define and configure the DB Context.

    Define and configure DbContext

    The idea behind Entity Framework is to create DB Context objects that map database tables to C# data sets. DB Contexts are the entry point to the tables, and the EF way to work with databases.

    So, the first thing to do is to define a class that inherits from DbContext:

    public class BoardGamesContext : DbContext
    {
    
    }
    

    Within this class we define one or more DbSets, that represent the collections of data rows on their related DB table:

    public DbSet<BoardGame> Games { get; set; }
    

    Then we can configure this specific DbContext by overriding the OnConfiguring method and specifying some options; for example, you can specify the connection string:

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseNpgsql(CONNECTION_STRING);
        base.OnConfiguring(optionsBuilder);
    }
    

    Remember to call base.OnConfiguring! Otherwise some configurations will not be applied, and the system may not work.

    Also, pay attention to the Port in the connection string! While with other libraries you can define it as

    private const string CONNECTION_STRING = "Host=localhost:5455;" +
        "Username=postgresUser;" +
        "Password=postgresPW;" +
        "Database=postgresDB";
    

    Entity Framework core requires the port to be specified in a different field:

    private const string CONNECTION_STRING = "Host=localhost;"+
                "Port=5455;" + // THIS!!!!!
                "Username=postgresUser;" +
                "Password=postgresPW;" +
                "Database=postgresDB";
    

    If you don’t explicitly define the Port, EF Core won’t recognize the destination host.

    Then, we can configure the models mapped to DB tables by overriding OnModelCreating:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<BoardGame>(e => e.ToTable("games"));
        base.OnModelCreating(modelBuilder);
    }
    

    Here we’re saying that the rows in the games table will be mapped to BoardGame objects. We will come back to it later.

    For now, we’re done; here’s the full BoardGamesContext class:

    public class BoardGamesContext : DbContext
    {
        public DbSet<BoardGame> Games { get; set; }
    
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseNpgsql(CONNECTION_STRING);
            base.OnConfiguring(optionsBuilder);
        }
        private const string CONNECTION_STRING = "Host=localhost;Port=5455;" +
                    "Username=postgresUser;" +
                    "Password=postgresPW;" +
                    "Database=postgresDB";
    
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<BoardGame>(e => e.ToTable("games"));
            base.OnModelCreating(modelBuilder);
        }
    }
    

    Add the DbContext to Program

    Now that we have the BoardGamesContext ready we have to add its reference in the Startup class.

    In the ConfigureServices method, add the following instruction:

    services.AddDbContext<BoardGamesContext>();
    

    With this instruction, you make the BoardGamesContext context available across the whole application.

    You can further configure that context using an additional parameter of type Action<DbContextOptionsBuilder>. In this example, you can skip it, since we’ve already configured the BoardGamesContext using the OnConfiguring method. They are equivalent.

    If you don’t like

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseNpgsql(CONNECTION_STRING);
        base.OnConfiguring(optionsBuilder);
    }
    

    you can do

    services.AddDbContext<BoardGamesContext>(
        optionsBuilder => optionsBuilder.UseNpgsql(CONNECTION_STRING)
    );
    

    The choice is yours!

    Define and customize the DB Model

    As we know, EF allows you to map DB rows to C# objects. So, we have to create a class and configure it in a way that allows EF Core to perform the mapping.

    Here we have the BoardGame class:

    public class BoardGame
    {
        [System.ComponentModel.DataAnnotations.Key]
        public int Id { get; set; }
    
        public string Name { get; set; }
    
        public int MinPlayers { get; set; }
    
        public int MaxPlayers { get; set; }
    
        public int AverageDuration { get; set; }
    }
    

    Notice that we’ve explicitly declared that Id is the primary key in the table.

    But it’s not enough! This way the code won’t work! 😣

    Have a look at the table on Postgres:

    Games table on Posgres

    Have you noticed it? Postgres uses lowercase names, but we are using CamelCase. C# names must be 100% identical to those in the database!

    Now we have two ways:

    ➡ Rename all the C# properties to their lowercase equivalent

    public class BoardGame
    {
        [System.ComponentModel.DataAnnotations.Key]
        public int id { get; set; }
        public string name { get; set; }
        /// and so on
    }
    

    ➡ decorate all the properties with the Column attribute.

    public class BoardGame
    {
        [System.ComponentModel.DataAnnotations.Key]
        [Column("id")]
        public int Id { get; set; }
    
        [Column("name")]
        public string Name { get; set; }
    
        [Column("minplayers")]
        public int MinPlayers { get; set; }
    
        [Column("maxplayers")]
        public int MaxPlayers { get; set; }
    
        [Column("averageduration")]
        public int AverageDuration { get; set; }
    }
    

    Using the Column attribute is useful also when the DB column names and the C# properties differ for more than just the case, like in:

    [Column("averageduration")]
    public int AvgDuration { get; set; }
    

    Is it enough? Have a look again at the table definition:

    Games table on Posgres

    Noticed the table name? It’s “games”, not “BoardGame”!

    We need to tell EF which is the table that contains BoardGame objects.

    Again, we have two ways:

    ➡ Override the OnModelCreating method in the BoardGamesContext class, as we’ve seen before:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<BoardGame>(e => e.ToTable("games"));
        base.OnModelCreating(modelBuilder);
    }
    

    ➡ Add the Table attribute to the BoardGame class:

    [Table("games")]
    public class BoardGame
    {...}
    

    Again, the choice is yours.

    CRUD operations with Entity Framework

    Now that the setup is complete, we can perform our CRUD operations. Entity Framework simplifies a lot the way to perform such types of operations, so we can move fast in this part.

    There are two main points to remember:

    1. to access the context we have to create a new instance of BoardGamesContext, which should be placed into a using block.
    2. When performing operations that change the status of the DB (insert/update/delete rows), you have to explicitly call SaveChanges or SaveChangesAsync to apply those changes. This is useful when performing batch operations on one or more tables (for example, inserting an order in the Order table and updating the user address in the Users table).

    Create

    To add a new BoardGame, we have to initialize the BoardGamesContext context and add a new game to the Games DbSet.

    public async Task Add(BoardGame game)
    {
        using (var db = new BoardGamesContext())
        {
            await db.Games.AddAsync(game);
            await db.SaveChangesAsync();
        }
    }
    

    Read

    If you need a specific entity by its id you can use Find and FindAsync.

    public async Task<BoardGame> Get(int id)
    {
        using (var db = new BoardGamesContext())
        {
            return await db.Games.FindAsync(id);
        }
    }
    

    Or, if you need all the items, you can retrieve them by using ToListAsync

    public async Task<IEnumerable<BoardGame>> GetAll()
    {
        using (var db = new BoardGamesContext())
        {
            return await db.Games.ToListAsync();
        }
    }
    

    Update

    Updating an item is incredibly straightforward: you have to call the Update method, and then save your changes with SaveChangesAsync.

    public async Task Update(int id, BoardGame game)
    {
        using (var db = new BoardGamesContext())
        {
            db.Games.Update(game);
            await db.SaveChangesAsync();
    
        }
    }
    

    For some reason, EF does not provide an asynchronous way to update and remove items. I suppose that it’s done to prevent or mitigate race conditions.

    Delete

    Finally, to delete an item you have to call the Remove method and pass to it the game to be removed. Of course, you can retrieve that game using FindAsync.

    public async Task Delete(int id)
    {
        using (var db = new BoardGamesContext())
        {
            var game = await db.Games.FindAsync(id);
            if (game == null)
                return;
    
            db.Games.Remove(game);
            await db.SaveChangesAsync();
        }
    }
    

    Further readings

    Entity Framework is impressive, and you can integrate it with tons of database vendors. In the link below you can find the full list. But pay attention that not all the libraries are implemented by the EF team, some are third-party libraries (like the one we used for Postgres):

    🔗 Database Providers | Microsoft docs

    If you want to start working with PostgreSQL, a good way is to download it as a Docker image:

    🔗 How to run PostgreSQL locally with Docker | Code4IT

    Then, if you don’t like Entity Framework, you can perform CRUD operations using the native library, NpgSql:

    🔗 CRUD operations on PostgreSQL using C# and Npgsql | Code4IT

    or, maybe, if you prefer Dapper:

    🔗 PostgreSQL CRUD operations with C# and Dapper | Code4IT

    Finally, you can have a look at the full repository here:

    🔗 Repository used for this article | GitHub

    This article first appeared on Code4IT 🐧

    Wrapping up

    This article concludes the series that explores 3 ways to perform CRUD operations on a Postgres database with C#.

    In the first article, we’ve seen how to perform bare-metal queries using NpgSql. In the second article, we’ve used Dapper, which helps mapping queries results to C# DTOs. Finally, we’ve used Entity Framework to avoid writing SQL queries and have everything in place.

    Which one is your favorite way to query relational databases?

    What are the pros and cons of each approach?

    Happy coding!

    🐧



    Source link

  • How an XDR Platform Transforms Your SOC Operations

    How an XDR Platform Transforms Your SOC Operations


    XDR solutions are revolutionizing how security teams handle threats by dramatically reducing false positives and streamlining operations. In fact, modern XDR platforms generate significantly fewer false positives than traditional SIEM threat analytics, allowing security teams to focus on genuine threats rather than chasing shadows. We’ve seen firsthand how security operations centers (SOCs) struggle with alert fatigue, fragmented visibility, and resource constraints. However, an XDR platform addresses these challenges by unifying information from multiple sources and providing a holistic view of threats. This integration enables organizations to operate advanced threat detection and response with fewer SOC resources, making it a cost-effective approach to modern security operations.

    An XDR platform consolidates security data into a single system, ensuring that SOC teams and surrounding departments can operate from the same information base. Consequently, this unified approach not only streamlines operations but also minimizes breach risks, making it an essential component of contemporary cybersecurity strategies.

    In this article, we’ll explore how XDR transforms SOC operations, why traditional tools fall short, and the practical benefits of implementing this technology in your security framework.

    The SOC Challenge: Why Traditional Tools Fall Short

    Security Operations Centers (SOCs) today face unprecedented challenges with their traditional security tools. While security teams strive to protect organizations, they’re increasingly finding themselves overwhelmed by fundamental limitations in their security infrastructure.

    Alert overload and analyst fatigue

    Modern SOC teams are drowning in alerts. As per Vectra AI, an overwhelming 71% of SOC practitioners worry they’ll miss real attacks buried in alert floods, while 51% believe they simply cannot keep pace with mounting security threats. The statistics paint a troubling picture:

    Siloed tools and fragmented visibility

    The tool sprawl in security operations creates massive blind spots. According to Vectra AI findings, 73% of SOCs have more than 10 security tools in place, while 45% juggle more than 20 different tools. Despite this arsenal, 47% of practitioners don’t trust their tools to work as needed.

    Many organizations struggle with siloed security data across disparate systems. Each department stores logs, alerts, and operational details in separate repositories that rarely communicate with one another. This fragmentation means threat hunting becomes guesswork because critical artifacts sit in systems that no single team can access.

    Slow response times and manual processes

    Traditional SOCs rely heavily on manual processes, significantly extending detection and response times. When investigating incidents, analysts must manually piece together information from different silos, losing precious time during active cyber incidents.

    According to research by Palo Alto Networks, automation can reduce SOC response times by up to 50%, significantly limiting breach impacts. Unfortunately, most traditional SOCs lack this capability. The workflow in traditional environments is characterized by manual processes that exacerbate alert fatigue while dealing with massive threat alert volumes.

    The complexity of investigations further slows response. When an incident occurs, analysts must combine data from various sources to understand the full scope of an attack, a time-consuming process that allows threats to linger in systems longer than necessary.

    What is an XDR Platform and How Does It Work?

    Extended Detection and Response (XDR) platforms represent the evolution of cybersecurity technology, breaking down traditional barriers between security tools. Unlike siloed solutions, XDR solutions provide a holistic approach to threat management through unified visibility and coordinated response.

    Unified data collection across endpoints, network, and cloud

    At its core, an XDR platform aggregates and correlates data from multiple security layers into a centralized repository. This comprehensive data collection encompasses:

    • Endpoints (computers, servers, mobile devices)
    • Network infrastructure and traffic
    • Cloud environments and workloads
    • Email systems and applications
    • Identity and access management

    This integration eliminates blind spots that typically plague security operations. By collecting telemetry from across the entire attack surface, XDR platforms provide security teams with complete visibility into potential threats. The system automatically ingests, cleans, and standardizes this data, ensuring consistent, high-quality information for analysis.

    Real-time threat detection using AI and ML

    XDR platforms leverage advanced analytics, artificial intelligence, and machine learning to identify suspicious patterns and anomalies that human analysts might miss. These capabilities enable:

    • Automatic correlation of seemingly unrelated events across different security layers
    • Identification of sophisticated multi-vector attacks through pattern recognition
    • Real-time monitoring and analysis of data streams for immediate threat identification
    • Reduction in false positives through contextual understanding of alerts

    The AI-powered capabilities enable XDR platforms to detect threats at a scale and speed impossible for human analysts alone. Moreover, these systems continuously learn and adapt to evolving threats through machine learning models.

    Automated response and orchestration capabilities

    Once threats are detected, XDR platforms can initiate automated responses without requiring manual intervention. This automation includes:

    • Isolation of compromised devices to contain threats
    • Blocking of malicious IP addresses and domains
    • Execution of predefined response playbooks for consistent remediation
    • Prioritization of incidents based on severity for efficient resource allocation

    Key Benefits of XDR for SOC Operations

    Implementing an XDR platform delivers immediate, measurable advantages to security operations centers struggling with traditional tools and fragmented systems. SOC teams gain specific capabilities that fundamentally transform their effectiveness against modern threats.

    Faster threat detection and reduced false positives

    The strategic advantage of XDR solutions begins with their ability to dramatically reduce alert volume. XDR tools automatically group related alerts into unified incidents, representing entire attack sequences rather than isolated events. This correlation across different security layers identifies complex attack patterns that traditional solutions might miss.

    Improved analyst productivity through automation

    As per the Tines report, 64% of analysts spend over half their time on tedious manual work, with 66% believing that half of their tasks could be automated. XDR platforms address this challenge through built-in orchestration and automation that offload repetitive tasks. Specifically, XDR can automate threat detection through machine learning, streamline incident response processes, and generate AI-powered incident reports. This automation allows SOC teams to detect sophisticated attacks with fewer resources while reducing response time.

    Centralized visibility and simplified workflows

    XDR provides a single pane view that eliminates “swivel chair integration,” where analysts manually interface across multiple security systems. This unified approach aggregates data from endpoints, networks, applications, and cloud environments into a consolidated platform. As a result, analysts gain swift investigation capabilities with instant access to all forensic artifacts, events, and threat intelligence in one location. This centralization particularly benefits teams during complex investigations, enabling them to quickly understand the complete attack story.

    Better alignment with compliance and audit needs

    XDR strengthens regulatory compliance through detailed documentation and monitoring capabilities. The platform generates comprehensive logs and audit trails of security events, user activities, and system changes, helping organizations demonstrate compliance to regulators. Additionally, XDR’s continuous monitoring adapts to new threats and regulatory changes, ensuring consistent compliance over time. Through centralized visibility and data aggregation, XDR effectively monitors data flows and access patterns, preventing unauthorized access to sensitive information.

    Conclusion

    XDR platforms clearly represent a significant advancement in cybersecurity technology.  At Seqrite, we offer a comprehensive XDR platform designed to help organizations simplify their SOC operations, improve detection accuracy, and automate responses. If you are looking to strengthen your cybersecurity posture with an effective and scalable XDR solution, Seqrite XDR is built to help you stay ahead of evolving threats.

     



    Source link