querybuilder icon indicating copy to clipboard operation
querybuilder copied to clipboard

Support class Property Annotation

Open ahmad-moussawi opened this issue 6 years ago • 11 comments

The ability to ignore a property, override the column name, when using the Insert(object) and Update(object) overloads.

class Entry {

   [Ignore]
   public string Type {get; set;}
   
   [Column("user_name")]
   public string UserName {get; set;}
}

Insert()

ahmad-moussawi avatar Mar 13 '19 13:03 ahmad-moussawi

I already make couple of test I solved on my local I just need to upload it to prod but the name of the attributes will be SqlKataIgnoreAttribute and SqlKataColumn

So the idea is when calling Insert or Update it will call the method AsInsert and AsUpdate.

This will be code change.

Annotations

   public class SqlKataIgnoreAttribute : Attribute
    {
    }


 public class SqlKataColumn : Attribute
    {
        public string Name { get; private set; }
        public SqlKataColumn(string name)
        {
            if (string.IsNullOrEmpty(name))
            {
                throw new ArgumentNullException("SqlKataColumn empty or null Name parameter, please provide a name");
            }
            Name = name;
        }

    }

Code for Update

 public Query AsUpdate(object data)
        {
            var dictionary = new Dictionary<string, object>();

            var props = data.GetType().GetRuntimeProperties()
                .Where(_ => _.GetCustomAttribute(typeof(SqlKataIgnoreAttribute)) == null);

            foreach (var item in props)
            {
                var attr = item.GetCustomAttribute(typeof(SqlKataColumn)) as SqlKataColumn;
                if (attr != null)
                {
                    dictionary.Add(attr.Name, item.GetValue(data));
                }
                else
                {
                    dictionary.Add(item.Name, item.GetValue(data));
                }
            }

            return AsUpdate(dictionary);
        }

Code for Insert


   public Query AsInsert(object data, bool returnId = false)
        {
            var dictionary = new Dictionary<string, object>();

            var props = data.GetType()
                        .GetRuntimeProperties()
                        .Where(_ => _.GetCustomAttribute(typeof(SqlKataIgnoreAttribute)) == null);

            foreach (var item in props)
            {
                var attr = item.GetCustomAttribute(typeof(SqlKataColumn)) as SqlKataColumn;
                if (attr != null)
                {
                    dictionary.Add(attr.Name, item.GetValue(data));
                }
                else
                {
                    dictionary.Add(item.Name, item.GetValue(data));
                }
            }

            return AsInsert(dictionary, returnId);
        }

As a said is very simple and I added a couple of test in order to make sure it works

1

Test for Update


  private class Book
        {
            public Book(string name, string author, decimal price = 1.0m, string color = null)
            {
                this.Name = name ?? throw new ArgumentNullException("name must be provided");
                this.BookPrice = price;
                this.color = color;
                this.BookAuthor = author;
            }

            public string Name { get; set; }
            [SqlKataColumn("Author")]
            public string BookAuthor { get; set; }
            [SqlKataColumn("Price")]
            public decimal BookPrice { get; set; }
            [SqlKataIgnore]
            public string color { get; set; }
        }

        [Fact]
        public void UpdateWithIgnoreAndColumnProperties()
        {
            var account = new Book(name: $"SqlKataBook", author: "Kata", color: $"red", price: 100m);
            var query = new Query("Book").AsInsert(account);

            var c = Compile(query);

            Assert.Equal(
                "INSERT INTO [Book] ([Name], [Author], [Price]) VALUES ('SqlKataBook', 'Kata', 100)",
                c[EngineCodes.SqlServer]);
        }

Test for Insert

  private class Account
        {
            public Account(string name, string currency = null, string created_at = null, string color = null)
            {
                this.name = name ?? throw new ArgumentNullException("name must be provided");
                this.Currency = currency;
                this.color = color;
            }

            public string name { get; set; }
            [SqlKataColumn("currency_id")]
            public string Currency { get; set; }
            [SqlKataIgnore]
            public string color { get; set; }
        }

        [Fact]
        public void InsertWithIgnoreAndColumnProperties()
        {
            var account = new Account(name: $"popular", color: $"blue", currency: "US");
            var query = new Query("Account").AsInsert(account);

            var c = Compile(query);

            Assert.Equal(
                "INSERT INTO [Account] ([name], [currency_id]) VALUES ('popular', 'US')",
                c[EngineCodes.SqlServer]);
        }

mota57 avatar Apr 19 '19 02:04 mota57

@ahmad-moussawi I dont even know how to add a pull request what should I do?

mota57 avatar Apr 19 '19 04:04 mota57

hello?

mota57 avatar Apr 20 '19 13:04 mota57

@mota57 thanks!, but I think keeping the attributes name shorter (Column and Ignore) could be much simpler for developers. and please add some unit tests, check this https://github.com/sqlkata/querybuilder/blob/master/QueryBuilder.Tests/InsertTests.cs to get an idea.

From your side you have to create a new branch on your local git something like

git checkout -b annotations

Do your changes then commit and push back to the remote repo, or you can follow a simpler approach mentioned here https://help.github.com/en/desktop/contributing-to-projects/creating-a-pull-request

ahmad-moussawi avatar Apr 22 '19 03:04 ahmad-moussawi

I am trying to push it and I can't because I dont have permission.

image

I also generate a ssh key then I found an alternative solution on stackoverflow (the second answer) but still this is the results that I get. image

Also I clone another project, move the code there and create the branch from scratch then attempt to push the code and I have kind of the same error.

image

mota57 avatar Apr 23 '19 02:04 mota57

@ahmad-moussawi ready.

mota57 avatar Apr 24 '19 01:04 mota57

@ahmad-moussawi FWIW I think [Ignore] and [Column] are too generic; another developer will see those and not have a clue they are provided by SqlKata (or are related to the database at all).

Specifically in the case of "Ignore" I think it would be sufficient if the provided column list didn't include the properties we wanted to ignore. For example I have a class with 3 properties but only 2 are written to the table. If I provide a column list with the 2 properties I want to write and then the object (or list of objects for "insert many") it should ignore the missing ones by default. Then we don't need decorators all over the classes, nor to throw an exception as is currently done in that case ("columns should be equal to values count").

k3davis avatar May 09 '19 19:05 k3davis

@Mota57 I suggest using System.ComponentModel.DataAnnotations.Schema.Column instead of creating a new attribute that does the same thing with the same name. Aside from reducing code, this would allow you to use the same annotation for sqlkata and dapper mapping.

PhilConnor avatar Nov 22 '19 19:11 PhilConnor

@PhilConnor The suggestion should be for the owner of the repository for @ahmad-moussawi

mota57 avatar Nov 25 '19 13:11 mota57

Now, ColumnAtribute from SqlKata not yet support SELECT?

azhe403 avatar May 09 '21 22:05 azhe403

Any news on this? Is it not supported for selects and is this intended? What is the proposed workaround?

denisulmer avatar Jul 13 '21 16:07 denisulmer