0

We are working on a Web API Project built using ASP.Net Core3.1 and we are wondering if the following is possible -

We are using Stored Procedures to handle INSERT / UPDATE queries and based on the validation, returning different status codes as below.

public async Task<IActionResult> PutCompany(Company company)
    {

     var data = _context.Set<sp_Upsert_Company>().FromSqlInterpolated($"sp_Upsert_Company {master_Company.Name},{uID}").ToList().FirstOrDefault();

            if (data.Code == -400)
            {
                return NotFound(new NotFoundAPIResponse());
            }
            else if (data.Code == -300)
            {
                return Ok(new ErrorAPIResponse("Duplicate Data Found"));
            }
            else
                return Ok(response);
    }

We're returning different codes for different scenarios (-400, -300 etc.) and the codes are consistent across entire project. We're thinking to encapsulate the return code checking lines into a function in a separate common class like below -

public IActionResult Validate(dynamic dynamicObjectData)
    {
        if (dynamicObjectData.Code == -400)
        {
            return NotFound(new NotFoundAPIResponse());
        }
        else if (dynamicObjectData.Code == -300)
        {
                return Ok(new ErrorAPIResponse("Duplicate Data Found"));
        }
        return Ok(response);
    }

Once the above is encapsulated in a different class, we would like to change the controller function as below -

    public async Task<IActionResult> PutCompany(Company company)
    {

     var data = _context.Set<sp_Upsert_Company>().FromSqlInterpolated($"sp_Upsert_Company {master_Company.Name},{uID}").ToList().FirstOrDefault();

            if ((!Validate(data) is OkResult))
            {
                return Validate(data);
            }
            else 
            {
                //Do whatever we want to do
            }
    }

Please suggest. Thanks in advance.

2
  • What exactly is the problem? You don't want to write Validate() in each method? And waht is _context.Set<sp_Upsert_Company>? Is it a table, i.e. _context.Set<Company>? Commented Jan 12, 2021 at 15:40
  • _context is the object of DbContext class found at controller level & sp_Upsert_Company is a SP Commented Jan 12, 2021 at 17:15

1 Answer 1

1

In general I would strictly separate data access and return codes of the web API. So I'd first go with some kind of repository to query data:

public abstract class RepositoryBase
{
  protected void Validate(dynamic dynamicObjectData) { ... }
}

I would not use stored procedures, but operate on context with EF entities (all queries are typed), but I guess you have a good reason to use SPs, so you could go with:

public class CompanyRepository : RepositoryBase
{
  public void UpsertCompany(Company company)
  {
      var result = _context.Set<sp_Upsert_Company>().FromSqlInterpolated($"sp_Upsert_Company {master_Company.Name},{uID}").ToList().FirstOrDefault();
      Validate(result);
  }
}

Validate function shall throw exceptions if something is wrong:

if (dynamicObjectData.Code == -400)
        {
            throw new MyNotFoundException();
        }

So data layer queries data and throws if something bad happens.

Now you register a central exception handler on your ApplicationBuilder:

app.UseExceptionHandler(
   builder => builder.Run(async context =>
   {
      var exceptionFeature = context.Features.Get<IExceptionHandlerFeature>();
      if (exceptionFeature != null)
      {
         var exception = exceptionFeature.Error;
         // Handle all your app exceptions in one place and decide which code to return, create a custom error:
         ...
         context.Response.StatusCode = error.StatusCode; // Choose based on exception
         await context.Response.WriteAsync(JsonConvert.SerializeObject(error));
      })

So you encapsulate bad return codes with exceptions, catch them in one place, and decide what HTTP code to return.

Sign up to request clarification or add additional context in comments.

5 Comments

Great insights @Maxim! Any specific reasons for not using SPs? We do not want to return all data back in response or model, also trying to have 1 SP (update+insert = upsert) for entities. Will try throwing exceptions and see if serves our purpose. The main thing is to separate data layer from API layer, we're doing it within the API functions.
question : context is the main DbContext class, how do we find it in CompanyRepository class?
You would inject StorageContext into your repository with DI, just register it with your DI container, sth like services.AddDbContext<StorageContext>( options => options.UseSqlServer( connectionString, sqlServerOptions => sqlServerOptions.EnableRetryOnFailure()), ServiceLifetime.Transient);
With SPs your logic is outside of your application. EF offers everything you need to accomplish CRUD operations and more on the object level. Unless you need to tweak for some super fast performance, EF already does a good job and brings validation, simpler unit testing and absence of magic SQL strings in your code. Also you don't need (and in fact should never) return EF entities through API. For this you should create DTO that only have what the end client needs. Mapping from EF entities to DTOs is easily done with e.g. AutoMapper.
Valid points, we implemented it successfully.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.