none
C# Where do filtering of data go? RRS feed

  • Question

  • Hi, I've come across a code today and i am now confuse where to do the filtering. I've been doing filtering in the UI layer while this one I've  come across did it in the business layer ended  up with a lot of filters.We do have the same business layer/or a service layer(whatever you call it) that returns a list of products. The only difference is this one comes with a lot of filter already.

    Another things, say we have a class of Products

    public class Product
    {
    	public int Id { get; set; }
    	public string ProductName { get; set; }
    	public DateTime CreatedDate { get; set;}
    }

    The author of the code i'm looking at created another class like this.

    public class ProductNames
    {
    	public string ProductName { get; set; }
    }

    He then pass the result of the filter to this class which ended up a lot with a lot of classes to maintain..

    public List<ProductNames> productnames()
    {
    	return productRepository.GetAll().Where(x => x.ProductName).ToList().Select(x => new ProductNames
    	{
    		ProductName = x.ProductName
    	}
    }
    Is there any benefits following this route?

    Saturday, November 4, 2017 5:35 AM

Answers

  • Oh, my fault, the Product Class is not totally a DTO. It may contain some validation logic in so side of it. Do you think it is good to combine them in a single assembly?

    Why not? I have used what I called SmartDTO that knew its state. So when I looked at the state, I knew be looking at the DTO(s) state how I was going to persist to DTO to the DB.

    IsNew and IsDirty -- insert record into DB table

    !IsNew and IsDirty -- updated existing record in the DB table

    isDelete - delete record from database table 

    I just used what I needed out of the example.

    http://www.dailycoding.com/Posts/maintaining_dirty_and_new_state_of_objects.aspx

    I was told that i need to use a parameterized queries in order to gain the benefit of reusing connections. I ended adding parameters in all of my queries before just to have my connection pooled. I should have known this before.

    I don't knw where you go it from, but it's not right.

    https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-connection-pooling

    <copied>

    Only connections with the same configuration can be pooled. ADO.NET keeps several pools at the same time, one for each configuration. Connections are separated into pools by connection string, and by Windows identity when integrated security is used. Connections are also pooled based on whether they are enlisted in a transaction. When using ChangePassword, the SqlCredential instance affects the connection pool. Different instances of SqlCredential will use different connection pools, even if the user ID and password are the same.

    <end>

    • Marked as answer by Dikong42 Sunday, November 5, 2017 4:27 AM
    Saturday, November 4, 2017 5:09 PM
  • The SL that I have been shown and used is the  means to call the CRUD operations in a DAL. The example shown is calling a WCF Web service that has the Repository and DAL sitting behind the service.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Entities;
    
    namespace ServiceLayer
    {
        public class ServiceA : IServiceA
        {
            public List<DTOStudent> GetStudents()
            {
                var dtos = new List<DTOStudent>();
    
                var client = new WCFServiceReference1.Service1Client();
    
                dtos = client.GetStudents().ToList();
                 
                if (dtos[0].DtoResponse.Message != null)
                {
                    throw new Exception(dtos[0].DtoResponse.Message + " " + dtos[0].DtoResponse.InnerException
                        + " " + dtos[0].DtoResponse.StackTrace);
                }
    
                return dtos;
            }
    
            public DTOStudent GetStudentById(Int32 id)
            {
                var client = new WCFServiceReference1.Service1Client();
    
               var dto = client.GetStudentById(id);
    
                if (dto.DtoResponse.Message != null)
                {
                    throw new Exception(dto.DtoResponse.Message + " " + dto.DtoResponse.InnerException
                        + " " + dto.DtoResponse.StackTrace);
                }
    
                return dto;
            }
            public void CreateStudent(DTOStudent dto)
            {
                var client = new WCFServiceReference1.Service1Client();
    
                var dtor = client.CreateStudent(dto);
    
                if (dtor != null)
                {
                    throw new Exception(dtor.DtoResponse.Message + " " + dtor.DtoResponse.InnerException
                        + " " + dtor.DtoResponse.StackTrace);
                }
            }
            public void UpdateStudent(DTOStudent dto)
            {
                var client = new WCFServiceReference1.Service1Client();
    
                var dtor = client.UpdateStudent(dto);
    
                if (dtor != null)
                {
                    throw new Exception(dtor.DtoResponse.Message + " " + dtor.DtoResponse.InnerException
                        + " " + dtor.DtoResponse.StackTrace);
                }
            }
            public void DeleteStudent(Int32 id)
            {
                var client = new WCFServiceReference1.Service1Client();
    
                var dtor = client.DeleteStudent(id);
    
                if (dtor != null)
                {
                    throw new Exception(dtor.DtoResponse.Message + " " + dtor.DtoResponse.InnerException
                        + " " + dtor.DtoResponse.StackTrace);
                }
            }
            public DTOEnrollment GetEnrollmentById(Int32 id)
            {
                var client = new WCFServiceReference1.Service1Client();
    
                return client.GetEnrollmentById(id);
            }
            public List<DTOEnrollment> GetEnrollments()
            {
                var dtos = new List<DTOEnrollment>();
    
                var client = new WCFServiceReference1.Service1Client();
    
                dtos = client.GetEnrollments().ToList();
    
                return dtos;
            }
            public void CreateEnrollment(DTOEnrollment dto)
            {
                var client = new WCFServiceReference1.Service1Client();
                client.CreateEnrollment(dto);
            }
            public void UpdateEnrollment(DTOEnrollment dto)
            {
                var client = new WCFServiceReference1.Service1Client();
                client.UpdateEnrollment(dto);
            }
            public void DeleteEnrollment(Int32 id)
            {
                var client = new WCFServiceReference1.Service1Client();
                client.DeleteEnrollment(id);
            }
        }
    }
    

    • Marked as answer by Dikong42 Sunday, November 5, 2017 4:27 AM
    Saturday, November 4, 2017 8:10 PM

All replies

  • Is there any benefits following this route?

    Benefits: It is type-safe. That is, it will save the mistake of using, say, a list of CustomerNames somewhere that a list of ProductNames is expected. If you just use a list of string for all of them, then the compiler would not detect the mistake.

    Drawbacks: You have to maintain a lot of classes.

    Possible compromise: Use a Generic type such as List<Descriptor<T>>, where the Descriptor<T> could be invoked with a <T> that is either a Product or a Customer, etc. This would solve the type-safety issue without requiring the maintenance of multiple classes (but requiring a more awkward syntax using all the <whatever> specifiers).

    Saturday, November 4, 2017 10:36 AM
    Moderator
  • Is there any benefits following this route?

    https://en.wikipedia.org/wiki/Separation_of_concerns`

    Filtering is business rules not UI rules.

    He then pass the result of the filter to this class which ended up a lot with a lot of classes to maintain..

    The example is using a Repository.

    https://msdn.microsoft.com/en-us/library/ff649690.aspx

    Saturday, November 4, 2017 12:38 PM
  • Would you mind to show me an example sir.

    And what do we call these classes, can we call them DTO's?

    • Edited by Dikong42 Saturday, November 4, 2017 2:28 PM
    Saturday, November 4, 2017 2:01 PM
  • Would you mind to show me an example sir.

    And what do we call these classes, can we call them DTO's?

    Yes, you can call the classes like the ones below a prefixed or suffixed with DTO, with all the DTO(s) kept in the Entities project.

    public class DtoProduct { public int Id { get; set; } public string ProductName { get; set; } public DateTime CreatedDate { get; set;} }

    public class DtoProductName
    {
    public string ProductName { get; set; }
    }


    As far as the Repository, it can be non-generic or generic and not using an ORM.

    http://deviq.com/repository-pattern/

    https://www.danylkoweb.com/Blog/creating-a-repository-pattern-without-an-orm-A9

    Myself, I like to push the low level database access to the DAO.

    http://blog.sapiensworks.com/post/2012/11/01/Repository-vs-DAO.aspx

    Saturday, November 4, 2017 3:03 PM
  • I'm using DAO pattern by the way, it feels like it is easier to extend and i can customized my queries to maximize performance. 

    So instead of doing something like this. 

    public List<ProductNames> productnames()
    {
    	return productRepository.GetAll().Where(x => x.ProductName).ToList().Select(x => new ProductNames
    	{
    		ProductName = x.ProductName
    	}
    }

    do you think it would be better to do something like this

     public List<ProductNames> GetProductNames()
            {
                string sql = "SELECT ProductName  FROM PRODUCT ";
                return SqlHelper.DapperConnection.Query<Model.ProductNames>(sql).ToList();
            }

    Instead of doing filtering in the business layer, do you think it's better  to do all these stuffs in the datalayer? I will have a lot of selects and maybe lots of logic in my sql codes but i know i will surely gain performance. How's your take on that.

     public List<ProductSearchModel> GetProduct(string searchObject)
            {
                var search = ProductDao.GetProduct().Where(x => x.BARCODE.Contains(searchObject)).ToList();
                if (search.Any())
                {
                    return search.ToList().Take(10)
                        .Select(x => new ProductSearchModel
                        {
                            UnitPrice = (double?) x.UNITPRICE,
                            ID = x.ID,
                            CurrentCount = x.CURRENTCOUNT,
                            Model = x.MODEL,
                            Description = x.DESCRIPTION
                        }).ToList();
                }
                search = ProductDao.GetProduct().Where(x => x.DESCRIPTION.Contains(searchObject)).ToList();
                if (search.Any())
                {
                    return search.ToList().Take(10)
                        .Select(x => new ProductSearchModel
                        {
                            UnitPrice = (double?)x.UNITPRICE,
                            ID = x.ID,
                            CurrentCount = x.CURRENTCOUNT,
                            Model = x.MODEL,
                            Description = x.DESCRIPTION
                        }).ToList();
                }
                search = ProductDao.GetProduct().Where(x => x.MODEL.Contains(searchObject)).ToList();
                if (search.Any())
                {
                    return search.ToList().Take(10)
                        .Select(x => new ProductSearchModel
                        {
                            UnitPrice = (double?)x.UNITPRICE,
                            ID = x.ID,
                            CurrentCount = x.CURRENTCOUNT,
                            Model = x.MODEL,
                            Description = x.DESCRIPTION
                        }).ToList();
                }
                return null;
            }

    This code happens to be inside of the service layer, i can easily do this in the data layer. What will be the disadvantage of moving this to the data layer?

    Oh, i'm using a static connection here by the way. Don't know how it affects connection pooling but this app is a single threaded so i guess i'm safe with that.

    You mentioned entities projects,

     My entities project represents all the tables and columns inside of my database something like this.

    public class Product
    {
    	public int Id { get; set; }
    	public string ProductName { get; set; }
    	public DateTime CreatedDate { get; set;}
    }

    but in your example you prefixed this class with a DTO as well. 

    What is your intention doing that?


    • Edited by Dikong42 Saturday, November 4, 2017 3:27 PM
    Saturday, November 4, 2017 3:14 PM
  • I'm using DAO pattern by the way, it feels like it is easier to extend and i can customized my queries to maximize performance. 

    So instead of doing something like this. 

    public List<ProductNames> productnames()
    {
    	return productRepository.GetAll().Where(x => x.ProductName).ToList().Select(x => new ProductNames
    	{
    		ProductName = x.ProductName
    	}
    }

    do you think it would be better to do something like this

     public List<ProductNames> GetProductNames()
            {
                string sql = "SELECT ProductName  FROM PRODUCT ";
                return SqlHelper.DapperConnection.Query<Model.ProductNames>(sql).ToList();
            }

    Instead of doing filtering in the business layer, do you think it's better  to do all these stuffs in the datalayer? I will have a lot of selects and maybe lots of logic in my sql codes but i know i will surely gain performance. How's your take on that.

    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    That's where I would do them in your second option.

    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

     public List<ProductSearchModel> GetProduct(string searchObject)
            {
                var search = ProductDao.GetProduct().Where(x => x.BARCODE.Contains(searchObject)).ToList();
                if (search.Any())
                {
                    return search.ToList().Take(10)
                        .Select(x => new ProductSearchModel
                        {
                            UnitPrice = (double?) x.UNITPRICE,
                            ID = x.ID,
                            CurrentCount = x.CURRENTCOUNT,
                            Model = x.MODEL,
                            Description = x.DESCRIPTION
                        }).ToList();
                }
                search = ProductDao.GetProduct().Where(x => x.DESCRIPTION.Contains(searchObject)).ToList();
                if (search.Any())
                {
                    return search.ToList().Take(10)
                        .Select(x => new ProductSearchModel
                        {
                            UnitPrice = (double?)x.UNITPRICE,
                            ID = x.ID,
                            CurrentCount = x.CURRENTCOUNT,
                            Model = x.MODEL,
                            Description = x.DESCRIPTION
                        }).ToList();
                }
                search = ProductDao.GetProduct().Where(x => x.MODEL.Contains(searchObject)).ToList();
                if (search.Any())
                {
                    return search.ToList().Take(10)
                        .Select(x => new ProductSearchModel
                        {
                            UnitPrice = (double?)x.UNITPRICE,
                            ID = x.ID,
                            CurrentCount = x.CURRENTCOUNT,
                            Model = x.MODEL,
                            Description = x.DESCRIPTION
                        }).ToList();
                }
                return null;
            }

    This code happens to be inside of the service layer, i can easily do this in the data layer. What will be the disadvantage of moving this to the data layer?

    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>]

    It's because any changes in the DAL should not affect or impact the code above it. The service layer should be unware of the data source and low-level database activities.

    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    Oh, i'm using a static connection here by the way. Don't know how it affects connection pooling but this app is a single threaded so i guess i'm safe with that.

    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    Connection pooling has to do with the credentials used to logon to the DB. If a connection used by a program uses the same credentials that an existing connection is using, then the connection is shared so that connection can be pooled, which allows multiple clients/programs to share the single connection to the database. 

    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    You mentioned entities projects,

     My entities project represents all the tables and columns inside of my database something like this.

    public class Product
    {
    	public int Id { get; set; }
    	public string ProductName { get; set; }
    	public DateTime CreatedDate { get; set;}
    }

    but in your example you prefixed this class with a DTO as well. 

    What is your intention doing that?

    <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

    This is what a DTO is about so when one sees a class that is prefixed or suffixed with DTO one knows the intent.

    https://www.codeproject.com/Articles/1050468/Data-Transfer-Object-Design-Pattern-in-Csharp

    <copied>

    DTO (Data Transfer objects) is a data container for moving data between layers. They are also termed as transfer objects. DTO is only used to pass data and does not contain any business logic. They only have simple setters and getters.

    <end>



    Saturday, November 4, 2017 4:13 PM
  • Oh, my fault, the Product Class is not totally a DTO. It may contain some validation logic inside of it. Do you think it is good to combine them in a single assembly?

    "Connection pooling has to do with the credentials used to logon to the DB. If a connection used by a program uses the same credentials that an existing connection is using, then the connection is shared so that connection can be pooled, which allows multiple clients/programs to share the single connection to the database. "

    I was told that i need to use a parameterized queries in order to gain the benefit of reusing connections. I ended adding parameters in all of my queries before just to have my connection pooled. I should have known this before.

    • Edited by Dikong42 Saturday, November 4, 2017 4:24 PM
    Saturday, November 4, 2017 4:17 PM
  • Oh, my fault, the Product Class is not totally a DTO. It may contain some validation logic in so side of it. Do you think it is good to combine them in a single assembly?

    Why not? I have used what I called SmartDTO that knew its state. So when I looked at the state, I knew be looking at the DTO(s) state how I was going to persist to DTO to the DB.

    IsNew and IsDirty -- insert record into DB table

    !IsNew and IsDirty -- updated existing record in the DB table

    isDelete - delete record from database table 

    I just used what I needed out of the example.

    http://www.dailycoding.com/Posts/maintaining_dirty_and_new_state_of_objects.aspx

    I was told that i need to use a parameterized queries in order to gain the benefit of reusing connections. I ended adding parameters in all of my queries before just to have my connection pooled. I should have known this before.

    I don't knw where you go it from, but it's not right.

    https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-connection-pooling

    <copied>

    Only connections with the same configuration can be pooled. ADO.NET keeps several pools at the same time, one for each configuration. Connections are separated into pools by connection string, and by Windows identity when integrated security is used. Connections are also pooled based on whether they are enlisted in a transaction. When using ChangePassword, the SqlCredential instance affects the connection pool. Different instances of SqlCredential will use different connection pools, even if the user ID and password are the same.

    <end>

    • Marked as answer by Dikong42 Sunday, November 5, 2017 4:27 AM
    Saturday, November 4, 2017 5:09 PM
  • Oh, one more thing before i will close this thread.

    public class Person
    {	
    	public int Id { get; set; }
    	public string FirstName { get; set; }
    	public string LastName { get; set;}
    }
    
    
    
    //service layer
    public void UpdateFirstName(int id)
    {
    		//do a search using the Id as criteria then return a person
    		//set the person's firstname
    		//do upate
    }
    
    public void UpdateLastName(int id)
    {
    	//do a search using the Id as criteria then return a person
    	//set the person's lastname
    	//do upate
    }

    The code i'm looking at also have lot of codes like this. This code is living inside a service layer/business layer. I guess the author of this code doesn't want to tie himself to the UI so he decided to do it this way. The author of the code isn't using MVP pattern either and i must admit i am not good implementing the pattern myself as well but seeing a code like this makes me think. Is this better than having a code like this.

    public void UpdatePerson(Person person)
    {
    	//call the update method here
    }

    I admit this code is a little harder to implement if the person class does have a lot of properties as i may need to set all of them before passing it to the update parameter but lets keep the person's properties to just Id,FirstName and LastName for now.

    What approach will you going to follow?


    • Edited by Dikong42 Saturday, November 4, 2017 7:19 PM
    Saturday, November 4, 2017 7:18 PM
  • The SL that I have been shown and used is the  means to call the CRUD operations in a DAL. The example shown is calling a WCF Web service that has the Repository and DAL sitting behind the service.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using Entities;
    
    namespace ServiceLayer
    {
        public class ServiceA : IServiceA
        {
            public List<DTOStudent> GetStudents()
            {
                var dtos = new List<DTOStudent>();
    
                var client = new WCFServiceReference1.Service1Client();
    
                dtos = client.GetStudents().ToList();
                 
                if (dtos[0].DtoResponse.Message != null)
                {
                    throw new Exception(dtos[0].DtoResponse.Message + " " + dtos[0].DtoResponse.InnerException
                        + " " + dtos[0].DtoResponse.StackTrace);
                }
    
                return dtos;
            }
    
            public DTOStudent GetStudentById(Int32 id)
            {
                var client = new WCFServiceReference1.Service1Client();
    
               var dto = client.GetStudentById(id);
    
                if (dto.DtoResponse.Message != null)
                {
                    throw new Exception(dto.DtoResponse.Message + " " + dto.DtoResponse.InnerException
                        + " " + dto.DtoResponse.StackTrace);
                }
    
                return dto;
            }
            public void CreateStudent(DTOStudent dto)
            {
                var client = new WCFServiceReference1.Service1Client();
    
                var dtor = client.CreateStudent(dto);
    
                if (dtor != null)
                {
                    throw new Exception(dtor.DtoResponse.Message + " " + dtor.DtoResponse.InnerException
                        + " " + dtor.DtoResponse.StackTrace);
                }
            }
            public void UpdateStudent(DTOStudent dto)
            {
                var client = new WCFServiceReference1.Service1Client();
    
                var dtor = client.UpdateStudent(dto);
    
                if (dtor != null)
                {
                    throw new Exception(dtor.DtoResponse.Message + " " + dtor.DtoResponse.InnerException
                        + " " + dtor.DtoResponse.StackTrace);
                }
            }
            public void DeleteStudent(Int32 id)
            {
                var client = new WCFServiceReference1.Service1Client();
    
                var dtor = client.DeleteStudent(id);
    
                if (dtor != null)
                {
                    throw new Exception(dtor.DtoResponse.Message + " " + dtor.DtoResponse.InnerException
                        + " " + dtor.DtoResponse.StackTrace);
                }
            }
            public DTOEnrollment GetEnrollmentById(Int32 id)
            {
                var client = new WCFServiceReference1.Service1Client();
    
                return client.GetEnrollmentById(id);
            }
            public List<DTOEnrollment> GetEnrollments()
            {
                var dtos = new List<DTOEnrollment>();
    
                var client = new WCFServiceReference1.Service1Client();
    
                dtos = client.GetEnrollments().ToList();
    
                return dtos;
            }
            public void CreateEnrollment(DTOEnrollment dto)
            {
                var client = new WCFServiceReference1.Service1Client();
                client.CreateEnrollment(dto);
            }
            public void UpdateEnrollment(DTOEnrollment dto)
            {
                var client = new WCFServiceReference1.Service1Client();
                client.UpdateEnrollment(dto);
            }
            public void DeleteEnrollment(Int32 id)
            {
                var client = new WCFServiceReference1.Service1Client();
                client.DeleteEnrollment(id);
            }
        }
    }
    

    • Marked as answer by Dikong42 Sunday, November 5, 2017 4:27 AM
    Saturday, November 4, 2017 8:10 PM