none
Interface, Abstract Class, or let it be as it is for now RRS feed

  • Question

  • After reading this post (I saw it on a member of this forum, nice post btw), I realized that maybe be I'm using an interface while I shouldn't.

    He is my code:

        public interface IDAOClients
        {
            List<Clients> GetAll();
            void InsertClients(Clients clientes);
            void UpdateClientes(Clients clientes);
            void DeleteClientes(Clients clientes);
        }
    
       public interface IDAOWorkers
        {
            List<Workers> GetAll();
            void InsertWorker(Workers funcionarios);
            void UpdateWorker(Workers funcionarios);
            void DeleteWorker(Workers funcionarios);
        }
    
    
      public interface IDAOClothes
        {
            List<Clothes> GetAll();
            void InsertClothes(Clothes roupas);
            void UpdateClothes(Clothes roupas);
            void DeleteClothes(Clothes roupas);
        }
    
      public interface IDAOColors
        {
            List<Colors> GetAll();
            void InsertColors(Colors cores);
            void UpdateColors(Colors cores);
            void DeleteColors(Colors cores);
        }
    He is my code part 2:
    public class DAOClients: IDAOClients
        {
           public List<Clients> GetAll() { //... }
           public void InsertClients(Clients clientes) { //...} 
           public void UpdateClientes(Clients clientes) { //...}
           public void DeleteClientes(Clients clientes) { //...}
        }
    
       public class DAOWorkers: IDAOWorkers
        {
    public List<Workers> GetAll() {}
            public void InsertWorker(Workers funcionarios) {}
            public void UpdateWorker(Workers funcionarios){}
           public void DeleteWorker(Workers funcionarios){}
        }
    
    
      public class DAOClothes: IDAOClothes
        {
           public List<Clothes> GetAll() {}
           public void InsertClothes(Clothes roupas){}
           public void UpdateClothes(Clothes roupas){}
           public void DeleteClothes(Clothes roupas){}
        }
    
      public class DAOColors: IDAOColors
        {
           public List<Colors> GetAll() {}
           public void InsertColors(Colors cores){}
           public void UpdateColors(Colors cores){}
           public void DeleteColors(Colors cores){}
        }

    That's only the basic operation they got.
    As you may see, I have a lot of repeated method names, the only difference is that they make reference to a different class.

    I did this all interface thing because I wanted to separated the and only have one reason to change the code for each class/interface..., I also wanted to be prepared in case I changed database type even knowing that I probably won't. Something that I saw here, made me realized that maybe I'm wrong.

    I wanted to make an interface with all those basic CRUD and make all then inherit from it, and for those methods particular for each class, I wanted to write singularly for each class that may need. Something like this:

    //temp name
    Interface CRUD{
    Insert();
    Update();
    Delete();
    
    }
    
    //I know it doesn't work this way, but...
    Clients,Clothes,Workers,Colors:CRUD{
    
    }
    
    //and if one need another singular method
    //I would just add on clients
    Clients(){
    
    string GetName();
    }

    The problem is... That I cannot know which class they will instantiate, I cannot make sure that they instantiate just a class
    It must be the right one (or I think it must), because each class has it's own fields/properties.

    Abstract Class, I also thought on make this supposed single Interface an abstract class, then I could change whenever I want. But if a method was declared as abstract the class that inherit the main class, will also need to inherit, but I could declare abstract methods carefully.

    But now I don't know what to do, I don't have that much experience using abstract classes, I normally use interfaces, they are contract, well... Then I just need to implement it all.
    I have the POCOs, the Interfaces and the Data Access Object, when I need to change something, most of the time I only change for one reason. If I change the IDAOClients, is just to add more code related to the DB on the client and no more.

    Since I was coding the interfaces thinking on SRPWikiPedia, SRPHackernoon, "The SRP is about limiting the impact of change", I have a lot of code, maybe I'm over reacting of this SRP thing, but I feel like it's working.
    I someone need to work on Worker's DAO class, he wouldn't need to be worried about nothing but that.

    But the "What Is Wrong With My Interface" made me realized that  maybe there's a better way to do all that.
    At least if I could put all those CRUD on a single class/interface and make the user instantiated the right class, I think it would remove a lot of possible duplicates piece of code.

    -


    BP-LP 2005/2016 @ll rights reserved




    Wednesday, July 10, 2019 7:59 AM

Answers

  • Greetings again Belarmino.

    I think you do, in fact, want unrelated classes to implement the same interface. The classes Workers, Colours, Clients, and so on all have the same methods, but those methods behave differently. This is where interfaces come in.

    Here's an example. Any class that implements the interface can go in the list, and the Add methods can be completely different for each class.

    using System;
    using System.Collections.Generic;
    
    
    namespace NamespaceX
    {
       class Program
       {
          static void Main(string[] args)
          {
             // We can have a list of workers.
             MyList<Workers> workers = new MyList<Workers>();
             workers.Add(new Workers("Fred"));
             workers.Add(new Workers("Joe"));
             workers.Add(new Workers("Sam"));
    
             // And a list of colours.
             MyList<Colors> colours = new MyList<Colors>();
             colours.Add(new Colors("Red"));
             colours.Add(new Colors("Purple"));
             colours.Add(new Colors("Pink"));
             colours.Add(new Colors("Taupe"));
    
             // We can even mix up classes in a single list, provided they implement our interface.
             MyList<IMyInterface> alltogether = new MyList<IMyInterface>();
             alltogether.Add(new Workers("Phil"));
             alltogether.Add(new Colors("Magenta"));
    
             // And they all give us the correct queries.
             workers.InsertAll();
             colours.InsertAll();
             alltogether.InsertAll();
    
          }
    
          public interface IMyInterface
          {
             void Add();
          }
    
          public class Workers : IMyInterface
          {
             string FullName;
    
             public Workers(string name)
             {
                FullName = name;
             }
    
             public void Add()
             {
                string query = "INSERT INTO `CIEPA`.`Workers`" +
                     "(`fullName`" +
                     $"VALUES('{FullName})";
    
                Console.WriteLine(query);
    
             }
          }
    
          public class Colors : IMyInterface
          {
             string Color;
    
             public Colors(string color)
             {
                Color = color;
             }
    
             public void Add()
             {
                string query = "INSERT INTO `CIEPA`.`Colors`" +
                     "(`color`" +
                     $"VALUES('{Color})";
    
                Console.WriteLine(query);
             }
          }
    
          public class MyList<T> : List<T> where T : IMyInterface
          {
             public void InsertAll()
             {
                foreach (T t in this)
                {
                   t.Add();
                }
                Console.WriteLine(""); // Blank line to separate the lists on screen.
             }
          }
       }
    }
    

    Friday, July 12, 2019 12:08 AM

All replies

  • You are trying to use the DAO pattern, which is well known in the Java world that uses Interfaces,   but  it is not well known  in the .NET world, becuase .NET came well after Java. The .NET world is stuck on the generic repository pattern, and the Repository pattern is being misused and is limited in scope from a generic standpoint, which is a domain pattern and not a persistence pattern.

    https://www.tutorialspoint.com/design_pattern/data_access_object_pattern.htm

    https://programmingwithmosh.com/net/common-mistakes-with-the-repository-pattern/

    The usage of an Interface is about loose coupling and communications between objects.

    https://www.c-sharpcorner.com/blogs/understanding-interfaces-via-loose-coupling-and-tight-coupling

    I have seen abstract class used to control the development of derived class, like the derived class could only have the Insert, Update, GetAll, Find (Find by Id) and Delete that uses the Override to override the methods in the abstract class so that the developer can place the needed code functionality for a particular derived class. The abstract class had no code in its methods, and it is just a blueprint of the methods the derived class has available to it.

    The derived class could have low-level debase logic to  access the PayRoll table, another derived class for Inventory table, etc, and etc. But the developer that is developing the derived class adheres to the blueprint abstract class and the expected methods to use. The developer can put any code in the override method as needed in a derived class, but the developer has the blueprint of the expected methods to be used that the abstract class has defined. The developer may or may not use all of the methods defined by the abstract class in the derived class.

    That's how I have seen the abstract class used.

    For the DAO pattern,  I show the example on how a DAO class uses the methods of another DOA to do something on the behalf of the calling DAO. 

    The architecture of the solution is 

    1) Presentation Layer using ASP.NET MVC

    2) Service Layer

    3) ASP.NET WebAPI

    4) Data Access Layer

    The DTO travels between the layers. The DTO travels between DAO(s). The DTO travels between the WebAPI client objects in the Service Layer and the WebAPI service.

    https://www.codeproject.com/articles/1050468/data-transfer-object-design-pattern-in-csharp

    If you ever get into dependency injection and inversion of control (IoC)  or unit testing using mocked objects, then the Interface implementation on a class comes into play.

    using DAL.Models;
    using System;
    using System.Threading.Tasks;
    using Entities;
    using System.Collections.Generic;
    using Microsoft.EntityFrameworkCore;
    using System.Data.SqlClient;
    using System.Linq;
    
    namespace DAL
    {
        public class DaoPayroll :IDaoPayroll
        {
            private PublishingCompanyContext pc;
            private IDaoAuthor _daoauthor;
    
            public DaoPayroll(PublishingCompanyContext dbcontext, IDaoAuthor daoAuthor)
            {
                pc = dbcontext;
                _daoauthor = daoAuthor;
            }
    
            public async Task<List<DtoPayroll>> GetAll()
            {
                var dtos = new List<DtoPayroll>();
    
                var payrolls = await pc.Payroll.ToListAsync();
    
                foreach (var payroll in payrolls)
                {
                    var dtoauthor = await _daoauthor.Find(payroll.AuthorId); 
                    var dto = new DtoPayroll
                    {
                        PayrollId = payroll.PayrollId,
                        AuthorId = payroll.AuthorId,
                        AuthorFirstName = dtoauthor.FirstName,
                        AuthorLastName = dtoauthor.LastName,
                        Salary = payroll.Salary
                    };
    
                    dtos.Add(dto);
                }
    
                return dtos;
            }
    
            public async Task<DtoPayroll> Find(int id)
            {
                var dto = new DtoPayroll();
    
                var payroll = await pc.Payroll.FindAsync(id);
    
                if (payroll != null)
                { 
                    var dtoauthor = await _daoauthor.Find(payroll.AuthorId);
    
                    if (dtoauthor != null)
                    {
                        dto.PayrollId = payroll.PayrollId;
                        dto.AuthorId = payroll.AuthorId;
                        dto.AuthorFirstName = dtoauthor.FirstName;
                        dto.AuthorLastName = dtoauthor.LastName;
                        dto.Salary = payroll.Salary;
                    }
                    else
                    {
                        throw new Exception($"Author with ID = {id} was not found.");
                    }
                }
                else
                {
                    throw new Exception($"Payroll with ID = {id} was not found.");
                }
    
                return dto;
    
            }
    
            public async Task<DtoPayroll> FindPayRollByAuthorId(int id)
            {
                var dto = new DtoPayroll();
                
                var payroll = await pc.Payroll.Where(a =>a.AuthorId == id).SingleOrDefaultAsync();
    
                if (payroll != null)
                {
                    dto.PayrollId = payroll.PayrollId;
                }
    
                return dto;
            }
    
            public async Task Add(DtoPayroll dto)
            {
                var payroll = new Payroll
                {
                    AuthorId = dto.AuthorId,
                    Salary = dto.Salary
                };
    
                pc.Payroll.Add(payroll);
                await pc.SaveChangesAsync();
    
            }
    
            public async Task Update(DtoPayroll dto)
            {
                var payroll = new Payroll
                {
                    PayrollId = dto.PayrollId,
                    AuthorId = dto.AuthorId,
                    Salary = dto.Salary
                };
    
                pc.Entry(payroll).State = EntityState.Modified;
                await pc.SaveChangesAsync();
    
            }
    
            public async Task Delete(int id)
            {
                var payroll =  pc.Payroll.Find(id);
    
                if (payroll != null)
                {
                    pc.Payroll.Remove(payroll);
                    await pc.SaveChangesAsync();
                }
            }
    
        }
    }
    

    Wednesday, July 10, 2019 9:21 AM

  • For the DAO pattern,  I show the example on how a DAO class uses the methods of another DOA to do something on the behalf of the calling DAO. 

    Can I get your payroll project?

    BP-LP 2005/2016 @ll rights reserved

    Wednesday, July 10, 2019 1:34 PM

  • I have seen abstract class used to control the development of derived class, like the derived class could only have the Insert, Update, GetAll, Find (Find by Id) and Delete that uses the Override to override the methods in the abstract class so that the developer can place the needed code functionality for a particular derived class. The abstract class had no code in its methods, and it is just a blueprint of the methods the derived class has available to it.

    The derived class could have low-level debase logic to  access the PayRoll table, another derived class for Inventory table, etc, and etc. But the developer that is developing the derived class adheres to the blueprint abstract class and the expected methods to use. The developer can put any code in the override method as needed in a derived class, but the developer has the blueprint of the expected methods to be used that the abstract class has defined. The developer may or may not use all of the methods defined by the abstract class in the derived class.

    That's how I have seen the abstract class used.


    So I can make something like this using an abstract class to reduce the interfaces I have with almost the same methods?

    //temp name
    Interface CRUD{
    Insert();
    Update();
    Delete();
    
    }
    
    //I know it doesn't work this way, but...
    Clients,Clothes,Workers,Colors:CRUD{
    
    }
    
    //and if one need another singular method
    //I would just add on clients
    Clients(){
    
    string GetName();
    }

    BP-LP 2005/2016 @ll rights reserved

    Wednesday, July 10, 2019 1:36 PM
  • This is how I have seen an abstract class work. It's just a loose example, simple. 

    using System;
    using Entities;
    using DataAccessLayer;
    
    namespace ServiceLayer
    {
        public class Service : IService
        {
            public void Save(DtoProduct dto)
            {
                new DaoInventory().Add();
                new DaoSales().Find();
            }
        }
    }

    namespace DataAccessLayer
    {
        public abstract class AbstractDAO
        {
            public abstract void GetAll();
            public abstract void Find();
            public abstract void Add();
            public abstract void Update();
            public abstract void Delete();
        }
    }
    

    using System;
    
    namespace DataAccessLayer
    {
        public class DaoInventory : AbstractDAO
        {
            public override void GetAll()
            {
                throw new NotImplementedException();
            }
    
            public override void Find()
            {
                throw new NotImplementedException();
            }
    
            public override void Add()
            {
                throw new NotImplementedException();
            }
    
            public override void Update()
            {
                throw new NotImplementedException();
            }
    
            public override void Delete()
            {
                throw new NotImplementedException();
            }
        }
    }
    

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DataAccessLayer
    {
        public class DaoSales : AbstractDAO
        {
            public override void GetAll()
            {
                throw new NotImplementedException();
            }
    
            public override void Find()
            {
                throw new NotImplementedException();
            }
    
            public override void Add()
            {
                throw new NotImplementedException();
            }
    
            public override void Update()
            {
                throw new NotImplementedException();
            }
    
            public override void Delete()
            {
                throw new NotImplementedException();
            }
        }
    }
    

    Wednesday, July 10, 2019 5:11 PM

  • For the DAO pattern,  I show the example on how a DAO class uses the methods of another DOA to do something on the behalf of the calling DAO. 

    Can I get your payroll project?

    BP-LP 2005/2016 @ll rights reserved

    I'll see if I can get the little project out on GitHub, which is based off of the link, just scroll down past the warnings stuff.

    http://www.vbforums.com/showthread.php?540421-Tutorial-An-Introduction-to-the-ADO-NET-Entity-Framework

    Thursday, July 11, 2019 12:08 AM
  • Greetings Belarmino.

    Aren't you over-complicating things a bit? This looks to me like a job for generics. You could make one generic class that handles all the types you want to add to a list.

    Here's a short sample. It uses a generic class called MyList, which I think is along the lines of what you are trying to do. The class has two methods - Insert and Output - but in your case it would have Update and Delete and so on. The sample uses the class for strings and integers, but it would work for your Client, Color and so on.

    using System;
    using System.Collections.Generic;
    
    
    
    namespace NamespaceX
    {
       class Program
       {
          static void Main(string[] args)
          {
             MyList<string> listStrings = new MyList<string>();
             listStrings.Insert("Apple");
             listStrings.Insert("Pear");
             listStrings.Insert("Orange");
    
             MyList<int> listInts = new MyList<int>();
             listInts.Insert(100);
             listInts.Insert(99);
             listInts.Insert(98);
             listInts.Insert(97);
    
             listStrings.Output();
             listInts.Output();
          }
    
          public class MyList<T> : List<T>
          {
             public void Insert(T t)
             {
                Add(t);
             }
    
             public void Output()
             {
                foreach (T t in this)
                {
                   Console.WriteLine(t.ToString());
                }
             }
          }
       }
    }
    

    The output is like so.

    Thursday, July 11, 2019 1:05 AM
  • Greetings Belarmino.

    Aren't you over-complicating things a bit? This looks to me like a job for generics. You could make one generic class that handles all the types you want to add to a list.

    Here's a short sample. It uses a generic class called MyList, which I think is along the lines of what you are trying to do. The class has two methods - Insert and Output - but in your case it would have Update and Delete and so on. The sample uses the class for strings and integers, but it would work for your Client, Color and so on.

    using System;
    using System.Collections.Generic;
    
    
    
    namespace NamespaceX
    {
       class Program
       {
          static void Main(string[] args)
          {
             MyList<string> listStrings = new MyList<string>();
             listStrings.Insert("Apple");
             listStrings.Insert("Pear");
             listStrings.Insert("Orange");
    
             MyList<int> listInts = new MyList<int>();
             listInts.Insert(100);
             listInts.Insert(99);
             listInts.Insert(98);
             listInts.Insert(97);
    
             listStrings.Output();
             listInts.Output();
          }
    
          public class MyList<T> : List<T>
          {
             public void Insert(T t)
             {
                Add(t);
             }
    
             public void Output()
             {
                foreach (T t in this)
                {
                   Console.WriteLine(t.ToString());
                }
             }
          }
       }
    }

    The output is like so.

    I thought on that, but the problem is that it uses persistence.

    And I'm using a string for the query where I pass the Entity. Since they are different, I it wouldn't work for each insert/update/delete

    BP-LP 2005/2016 @ll rights reserved

    Thursday, July 11, 2019 6:08 AM
  • This is how I have seen an abstract class work. It's just a loose example, simple. 

    using System;
    using Entities;
    using DataAccessLayer;
    
    namespace ServiceLayer
    {
        public class Service : IService
        {
            public void Save(DtoProduct dto)
            {
                new DaoInventory().Add();
                new DaoSales().Find();
            }
        }
    }

    namespace DataAccessLayer
    {
        public abstract class AbstractDAO
        {
            public abstract void GetAll();
            public abstract void Find();
            public abstract void Add();
            public abstract void Update();
            public abstract void Delete();
        }
    }

    using System;
    
    namespace DataAccessLayer
    {
        public class DaoInventory : AbstractDAO
        {
            public override void GetAll()
            {
                throw new NotImplementedException();
            }
    
            public override void Find()
            {
                throw new NotImplementedException();
            }
    
            public override void Add()
            {
                throw new NotImplementedException();
            }
    
            public override void Update()
            {
                throw new NotImplementedException();
            }
    
            public override void Delete()
            {
                throw new NotImplementedException();
            }
        }
    }

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace DataAccessLayer
    {
        public class DaoSales : AbstractDAO
        {
            public override void GetAll()
            {
                throw new NotImplementedException();
            }
    
            public override void Find()
            {
                throw new NotImplementedException();
            }
    
            public override void Add()
            {
                throw new NotImplementedException();
            }
    
            public override void Update()
            {
                throw new NotImplementedException();
            }
    
            public override void Delete()
            {
                throw new NotImplementedException();
            }
        }
    }

    Yeah, something like that. But my problem is that the add method isn't really obvious, it just say Add();
    My actual Add, says Add(Entity1 entity); Then the user know that he needs to pass the entity one, 
    I would like to have some abstract class that enforces the user to pass an specific entity, but somehow to all.
    When it's Workers, must be passed only workers, when it Clients, only clients... 
    Something like that, is that possible with only one abstract class?

    BP-LP 2005/2016 @ll rights reserved

    Thursday, July 11, 2019 6:11 AM
  • What query? I don't see how the insert/update/delete are different. And what does persistence have to do with it?

    I suppose you know what you're doing, so if you say it won't work, I'll take your word for it. But everything you have said so far still sounds to me like generics are the answer.

    Thursday, July 11, 2019 6:39 AM
  • What query? I don't see how the insert/update/delete are different. And what does persistence have to do with it?

    I suppose you know what you're doing, so if you say it won't work, I'll take your word for it. But everything you have said so far still sounds to me like generics are the answer.

     public void AddWorkers(Workers worker)
            {
    
    
    
                string query = "INSERT INTO `CIEPA`.`Workers`" +
                     "(`fullName`"+
                     $"VALUES('{worker.FullName})";
    
    
    
                MySqlCommand comando = new MySqlCommand(query, conexao);
    
                try
                {
                  
                    conexao.Close();
                    conexao.Open();
                    var result = comando.ExecuteNonQuery();
                    if (result>= 1)
                    {
                        MessageBox.Show("Worker Added", "Sucess",
                            MessageBoxButtons.OK, MessageBoxIcon.Information);
    
                    }
    
                }
                catch (Exception ex)
                {
    
                    MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                finally
                {
                    conexao.Close();
                }
    
            }
    
     public void AddColors(Colors color)
            {
    
    
    
                string query = "INSERT INTO `CIEPA`.`Colors`" +
                     "(`color`"+
                     $"VALUES('{color.Color})";
    
    
    
                MySqlCommand comando = new MySqlCommand(query, conexao);
    
                try
                {
                  
                    conexao.Close();
                    conexao.Open();
                    var result = comando.ExecuteNonQuery();
                    if (result>= 1)
                    {
                        MessageBox.Show("Color Added", "Sucess",
                            MessageBoxButtons.OK, MessageBoxIcon.Information);
    
                    }
    
                }
                catch (Exception ex)
                {
    
                    MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
                finally
                {
                    conexao.Close();
                }
    
            }

    I can't add generic while the query is different.
    Or there's a way and I don't know

    PS: I tried to use EF with MySQL it wasn't working, so I stick to this, my team mates also say this way is easier for them.


    BP-LP 2005/2016 @ll rights reserved

    Thursday, July 11, 2019 7:56 AM
  • Yeah, something like that. But my problem is that the add method isn't really obvious, it just say Add();
    My actual Add, says Add(Entity1 entity); Then the user know that he needs to pass the entity one

    Like I said, it was just a simple example.

    The method signature in an abstract class can have any type or collections of types  passed into or return out of the method, and it implements the method's signature into a derived class that must be adhered to by code that has been placed in the overridden method by the developer.

     So, if the below method was placed into an abstract class, the derived class's method signature would be the method that is implanted in the derived class.

    public void AddWorkers(Workers worker);

    The implementation...

    public override void AddWorkers(Workers worker) { // The developer can create any code needed, but the final situation is the developer must create code that is working with the method's signature. }


    I would like to have some abstract class that enforces the user to pass an specific entity, but somehow to all.

    When it's Workers, must be passed only workers, when it Clients, only clients... 
    Something like that, is that possible with only one abstract class?

    The example shows the power of an abstract class. If the developer tries to change the method's signature  away from the abstract class' implementation, the implementation in the derived class is broken resulting in a compile error


    • Edited by DA924x Thursday, July 11, 2019 10:29 AM
    Thursday, July 11, 2019 10:28 AM
  • I can't add generic while the query is different.
    Or there's a way and I don't know

    But you can use 'object' as a retuned type or passed in type for a method's signature,  and then cast the 'Object' to a known type. The below code shows the concept. The code in the MVC controller casts the object back to the view model type it needs to work with  in the controller's method and the method being called cats the object it needs to work with and then sands it back as 'Object'.

    Yes 'Object' can be used as a container for all objects, becuase 'object' is the base object for all objects in .NET.

    PS: I tried to use EF with MySQL it wasn't working, so I stick to this, my team mates also say this way is easier for them.

    The problem I see with your code is that you're not using parametrized inline T-SQL to prevent SQL Injection attack. 

    Also using the ADO.NET Entity Framework prevents SQL Injection attack too.

    You can use the micro ORM Dapper. Dapper works with MySQL that allows the developer to use T-SQL or a stored procedure instead of using  Linq-2-Entities and EF., which is faster than EF and is as fast as using ADO.NET with SQL Command objects and inline T-SQL or a sproc.

    https://exceptionnotfound.net/dapper-vs-entity-framework-vs-ado-net-performance-benchmarking/

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.DataAnnotations;
    using Microsoft.AspNetCore.Mvc.Rendering;
    
    namespace ProgMgmntCore2UserIdentity.Models
    {
        public class TaskViewModels
        {
            public class TaskCreate
            {
                public int TaskId { get; set; }
    
                [Required(ErrorMessage = "Task Name is required")]
                [StringLength(50)]
                public string TaskName { get; set; }
                [Required(ErrorMessage = "Note is required")]
                [StringLength(2000)]
                public string Note { get; set; }
    
                [Required(ErrorMessage = "Start Date is required")]
                [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM-dd-yyyy hh:mm:ss tt}")]
                public DateTime? StartDate { get; set; }
                [Required(ErrorMessage = "Resource is required")]
                public string ResourceId { get; set; }
                public int ProjectId { get; set; }
                [Required(ErrorMessage = "Duration is required")]
                public string TaskDuration { get; set; }
                [Required(ErrorMessage = "Status is required")]
                public string Status { get; set; }
    
                public List<SelectListItem> Statuses { get; set; }
                public List<SelectListItem> Durations { get; set; }
                public List<SelectListItem> Resources { get; set; }
            }
    
            public class TaskEdit
            {
                public int TaskId { get; set; }
    
                [Required(ErrorMessage = "Task Name is required")]
                [StringLength(50)]
                public string TaskName { get; set; }
                [Required(ErrorMessage = "Note is required")]
                [StringLength(2000)]
                public string Note { get; set; }
    
                [Required(ErrorMessage = "Start Date is required")]
                [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM-dd-yyyy hh:mm:ss tt}")]
                public DateTime? StartDate { get; set; }
               
                [DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM-dd-yyyy hh:mm:ss tt}")]
                public DateTime? EndDate { get; set; }
    
                public string ResourceId { get; set; }
                public int ProjectId { get; set; }
                public string TaskDuration { get; set; }
                public string TaskSpent { get; set; }
                public string Status { get; set; }
    
                public List<SelectListItem> Statuses { get; set; }
                public List<SelectListItem> Durations { get; set; }
                public List<SelectListItem> Resources { get; set; }
                public List<SelectListItem> Spents { get; set; }
            }
            public List<TaskEdit> Tasks { get; set; }
        }
    }


     [Authorize]
            public ActionResult Create()
            {
                return View(_taskModel.Create());
            }
    
            [Authorize]
            [HttpPost]
            public ActionResult Create(TaskViewModels.TaskCreate task, string submit)
            {
                if (submit == "Cancel") return RedirectToAction("Index", new { id = (int)TempData["ProjectId"]});
    
                ValidateddlStatuses();
                ValidateddlDurations();
                ValidateddlResources();
               
                task.Status = (Request.Form["ddlStatusTypes"]);
                task.TaskDuration = (Request.Form["ddlDurations"]);
                task.ResourceId = (Request.Form["ddlResources"]);
    
                if (!ModelState.IsValid) return View((TaskViewModels.TaskCreate)_taskModel.PopulateDropDownLists(task, "Create"));
    
                _taskModel.Create(task, (int)TempData["ProjectId"]);
                return RedirectToAction("Index", new { id = (int)TempData["ProjectId"] });
            }
    
            [Authorize]
            public ActionResult Edit(int id = 0)
            {
                return id == 0 ? null : View(_taskModel.Edit(id));
            }
    
            [Authorize]
            [HttpPost]
            public ActionResult Edit(TaskViewModels.TaskEdit task, string submit)
            {
                if (submit == "Cancel") return RedirectToAction("Index", new { id = (int)TempData["ProjectId"]});
    
                if (ModelState.IsValid && _modelHelper.IsEndDateLessThanStartDate(task, "Task"))
                {
                    ModelState.AddModelError(String.Empty, "End Date cannot be less than Start Date.");
                }
    
                if (!ModelState.IsValid) return View( (TaskViewModels.TaskEdit)_taskModel.PopulateDropDownLists(task, "Edit"));
    
                var thetask = new TaskViewModels.TaskEdit();
    
                thetask = task;
    
                thetask.ProjectId = (int) TempData["ProjectId"];
    
                _taskModel.Edit(thetask);
                return RedirectToAction("Index", new { id = (int)TempData["ProjectId"]});
            }

     public Object PopulateDropDownLists(object task, string type)
            {
                TaskViewModels.TaskEdit taskedit;
                TaskViewModels.TaskCreate taskcreate;
                Object obj;
    
                bool isExist = _memoryCache.TryGetValue("DtoCache", out DtoCache dtocache);
    
                if (!isExist)
                {
                    dtocache = _webApi.GetCacheApi();
    
                    var cacheEntryOptions = new MemoryCacheEntryOptions()
                        .SetSlidingExpiration(TimeSpan.FromSeconds(30));
    
                    _memoryCache.Set("DtoCache", dtocache, cacheEntryOptions);
                }
    
                if (type == "Create")
                {
                    taskcreate = (TaskViewModels.TaskCreate) task;
    
                    taskcreate.Statuses = new List<SelectListItem>();
    
                    foreach (var st in dtocache.Statuses)
                    {
                        var sli = new SelectListItem { Value = st.Value, Text = st.Text };
                        taskcreate.Statuses.Add(sli);
                    }
    
                    var selectedtask = (from a in taskcreate.Statuses.Where(a => a.Value == taskcreate.Status) select a)
                        .SingleOrDefault();
    
                    if (selectedtask != null)
                        selectedtask.Selected = true;
    
                    taskcreate.Durations = new List<SelectListItem>();
    
                    foreach (var du in dtocache.Durations)
                    {
                        var sli = new SelectListItem { Value = du.Value, Text = du.Text };
                        taskcreate.Durations.Add(sli);
                    }
    
                    if (taskcreate.TaskDuration != null)
                    {
                        var selectedduration = (from a in taskcreate.Durations.Where(a => a.Value == taskcreate.TaskDuration) select a)
                            .SingleOrDefault();
    
                        if (selectedduration != null)
                            selectedduration.Selected = true;
                    }
    
                    taskcreate.Resources = new List<SelectListItem>();
    
                    foreach (var rs in dtocache.Resources)
                    {
                        var sli = new SelectListItem { Value = rs.Value, Text = rs.Text };
                        taskcreate.Resources.Add(sli);
                    }
    
                    if (taskcreate.ResourceId != null)
                    {
                        var selectedresource = (from a in taskcreate.Resources.Where(a => a.Value == taskcreate.ResourceId) select a)
                            .SingleOrDefault();
    
                        if (selectedresource != null)
                            selectedresource.Selected = true;
                    }
    
                    obj = taskcreate;
                }
                else
                {
                    taskedit = (TaskViewModels.TaskEdit) task;
    
                    taskedit.Statuses = new List<SelectListItem>();
    
                    foreach (var st in dtocache.Statuses)
                    {
                        var sli = new SelectListItem { Value = st.Value, Text = st.Text };
                        taskedit.Statuses.Add(sli);
                    }
                  
                    var selectedtask = (from a in taskedit.Statuses.Where(a => a.Value == taskedit.Status) select a)
                        .SingleOrDefault();
    
                    if (selectedtask != null)
                        selectedtask.Selected = true;
    
                    taskedit.Durations = new List<SelectListItem>();
    
                    foreach (var du in dtocache.Durations)
                    {
                        var sli = new SelectListItem { Value = du.Value, Text = du.Text };
                        taskedit.Durations.Add(sli);
                    }
                    
                    if (taskedit.TaskDuration != null)
                    {
                        var selectedduration = (from a in taskedit.Durations.Where(a => a.Value == taskedit.TaskDuration) select a)
                            .SingleOrDefault();
    
                        if (selectedduration != null)
                            selectedduration.Selected = true;
                    }
    
                    taskedit.Spents = new List<SelectListItem>();
    
                    foreach (var du in dtocache.Durations)
                    {
                        var sli = new SelectListItem { Value = du.Value, Text = du.Text };
                        taskedit.Spents.Add(sli);
                    }
    
                    if (taskedit.TaskSpent != null)
                    {
                        var selectedduration = (from a in taskedit.Spents.Where(a => a.Value == taskedit.TaskSpent) select a)
                            .SingleOrDefault();
    
                        if (selectedduration != null)
                            selectedduration.Selected = true;
                    }
    
                    taskedit.Resources = new List<SelectListItem>();
    
                    foreach (var rs in dtocache.Resources)
                    {
                        var sli = new SelectListItem { Value = rs.Value, Text = rs.Text };
                        taskedit.Resources.Add(sli);
                    }
    
                    if (taskedit.ResourceId != null)
                    {
                        var selectedresource = (from a in taskedit.Resources.Where(a => a.Value == taskedit.ResourceId) select a)
                            .SingleOrDefault();
    
                        if (selectedresource != null)
                            selectedresource.Selected = true;
                    }
    
                    obj = taskedit;
                }
        
                return obj;
            }
        }

     

    Thursday, July 11, 2019 11:13 AM
  • I can't add generic while the query is different.
    Or there's a way and I don't know

    But you can use 'object' as a retuned type or passed in type for a method's signature,  and then cast the 'Object' to a known type. The below code shows the concept. The code in the MVC controller casts the object back to the view model type it needs to work with  in the controller's method and the method being called cats the object it needs to work with and then sands it back as 'Object'.

    Yes 'Object' can be used as a container for all objects, becuase 'object' is the base object for all objects in .NET.

    PS: I tried to use EF with MySQL it wasn't working, so I stick to this, my team mates also say this way is easier for them.

    The problem I see with your code is that you're not using parametrized inline T-SQL to prevent SQL Injection attack. 

    Also using the ADO.NET Entity Framework prevents SQL Injection attack too.

    You can use the micro ORM Dapper. Dapper works with MySQL that allows the developer to use T-SQL or a stored procedure instead of using  Linq-2-Entities and EF., which is faster than EF and is as fast as using ADO.NET with SQL Command objects and inline T-SQL or a sproc.

    https://exceptionnotfound.net/dapper-vs-entity-framework-vs-ado-net-performance-

     

    I was using Dapper, but they also said that it was complicated, so I adapted what they had into that.
    Their code was worst then that.

    //Dapper
      public void InserirClients(Clients clientes)
            {
                // Insert
                using (var db = new MySqlConnection(connstring))
                {
                    const string sql = @"INSERT INTO [Clientes] (FullName, Address, Contact1, Contacto2, IdGender) VALUES (@FullName, @Address, @Contact1, @Contact2, @IdGender)";
    
                    db.Execute(sql, new { FullName= clientes.FullName, Address= clientes.Address, Contact1 = clientes.Contact1, Contact2 = clientes.Contact2, IdGender = clientes.IdGender }, commandType: CommandType.Text);
                }
            }
    My code was practically inspired on this one, I thought their were almost the same.
    And it worked and my teammates didn't thought difficult.



    BP-LP 2005/2016 @ll rights reserved

    Thursday, July 11, 2019 11:47 AM
  • I can't add generic while the query is different.
    Or there's a way and I don't know

    But you can use 'object' as a retuned type or passed in type for a method's signature,  and then cast the 'Object' to a known type. The below code shows the concept. The code in the MVC controller casts the object back to the view model type it needs to work with  in the controller's method and the method being called cats the object it needs to work with and then sands it back as 'Object'.

    Yes 'Object' can be used as a container for all objects, becuase 'object' is the base object for all objects in .NET.

    PS: I tried to use EF with MySQL it wasn't working, so I stick to this, my team mates also say this way is easier for them.

    The problem I see with your code is that you're not using parametrized inline T-SQL to prevent SQL Injection attack. 

    Also using the ADO.NET Entity Framework prevents SQL Injection attack too.

    You can use the micro ORM Dapper. Dapper works with MySQL that allows the developer to use T-SQL or a stored procedure instead of using  Linq-2-Entities and EF., which is faster than EF and is as fast as using ADO.NET with SQL Command objects and inline T-SQL or a sproc.

    https://exceptionnotfound.net/dapper-vs-entity-framework-vs-ado-net-performance-

     

    I was using Dapper, but they also said that it was complicated, so I adapted what they had into that.
    Their code was worst then that.

    //Dapper
      public void InserirClients(Clients clientes)
            {
                // Insert
                using (var db = new MySqlConnection(connstring))
                {
                    const string sql = @"INSERT INTO [Clientes] (FullName, Address, Contact1, Contacto2, IdGender) VALUES (@FullName, @Address, @Contact1, @Contact2, @IdGender)";
    
                    db.Execute(sql, new { FullName= clientes.FullName, Address= clientes.Address, Contact1 = clientes.Contact1, Contact2 = clientes.Contact2, IdGender = clientes.IdGender }, commandType: CommandType.Text);
                }
            }
    My code was practically inspired on this one, I thought their were almost the same.
    And it worked and my teammates didn't thought difficult.



    BP-LP 2005/2016 @ll rights reserved

    It looks simple to me, and maybe,  your people need to stop whining, step to the baseball home plate, start swinging  and work with new technology. :)
    Thursday, July 11, 2019 12:15 PM
  • I can't add generic while the query is different.
    Or there's a way and I don't know

    But you can use 'object' as a retuned type or passed in type for a method's signature,  and then cast the 'Object' to a known type. The below code shows the concept. The code in the MVC controller casts the object back to the view model type it needs to work with  in the controller's method and the method being called cats the object it needs to work with and then sands it back as 'Object'.

    Yes 'Object' can be used as a container for all objects, becuase 'object' is the base object for all objects in .NET.


     

    Yeah, you're right.
     public abstract class AbstractClass
        {
            public abstract void Add(object entity);
            //other methods
        }
    
        class MyClass: AbstractClass
        {
    
            public override void Add(object entity ) 
            {
    
                using (var db = new MySqlConnection(connstring))
                {
                    const string sql="";
    
                    db.Execute(sql, new { ...}, commandType: CommandType.Text);
                }
    
            }
    
        }
    
        class MyClass2: AbstractClass
        {
    
            public override void Add(object entity ) 
            {
    
                using (var db = new MySqlConnection(connstring))
                {
                    const string sql="";
    
                    db.Execute(sql, new { ...}, commandType: CommandType.Text);
                }
    
            }
    
        }
    And

     public interface  IBaseCRUD
        {
            void Add(object entity);
            //other methods
        }
    
        class MyClass: IBaseCRUD
        {
    
            public  void Add(object entity ) 
            {
    
                using (var db = new MySqlConnection(connstring))
                {
                    const string sql="";
    
                    db.Execute(sql, new { ...}, commandType: CommandType.Text);
                }
    
            }
    
        }
    
        class MyClass2: IBaseCRUD
        {
    
            public void Add(object entity ) 
            {
    
                using (var db = new MySqlConnection(connstring))
                {
                    const string sql="";
    
                    db.Execute(sql, new { ...}, commandType: CommandType.Text);
                }
    
            }
    
        }

    Which one is preferable on my case?


    BP-LP 2005/2016 @ll rights reserved

    Thursday, July 11, 2019 12:18 PM
  • In the case of the persistence layer,  I have seen architects use the abstract class to enforce consistency, and not let other developers do whatever he or she wanted resulting in clean code. You'll want the data layer solid and consistent. 

     OO is OO no matter the language platform.

    https://dzone.com/articles/when-to-use-abstract-class-and-intreface

    Thursday, July 11, 2019 1:38 PM
  • In the case of the persistence layer,  I have seen architects use the abstract class to enforce consistency, and not let other developers do whatever he or she wanted resulting in clean code. You'll want the data layer solid and consistent. 

     OO is OO no matter the language platform.

    https://dzone.com/articles/when-to-use-abstract-class-and-intreface

    https://dzone.com/articles/when-to-use-abstract-class-and-intreface

    Consider using abstract classes if any of these statements apply to your situation:

    1. You want to share code among several closely related classes.
    2. You expect that classes that extend your abstract class have many common methods or fields or require access modifiers other than public (such as protected and private).
    3. You want to declare non-static or non-final fields. This enables you to define methods that can access and modify the state of the object to which they belong.

    Consider using interfaces if any of these statements apply to your situation:

    1. You expect that unrelated classes would implement your interface. For example, the interfaces Comparable and Cloneable are implemented by many unrelated classes.
    2. You want to specify the behavior of a particular data type, but not concerned about who implements its behavior.
    3. You want to take advantage of multiple inheritances.

    I think I know which to use from this.
    Since I don't want unrelated classes to implement my interface, I will make an abstract class for all common methods that I can think of, and if I find out some methods that duplicate methods that some of them need and others don't, I can create an interface and implement on those who need without altering the abstract class by adding methods that will can or cannot be use.

    am I thinking right on this?

    BP-LP 2005/2016 @ll rights reserved

    Thursday, July 11, 2019 2:02 PM
  • In addition to what the others have written, when you want to use interfaces, the you should look into generics. E.g.

    public interface IDataAccessObject<DataTransferObject>
    	where DataTransferObject : new
    {
    	List<DataTransferObject> GetAll();
    	void Insert(DataTransferObject dataTransferObject);
    	void Update(DataTransferObject dataTransferObject);
    	void Delete(DataTransferObject dataTransferObject);
    }
    
    public interface IDaoClients : IDataAccessObject<Clients>
    public interface IDaoWorkers : IDataAccessObject<Workers>
    public interface IDaoClothes : IDataAccessObject<Clothes>
    public interface IDaoColors : IDataAccessObject<Color>

    Then you may use also an abstract base class and use the template pattern to create the actual instances.

    Thursday, July 11, 2019 2:20 PM
  • In addition to what the others have written, when you want to use interfaces, the you should look into generics. E.g.

    public interface IDataAccessObject<DataTransferObject>
    	where DataTransferObject : new
    {
    	List<DataTransferObject> GetAll();
    	void Insert(DataTransferObject dataTransferObject);
    	void Update(DataTransferObject dataTransferObject);
    	void Delete(DataTransferObject dataTransferObject);
    }
    
    public interface IDaoClients : IDataAccessObject<Clients>
    public interface IDaoWorkers : IDataAccessObject<Workers>
    public interface IDaoClothes : IDataAccessObject<Clothes>
    public interface IDaoColors : IDataAccessObject<Color>

    Then you may use also an abstract base class and use the template pattern to create the actual instances.

    Ohhhhhhh!
    That was what Ante Meridian was saying, generics.
    I didn't know interfaces could have generics.
    Now it makes perfect sense.
    I'm so sorry :( , that's new, I never saw any example with interfaces and Generics

    BP-LP 2005/2016 @ll rights reserved


    Thursday, July 11, 2019 2:30 PM
  • Greetings again Belarmino.

    I think you do, in fact, want unrelated classes to implement the same interface. The classes Workers, Colours, Clients, and so on all have the same methods, but those methods behave differently. This is where interfaces come in.

    Here's an example. Any class that implements the interface can go in the list, and the Add methods can be completely different for each class.

    using System;
    using System.Collections.Generic;
    
    
    namespace NamespaceX
    {
       class Program
       {
          static void Main(string[] args)
          {
             // We can have a list of workers.
             MyList<Workers> workers = new MyList<Workers>();
             workers.Add(new Workers("Fred"));
             workers.Add(new Workers("Joe"));
             workers.Add(new Workers("Sam"));
    
             // And a list of colours.
             MyList<Colors> colours = new MyList<Colors>();
             colours.Add(new Colors("Red"));
             colours.Add(new Colors("Purple"));
             colours.Add(new Colors("Pink"));
             colours.Add(new Colors("Taupe"));
    
             // We can even mix up classes in a single list, provided they implement our interface.
             MyList<IMyInterface> alltogether = new MyList<IMyInterface>();
             alltogether.Add(new Workers("Phil"));
             alltogether.Add(new Colors("Magenta"));
    
             // And they all give us the correct queries.
             workers.InsertAll();
             colours.InsertAll();
             alltogether.InsertAll();
    
          }
    
          public interface IMyInterface
          {
             void Add();
          }
    
          public class Workers : IMyInterface
          {
             string FullName;
    
             public Workers(string name)
             {
                FullName = name;
             }
    
             public void Add()
             {
                string query = "INSERT INTO `CIEPA`.`Workers`" +
                     "(`fullName`" +
                     $"VALUES('{FullName})";
    
                Console.WriteLine(query);
    
             }
          }
    
          public class Colors : IMyInterface
          {
             string Color;
    
             public Colors(string color)
             {
                Color = color;
             }
    
             public void Add()
             {
                string query = "INSERT INTO `CIEPA`.`Colors`" +
                     "(`color`" +
                     $"VALUES('{Color})";
    
                Console.WriteLine(query);
             }
          }
    
          public class MyList<T> : List<T> where T : IMyInterface
          {
             public void InsertAll()
             {
                foreach (T t in this)
                {
                   t.Add();
                }
                Console.WriteLine(""); // Blank line to separate the lists on screen.
             }
          }
       }
    }
    

    Friday, July 12, 2019 12:08 AM
  • @Ante

    If OP's co-developers on the team whined about using EF and Dapper, do you think they are going to go for it if the OP implements it?

    Not saying  that what you are teaching is bad or good one way or the other. I don't think the OP's co-developers on the team can handle it. :)

    Friday, July 12, 2019 1:01 AM
  • Greetings DA924x.

    I still think the OP's approach is too complicated, and Interfaces and generics used as they were intended would make things a lot simpler.

    But maybe it's just me.

    (Actually, I'm fairly sure it's not just me. The code in the original post, with four separate classes implementing four different interfaces is horrible and just screams for correction. But I suppose, ultimately, the OP should implement what's right for him/her no matter how ugly people like me find it.)

    Edit : The end of that last sentence should read "no matter how ugly it is found to be by people like me". Not any alternative interpretation. Obviously.
    • Edited by Ante Meridian Friday, July 12, 2019 1:52 AM Clarification about what is ugly.
    Friday, July 12, 2019 1:13 AM
  • I think the OP is learning. There is nothing wrong in taking advice from co-developers. But if the OP is the lead, then its the OP's show,  and the other developers need to support the OP's decisions.  
    Friday, July 12, 2019 1:43 AM
  • I think the OP is learning. There is nothing wrong in taking advice from co-developers. But if the OP is the lead, then its the OP's show,  and the other developers need to support the OP's decisions.  
    Didn't think about that, I just thought that I should use what they can use, it works and all get happy and I try to make fixes that they can follow.

    BTW: What does OP means?

    BP-LP 2005/2016 @ll rights reserved

    Friday, July 12, 2019 7:02 AM
  • Greetings DA924x.

    I still think the OP's approach is too complicated, and Interfaces and generics used as they were intended would make things a lot simpler.

    But maybe it's just me.

    (Actually, I'm fairly sure it's not just me. The code in the original post, with four separate classes implementing four different interfaces is horrible and just screams for correction. But I suppose, ultimately, the OP should implement what's right for him/her no matter how ugly people like me find it.)

    Edit : The end of that last sentence should read "no matter how ugly it is found to be by people like me". Not any alternative interpretation. Obviously.
    Well they are complicate, but somehow they are very easy to maintain, when something goes wrong on a piece of code without I only have to fix the DAO or the GUI class, I rarely make changes to the interfaces, I think that's very good when you have a problem and you know right way where to fix :)
    But since they are many and we're getting a new project I want to implement a new technique and try to refactor the old project.

    BP-LP 2005/2016 @ll rights reserved

    Friday, July 12, 2019 7:28 AM
  • BTW: What does OP means?

    Original Poster is the one that posted the initial post that started the thread.  That's my understand of OP.

    Friday, July 12, 2019 7:36 AM
  • Inspired by all the code I have been reading here, I'm thinking on something like this:
    BTW: It's to make a Laundry Management System

        public abstract class CRUDOperations
    
        {
            public abstract void Add<T>(T entity);
            public abstract void Update<T>(T entity);
            public abstract void Delete<T>(T entity);
            public abstract List<T> GetAll<T>();
        }
    
     public class DAOClients : CRUDOperations
        {
            public override void Update<T>(T entity)
            {
                throw new System.NotImplementedException();
            }
    
            public override void Add<T>(T entity)
            {
                throw new System.NotImplementedException();
            }
    
            public override void Delete<T>(T entity)
            {
                throw new System.NotImplementedException();
            }
    
            public override List<T> GetAll<T>()
            {
                throw new System.NotImplementedException();
            }
        }

    I know that I could use an interface, but something in me asks "Why you always use an interface?"

    And I can use an interface to add new stuff to singular classes.
    TBH I'm eager to learn something new and force myself to learn to use abstract classes, and I think on my case it really doesn't matter which one I use for the main 4 CRUD methods.
    and YAGNI keeps buzzing my ear to use interfaces instead of abstract class.

    So according you guys experience, me using abstract class may become a huge mess in the future for this type of project I'm working,  in for what will be related with CRUD operations?

    Edit: Changed the generic class to non generic


    BP-LP 2005/2016 @ll rights reserved


    • Edited by Belarmino Vicenzo Saturday, July 13, 2019 10:19 AM No need for generic class, only methods
    Friday, July 12, 2019 7:38 AM
  • Ohhh!
    Thanks.

    BP-LP 2005/2016 @ll rights reserved

    Friday, July 12, 2019 7:41 AM
  • In this topic I didn't see important aspect of interface in data layer. At first there are some patterns which are mixed. DAO (data access object) is pattern which encapsulate table operations. It means it is not depend if it is used in java, C#, … Main principle of this pattern is provide CRUD functionality to database table. Martin Fowler called it Table Gateway pattern. This pattern is ver useful in systems which are not too complex. 

    Repository pattern is not pattern of data layer. It lays between domain and data layer. It means there is some mechanism to access table (DAO can be used as well as). Repository provides access to domain object. This pattern makes sense with relation do domain model business pattern. It is because repository doesn't have any Save method. Save method has Unit of Work pattern which works well together with repository and which calls another mechanism 
    DAP) to save data to database (repository doesn't save anything to database). 

    At second, don't use generic types if not necessary. It is more complicated to implements some changes in future. For example: If you have IDao<T> and you have method T Get(); You create instance Dao<Person>() so it means Get method will returns Person object. It could be fine. But there could be some reason to return Person object which doesn't fit all columns from table. In future you will create new PersonDetail object. How do you change your Dao to return PersonDetail? Generic type is not necessary in this case. 

    What about interface? What is reason to create data layer? In my opinion reason to create data layer is provide data to another part of application to do some operation. If another part of application (ie. business layer) needs PersonDetail for operation it should be better when data layer provides PersonDetail. If another part needs Person object only data layer provides it. 

    What do I mean by it? I mean data layer is not who defines which data will be providen. It is another layer. It means interface is not in data layer but data layer implements it. So data layer is dependent to another layer. Interface is contract between layers. It can be two types:

    1) Facade - it is limitation of whole system functions. It says I provide this functionality.

    2) Contract - it is implementation of requirement of another layer. Another layer says: I need data in this form, could you provide it for me? If data layer wants to work together with another layer it need to implement their interface.

    There is one significant difference between abstract class and interface. Interface can change system behavior from outside of system. Typical example is strategy pattern. It defines interface which need to be implement. In example cassette player will implement same interface as CD player. You can change device and client can use it same way.

    But abstract class tends to create template method pattern. If you create AbstractDao<T> with method IEnumerable<T> GetAll() then each class derived from it will have GetAll method. If there will be some table with many records it will be akward to call GetAll method. 

    When you use interface you can create own interface for each Dao class. It is I principle from SOLID.

    Sunday, July 14, 2019 6:49 AM

  • At second, don't use generic types if not necessary. It is more complicated to implements some changes in future. For example: If you have IDao<T> and you have method T Get(); You create instance Dao<Person>() so it means Get method will returns Person object. It could be fine. But there could be some reason to return Person object which doesn't fit all columns from table. In future you will create new PersonDetail object. How do you change your Dao to return PersonDetail? Generic type is not necessary in this case. 


    Well, I think it is. Because I need a method that fits to all types.
    If I need to implement a PersonDetail later, I just need to create a DTO that returns those details or I just need to add a method to return PersonDetails on my DAO.
    Normally I do that, I might return the same Person different times, with diferente data, I normally only add a new method to return what I need from the database.
    After all, a person it's still a person.
    The generics will fit perfectly here, at least from my point of view.

    BP-LP 2005/2016 @ll rights reserved

    Monday, July 15, 2019 6:55 AM

  • But abstract class tends to create template method pattern. If you create AbstractDao<T> with method IEnumerable<T> GetAll() then each class derived from it will have GetAll method. If there will be some table with many records it will be akward to call GetAll method. 


    Yeah, I realized that.
    I really need that all them/most of them have some kind of GetAll()
    So, I just changed to something like GetAllRecords(), it doesn't say which one, and it isn't akward
    And at least for this project, most of types I wrote till now, will need at some point to get all records.


    BP-LP 2005/2016 @ll rights reserved

    Monday, July 15, 2019 7:02 AM
  • Yeah, project is small I think. But we have tables with many record when SELECT * FROM Table is not real. But it was small project so that it was not problem. We have generic abstract class and programmer doesn't have problem call GetAll() methods and then work with loaded objects. We cannot change this class so easily as we need. Don't repeat same mistakes as we did.
    Tuesday, July 16, 2019 10:07 AM
  • Yeah, project is small I think. But we have tables with many record when SELECT * FROM Table is not real. But it was small project so that it was not problem. We have generic abstract class and programmer doesn't have problem call GetAll() methods and then work with loaded objects. We cannot change this class so easily as we need. Don't repeat same mistakes as we did.
    Got it.
    Lesson learned.

    BP-LP 2005/2016 @ll rights reserved

    Tuesday, July 16, 2019 2:50 PM