0

We have a few repository classes in our application with the methods like the following:

public override void Add(Template template)
    {
        CheckDuplicateDescription(template);
        base.Add(template);
    }

    public override void Update(Template template)
    {
        CheckDuplicateDescription(template);
        base.Update(template);
    }

    private void CheckDuplicateDescription(Template template)
    {
        if( _dbSet.Any(x => x.Descrip.Equals(template.Descrip.Trim()) && x.TemplateId != template.TemplateId))
        {
            throw new DuplicatePropertyException("Description", 
                string.Format(Messages.alreadyExistsWithValue, template.Descrip.Trim()));
        }
    }

I'm trying to figure out if there is a way to make this method to be generic and implement in the base repository class (so we can probably provide a bool property to tell we need to validate, perhaps also the name of the column to check and the name of Pk column). I am not sure how to write such a code in a generic way assuming Entity entity in the update method.

Naomi
  • 718
  • 1
  • 9
  • 28
  • 1
    You should not throw exceptions for flow control and situations you can handle with other means (return values, etc). https://softwareengineering.stackexchange.com/questions/189222/are-exceptions-as-control-flow-considered-a-serious-antipattern-if-so-why – Steve Greene May 24 '18 at 18:49
  • I'm going to read the article which is referred in the first answer, but changing this now would be a huge re-write effort as it's already handled this way in our application (with exceptions from server-side code handled via toastr in the web application). For now I was thinking of just making that portion of the common logic to be generic. – Naomi May 24 '18 at 18:56
  • 1
    You could introduce a [service layer](https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions-1/models-data/validating-with-a-service-layer-cs) for validation. Also, if it web based (MVC?) you could maximize client side validation to insure the exceptions are minimal. – Steve Greene May 24 '18 at 18:56
  • 1
    Also see this https://stackoverflow.com/questions/9603131/where-to-run-a-duplicate-check-for-an-entity – Steve Greene May 24 '18 at 19:01
  • It is EF/ASP.NET MVC application using angularJs for front-end. I wrote one article about using server-side validation from the front end, which you may find interesting - I'll read both articles you refer to later. https://social.technet.microsoft.com/wiki/contents/articles/42394.implementing-server-side-validations-in-angularjs.aspx – Naomi May 24 '18 at 19:02
  • Will all of your repos have the Descrip field? If so, then there is no reason not to move it to the Base, that way it exists in one place and you don't run the risk of it having different implementations in different places. I do agree with @Steve Greene though, valid or invalid should both produce actionable results rather than an exception. – user7396598 May 24 '18 at 19:20
  • Not all entity objects will have descrip column and Id column will be named differently too. That's why I'm trying to figure out to a way to make it generic and optional. Our architecture is using API controller, then adapter, then repository. I want that logic in the repository and that's why I'm throwing an error and the API controller should figure out how to handle it (although we're just passing 500 error to front-end) – Naomi May 24 '18 at 21:01
  • Hi Steve, I like the latest reference to the previous stackoverflow question about it you added. I think I may try to go this way. I see it tries to "cast" entity to specific type and then validate. So, our base repository class may have all validations in one place. I like that idea or even using a special class for validation only. – Naomi May 24 '18 at 21:12

1 Answers1

2

Well I have done things like this before. I hope i can help. What you can do is build an expression for the condition in the _dbSet.Any

Something like this:

 public Expression<Func<T,bool>> GetCondition(string nameProperty, string text)
        {
            var i = Expression.Parameter(typeof(T), "i");
            var prop = Expression.Property(i, nameProperty);
            var value = Expression.Constant(text);

            MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
            var containsMethodExp = Expression.Call(prop, method, value);
            var lambda = Expression.Lambda<Func<T, bool>>(containsMethodExp, i);

            return lambda;
        }

then you can use it like this:

private void CheckDuplicateDescription(T template)
        {
            var propDescription = GetPropertyNameDescription();//asuming you have the name of the Description property
            var value = GetValueDescription(template, propDescription);
            var condition = GetCondition(propDescription, value);

            if (_dbSet.Any(condition))
            {
                throw new DuplicatePropertyException("Description",
                    string.Format(Messages.alreadyExistsWithValue, template.Descrip.Trim()));
            }
        }
FractalCode
  • 360
  • 1
  • 4
  • I figured out a way to make this more or less generic. I also implemented a new angularJs directive that performs such logic using async validators. I tested it and it works fine although there are some minor quirks to work on. I haven't yet time to read carefully all the links Steve gave me, hopefully will do in the next couple of days. Thanks a lot both of you to contributing. – Naomi May 29 '18 at 19:53