Override http status code from validator - c#

I've got the following DTO:
public class SomethingRequest {
public string Code { get; set; }
}
Code must be unique, so I've created a validator that checks if there is already a record with provided code, like the following
public class SomethingValidator: AbstractValidator<SomethingRequest>
{
public SomethingValidator(ISomethingRepository repo) {
RuleFor(something => something.Code).Must(BeUnique);
}
private bool BeUnique(string code) { ... uniqueness check... }
}
As I'm using validation feature, the validator is automatically wired for all methods with SomethingRequest, which is really great.
When condition fails I would like to return 409 Conflict HTTP status code, but 400 Bad Request is always returned.
So, the questions are:
Am I misusing vaidation feature? (i.e. autowired validators were not designed to be used for application logic checks)
If I'm not, are there any ways to override 400 BadRequest status code from validator?

Am I misusing validation feature? (i.e. autowired validators were not designed to be used for application logic checks)
I would say this is best done in the business logic away from the validation, because checking for a uniqueness is actually a verification check rather than validation, because it requires checking against a data source. My answer on this question addresses a similar concern.
While you can override the response status code of the validation error using the ErrorResponseFilter, I would recommend creating your own request filter for this business logic, as overriding the response there will be messy as your application grows, and again, it's not really validation.
Using a filter attribute is straightforward in ServiceStack:
public class VerifySomethingCodeAttribute : Attribute, IHasRequestFilter
{
IHasRequestFilter IHasRequestFilter.Copy()
{
return this;
}
public int Priority { get { return int.MinValue; } }
public void RequestFilter(IRequest req, IResponse res, object requestDto)
{
SomethingRequest somethingRequestDto = requestDto as SomethingRequest;
if(somethingRequestDto == null)
return;
// Verify the code
// Replace with suitable logic
// If you need the database your wire it up from the IoC
// i.e. HostContext.TryResolve<IDbConnectionFactory>();
bool isUnique = ...
if(!isUnique)
throw HttpError.Conflict("This record already exists");
}
}
Then simply annotate the DTO:
[VerifySomethingCode]
public class SomethingRequest {
public string Code { get; set; }
}
Then you can be sure that the Code in the DTO will have been verified as unique and you can return any status and response you want. The filter gives you total control.
Hope this helps.

1) Although it allows dependency injection and wiring up of repositories, the fluent validation code isn't the place you are supposed to put this kind of code as it is more along the lines of verification code. This answer has a good explanation of the differences between the two. I'll just add that it also makes sense for splitting the verification up from validation if only for more easily returning the appropriate status code.
2)If you would like to override the 400 BadRequest status code, you can use the validation feature's ErrorResponseFilter like so:
Plugins.Add(new ValidationFeature
{
ErrorResponseFilter = CustomValidationError
});
...
private object CustomValidationError(ValidationResult validationResult, object errorDto)
{
var firstError = validationResult.Errors.First();
return new HttpError(HttpStatusCode.Conflict, firstError.ErrorCode, firstError.ErrorMessage);
}
This filter looks to be intended for a global solution as it doesn't appear to give you any easy way to determine the dto/service the error came from. I would suggest looking at making the change in 1 instead.

Related

Reduce/remove repetitive business logic checks in ASP.NET Core

Is there a way to reduce/remove constant duplication of user access checks (or some other checks) in a business layer?
Let's consider a following example: simple CRUD application with one entity BlogPost:
public class BlogPost
{
public int Id { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public int AuthorId { get; set; }
}
In PUT/DELETE requests before modifying or deleting entity I need to make a check whether the user that's making request is author of BlogPost, so he is permitted to delete/edit it.
So both in UpdateBlogPost and DeleteBlogPost of imaginary BlogPostService I'll have to write something like this:
var blogPostInDb = _blogPostRepository.GetBlogPost();
if(blogPostInDb == null)
{
// throw exception or do whatever is needed
}
if(blogPostInDb.AuthorId != _currentUser.Id)
{
// throw exception etc...
}
This kind of code will be the same for both Updateand Delete methods as well as other methods that may be added in future and the same for all entities.
Is there any way to reduce or completely remove such duplication?
I thought this over and came up with following solutions, but they don't satisfy me fully.
First solution
Using filters. We can create some custom filters like [EnsureEntityExists] and [EnsureUserCanManageEntity] but this way we're spreading some of business logic in our API layer and it's not flexible enough since we need to create such filter for every entity. Perhaps some kind of generic filter can be made using reflection.
Also there is another problem with this approach, let's say we've made such filter that's checking our rules. We're fetching entity from db, doing checks, throwing exceptions and all that stuff and letting controller method execute. BUT in service layer we need to fetch entity again, so we're making two roundtrips to db. Maybe I'm overthinking this problem and that's fine to make 2 roundtrips, taking into account that fact that caching can be applied.
Second solution
Since I'm using CQRS (or at least some kind of it) I have MediatR library and I can make use of Pipeline Behaviors and even pass fetched entity further into pipeline via mutating TRequest (which I don't wanna do). This solution requires some common interface for all requests to be able to retrieve id of the entity. The roundtrip problem also applicable here too.
public interface IBlogPostAccess
{
public int Id { get; set; }
}
public class ChangeBlogPostCommand: IRequest, IBlogPostAccess
{
// ...
}
public class DeleteBlogPostCommand: IRequest, IBlogPostAccess
{
// ...
}
public class BlogPostAccessBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : IBlogPostAccess
{
// all nessesary stuff injected via DI
public BlogPostAccessBehavior()
{
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
var blogPostInDb = _blogPostRepository.GetBlogPost(request.Id);
if(blogPostInDb == null)
{
// throw exception or do whatever is needed
}
if(blogPostInDb.AuthorId != _currentUser.Id)
{
// throw exception etc...
}
return await next();
}
}
Third solution
Create something like request context service. In a very simplified way it will be a dictionary that will be persisted across request where we can store data (in this case our BlogPost that we've fetched in filter/pipeline). This seems lame and recalls me a ViewBag in ASP.NET MVC.
Fourth solution
It's more enhancement than a solution, but we can use GuardClause or extension methods to reduce nesting of if statements.
Again, maybe I'm overthinking this problem or it's not a problem at all or that's a design issue. Any help, thoughts appreciated.
If you are concerned about many database calls you could try caching the returned objects per request with something like LazyCache https://github.com/alastairtree/LazyCache
I would not recommend caching across requests...
For code organization, I would recommend extracting the authorization logic into a separate method and calling that method each request. Benefit is that if the logic changes then only need to updated it in one place.
For example something like this:
bool canEdit(userId){
var user = getUserByUserId(userId);
if(user.IsAdmin) return true;
//depending on where this method lives might have access to blogpost here
if(_blogPost.AuthorId == userId) return true;
return false;
}

c# web api request validation filter, context values empty - arguments list isn't

I'm using C# web api and want to create a filter to all requests.
I have a designated class to every request so I just want to add some data annotations and get the validation over with.
The problem is that I'm getting true every time on actionContext.ModelState.IsValid
I have added my filter in the config:
config.Filters.Add(new RequestValidationFilter());
validation method looks like every other in the web
public class RequestValidationFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (actionContext.ModelState.IsValid == false)
{
var errors = actionContext.ModelState
.Values
.SelectMany(m => m.Errors
.Select(e => e.ErrorMessage));
actionContext.Response = actionContext.Request.CreateErrorResponse(
HttpStatusCode.BadRequest, actionContext.ModelState);
actionContext.Response.ReasonPhrase = string.Join("\n", errors);
}
}
}
I have the following method:
[HttpPost, Route("User/Login")]
public async Task<Responses.Login> Login(Requests.Login request)
{
...//some login logic
}
And also, I have my model which is:
public class Requests
{
public class Login
{
[Required(AllowEmptyStrings = false, ErrorMessage = "Email address cannot be empty!")]
[MinLength(5)]
public string Email;
[Required]
public string Password;
}
}
I'm sending both an empty request or request which Email and Password are null and still the actionContext.ModelState.IsValid evaluate it as true
Attached is an image when email was sent but password wasn't.
Following a comment, here is my request via Advanced Rest Client chrome plugin
NOTE
the image actually shows that Keys and Values are empty when in fact they are supplied..
EDIT
number of things i've also tried:
removing all other filters, why? maybe the context was messed up by another reading.
sending valid request in terms of fields, but email was 1 char long.why? maybe Requiredis working differently than others, still nothing about the min-length issue.
instead of nested objects, i created a seperate stand-alone class for the Login object. why? thought maybe the fact that it's nested the validation is not recursive.
Looping the Arguments list one-by-one and validate as object, answer is always true. never fails, why? cause Im almost admitting defeat.
instead of adding filter to config as i described in the question, tried GlobalConfiguration.Configuration.Filters.Add(new RequestValidationFilter()); instead
You need to add { get; set; } after your model properties:
public class Login
{
[Required(AllowEmptyStrings = false, ErrorMessage = "Email address cannot be empty!")]
[MinLength(5)]
public string Email { get; set; }
[Required]
public string Password { get; set; }
}
This is necessary because the default model validation for ASP.NET only includes properties with a public get method. From PropertyHelper.cs, here's some of the code which determines whether a property on the model will be included in validation:
// Indexed properties are not useful (or valid) for grabbing properties off an object.
private static bool IsInterestingProperty(PropertyInfo property)
{
return property.GetIndexParameters().Length == 0 &&
property.GetMethod != null &&
property.GetMethod.IsPublic &&
!property.GetMethod.IsStatic;
}
This method is used to filter the properties that are used in the default model binding in MVC/Web API. Notice that it's checking whether the GetMethod exists on the property and that it's public. Since your class didn't have the get methods on its properties, they were being ignored.
If you want to know more, you can kill a lot of time looking through the ASP.NET MVC source. I think the code on github is for a newer version of ASP.NET, but it seems like a lot of the same logic applies in the version you are using.

Access Servicstack.net session in validator

How can I access a ServiceStack.net session in my validation code?
public class UserSettingsValidator : AbstractValidator<UserSettingsRequest>
{
public UserSettingsValidator()
{
RuleFor(x => x.UserId)
.SetValidator(new PositiveIntegerValidator())
.SetValidator(new UserAccessValidator(session.UserId)); //<-- I need to pass the UserID from the session here
}
}
In the Service Implementation I just do:
var session = base.SessionAs<UserSession>();
but this does not work for my abstract validator.
Thanks!
Edit: this is version 3.9.71.0
I assume you are just using the ValidationFeature plugin, as most do. If that's the case, then I don't think it is possible. Ultimately the ValidationFeature is a plugin which uses a RequestFilter.
I wanted to do something similar before too, then realised it wasn't possible.
The RequestFilter is run before the ServiceRunner. See the order of operations guide here.
What this means to you is your populated request DTO reaches your service, and the validation feature's request filter will try validate your request, before it has even created the ServiceRunner.
The ServiceRunner is where an instance of your service class becomes active. It is your service class instance that will be injected with your UserSession object.
So effectively you can't do any validation that relies on the session at this point.
Overcomplicated ?:
It is possible to do validation in your service method, and you could create a custom object that would allow you pass the session along with the object you want to validate. (See next section). But I would ask yourself, are you overcomplicating your validation?
For a simple check of the request UserId matching the session's UserId, presumably you are doing this so the user can only make changes to their own records; Why not check in the service's action method and throw an Exception? I am guessing people shouldn't be changing this Id, so it's not so much a validation issue, but more a security exception. But like I say, maybe your scenario is different.
public class SomeService : Service
{
public object Post(UserSettingsRequest request) // Match to your own request
{
if(request.UserId != Session.UserId)
throw new Exception("Invalid UserId");
}
}
Validation in the Service Action:
You should read up on using Fluent Validators. You can call the custom validator yourself in your service method.
// This class allows you to add pass in your session and your object
public class WithSession<T>
{
public UserSession Session { get; set; }
public T Object { get; set; }
}
public interface IUserAccessValidator
{
bool ValidUser(UserSession session);
}
public class UserAccessValidator : IUserAccessValidator
{
public bool ValidUser(UserSession session)
{
// Your validation logic here
// session.UserId
return true;
}
}
public class UserSettingsValidator : AbstractValidator<WithSession<UserSettingsRequest>>
{
public IUserAccessValidator UserAccessValidator { get; set; }
public UserSettingsValidator()
{
// Notice check now uses .Object to access the object within
RuleFor(x => x.Object.UserId)
.SetValidator(new PositiveIntegerValidator());
// Custom User Access Validator check, passing the session
RuleFor(x => x.Session).Must(x => UserAccessValidator.ValidUser(x));
}
}
Then to actually use the validator in your service:
public class SomeService : Service
{
// Validator with be injected, you need to registered it in the IoC container.
public IValidator<WithSession<UserSettingsRequest>> { get; set; }
public object Post(UserSettingsRequest request) // Match to your own request
{
// Combine the request with the current session instance
var requestWithSession = new WithSession<UserSettingsRequest> {
Session = this.Session,
Object = request
};
// Validate the request
ValidationResult result = this.Validator.Validate(requestWithSession);
if(!result.IsValid)
{
throw result.ToException();
}
// Request is valid
// ... more logic here
return result;
}
}
I hope this helps. Note: code is untested
It appears that after reading from a bunch of people experiencing similar problems, then many hours of playing with several solutions based on the SS4 Cookbook etc, this is a problem that is already solved:
https://forums.servicestack.net/t/blaz-miheljak-355-feb-3-2015/176/2
Implement the IRequiresRequest interface on your validator, and voila.

Where to put global rules validation in DDD

I'm new to DDD, and I'm trying to apply it in real life. There is no questions about such validation logic, as null check, empty strings check, etc - that goes directly to entity constructor/property. But where to put validation of some global rules like 'Unique user name'?
So, we have entity User
public class User : IAggregateRoot
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
// other data and behavior
}
And repository for users
public interface IUserRepository : IRepository<User>
{
User FindByName(string name);
}
Options are:
Inject repository to entity
Inject repository to factory
Create operation on domain service
???
And each option more detailed:
1 .Inject repository to entity
I can query repository in entities constructor/property. But I think that keeping reference to repository in entity is a bad smell.
public User(IUserRepository repository)
{
_repository = repository;
}
public string Name
{
get { return _name; }
set
{
if (_repository.FindByName(value) != null)
throw new UserAlreadyExistsException();
_name = value;
}
}
Update: We can use DI to hide dependency between User and IUserRepository via Specification object.
2. Inject repository to factory
I can put this verification logic in UserFactory. But what if we want to change name of already existing user?
3. Create operation on domain service
I can create domain service for creating and editing users. But someone can directly edit name of user without calling that service...
public class AdministrationService
{
private IUserRepository _userRepository;
public AdministrationService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public void RenameUser(string oldName, string newName)
{
if (_userRepository.FindByName(newName) != null)
throw new UserAlreadyExistException();
User user = _userRepository.FindByName(oldName);
user.Name = newName;
_userRepository.Save(user);
}
}
4. ???
Where do you put global validation logic for entities?
Thanks!
Most of the times it is best to place these kind of rules in Specification objects.
You can place these Specifications in your domain packages, so anybody using your domain package has access to them. Using a specification, you can bundle your business rules with your entities, without creating difficult-to-read entities with undesired dependencies on services and repositories. If needed, you can inject dependencies on services or repositories into a specification.
Depending on the context, you can build different validators using the specification objects.
Main concern of entities should be keeping track of business state - that's enough of a responsibility and they shouldn't be concerned with validation.
Example
public class User
{
public string Id { get; set; }
public string Name { get; set; }
}
Two specifications:
public class IdNotEmptySpecification : ISpecification<User>
{
public bool IsSatisfiedBy(User subject)
{
return !string.IsNullOrEmpty(subject.Id);
}
}
public class NameNotTakenSpecification : ISpecification<User>
{
// omitted code to set service; better use DI
private Service.IUserNameService UserNameService { get; set; }
public bool IsSatisfiedBy(User subject)
{
return UserNameService.NameIsAvailable(subject.Name);
}
}
And a validator:
public class UserPersistenceValidator : IValidator<User>
{
private readonly IList<ISpecification<User>> Rules =
new List<ISpecification<User>>
{
new IdNotEmptySpecification(),
new NameNotEmptySpecification(),
new NameNotTakenSpecification()
// and more ... better use DI to fill this list
};
public bool IsValid(User entity)
{
return BrokenRules(entity).Count() == 0;
}
public IEnumerable<string> BrokenRules(User entity)
{
return Rules.Where(rule => !rule.IsSatisfiedBy(entity))
.Select(rule => GetMessageForBrokenRule(rule));
}
// ...
}
For completeness, the interfaces:
public interface IValidator<T>
{
bool IsValid(T entity);
IEnumerable<string> BrokenRules(T entity);
}
public interface ISpecification<T>
{
bool IsSatisfiedBy(T subject);
}
Notes
I think Vijay Patel's earlier answer is in the right direction, but I feel it's a bit off. He suggests that the user entity depends on the specification, where I belief that this should be the other way around. This way, you can let the specification depend on services, repositories and context in general, without making your entity depend on them through a specification dependency.
References
A related question with a good answer with example: Validation in a Domain Driven Design.
Eric Evans describes the use of the specification pattern for validation, selection and object construction in chapter 9, pp 145.
This article on the specification pattern with an application in .Net might be of interest to you.
I would not recommend disallowing to change properties in entity, if it's a user input.
For example, if validation did not pass, you can still use the instance to display it in user interface with validation results, allowing user to correct the error.
Jimmy Nilsson in his "Applying Domain-Driven Design and Patterns" recommends to validate for a particular operation, not just for persisting. While an entity could be successfully persisted, the real validation occurs when an entity is about to change it's state, for example 'Ordered' state changes to 'Purchased'.
While creating, the instance must be valid-for-saving, which involves checking for uniqueness. It's different from valid-for-ordering, where not only uniqueness must be checked, but also, for example, creditability of a client, and availability at the store.
So, validation logic should not be invoked on a property assignments, it should be invoked upon aggregate level operations, whether they are persistent or not.
Edit: Judging from the other answers, the correct name for such a 'domain service' is specification. I've updated my answer to reflect this, including a more detailed code sample.
I'd go with option 3; create a domain service specification which encapsulates the actual logic that performs the validation. For example, the specification initially calls a repository, but you could replace it with a web service call at a later stage. Having all that logic behind an abstract specification will keep the overall design more flexible.
To prevent someone from editing the name without validating it, make the specification a required aspect of editing the name. You can achieve this by changing the API of your entity to something like this:
public class User
{
public string Name { get; private set; }
public void SetName(string name, ISpecification<User, string> specification)
{
// Insert basic null validation here.
if (!specification.IsSatisfiedBy(this, name))
{
// Throw some validation exception.
}
this.Name = name;
}
}
public interface ISpecification<TType, TValue>
{
bool IsSatisfiedBy(TType obj, TValue value);
}
public class UniqueUserNameSpecification : ISpecification<User, string>
{
private IUserRepository repository;
public UniqueUserNameSpecification(IUserRepository repository)
{
this.repository = repository;
}
public bool IsSatisfiedBy(User obj, string value)
{
if (value == obj.Name)
{
return true;
}
// Use this.repository for further validation of the name.
}
}
Your calling code would look something like this:
var userRepository = IoC.Resolve<IUserRepository>();
var specification = new UniqueUserNameSpecification(userRepository);
user.SetName("John", specification);
And of course, you can mock ISpecification in your unit tests for easier testing.
I’m not an expert on DDD but I have asked myself the same questions and this is what I came up with:
Validation logic should normally go into the constructor/factory and setters. This way you guarantee that you always have valid domain objects. But if the validation involves database queries that impact your performance, an efficient implementation requires a different design.
(1) Injecting Entities: Injecting entities can be technical difficult and also makes managing application performance very hard due to the fragmentation of you database logic. Seemingly simple operations can now have an unexpectedly performance impact. It also makes it impossible to optimize your domain object for operations on groups of the same kind of entities, you no longer can write a single group query, and instead you always have individual queries for each entity.
(2) Injecting repository: You should not put any business logic in repositories. Keep repositories simple and focused. They should act as if they were collections and only contain logic for adding, removing and finding objects (some even spinoff the find methods to other objects).
(3) Domain service This seems the most logical place to handle the validation that requires database querying. A good implementation would make the constructor/factory and setters involved package private, so that the entities can only be created / modified with the domain service.
I would use a Specification to encapsulate the rule. You can then call when the UserName property is updated (or from anywhere else that might need it):
public class UniqueUserNameSpecification : ISpecification
{
public bool IsSatisifiedBy(User user)
{
// Check if the username is unique here
}
}
public class User
{
string _Name;
UniqueUserNameSpecification _UniqueUserNameSpecification; // You decide how this is injected
public string Name
{
get { return _Name; }
set
{
if (_UniqueUserNameSpecification.IsSatisifiedBy(this))
{
_Name = value;
}
else
{
// Execute your custom warning here
}
}
}
}
It won't matter if another developer tries to modify User.Name directly, because the rule will always execute.
Find out more here
In my CQRS Framework, every Command Handler class also contains a ValidateCommand method, which then calls the appropriate business/validation logic in the Domain (mostly implemented as Entity methods or Entity static methods).
So the caller would do like so:
if (cmdService.ValidateCommand(myCommand) == ValidationResult.OK)
{
// Now we can assume there will be no business reason to reject
// the command
cmdService.ExecuteCommand(myCommand); // Async
}
Every specialized Command Handler contains the wrapper logic, for instance:
public ValidationResult ValidateCommand(MakeCustomerGold command)
{
var result = new ValidationResult();
if (Customer.CanMakeGold(command.CustomerId))
{
// "OK" logic here
} else {
// "Not OK" logic here
}
}
The ExecuteCommand method of the command handler will then call the ValidateCommand() again, so even if the client didn't bother, nothing will happen in the Domain that is not supposed to.
in short you have 4 options:
IsValid method: transition an entity to a state (potentially invalid) and ask it to validate itself.
Validation in application services.
TryExecute pattern.
Execute / CanExecute pattern.
read more here
Create a method, for example, called IsUserNameValid() and make that accessible from everywhere. I would put it in the user service myself. Doing this will not limit you when future changes arise. It keeps the validation code in one place (implementation), and other code that depends on it will not have to change if the validation changes You may find that you need to call this from multiple places later on, such as the ui for visual indication without having to resort to exception handling. The service layer for correct operations, and the repository (cache, db, etc.) layer to ensure that stored items are valid.
I like option 3. Simplest implementation could look so:
public interface IUser
{
string Name { get; }
bool IsNew { get; }
}
public class User : IUser
{
public string Name { get; private set; }
public bool IsNew { get; private set; }
}
public class UserService : IUserService
{
public void ValidateUser(IUser user)
{
var repository = RepositoryFactory.GetUserRepository(); // use IoC if needed
if (user.IsNew && repository.UserExists(user.Name))
throw new ValidationException("Username already exists");
}
}
Create domain service
Or I can create domain service for
creating and editing users. But
someone can directly edit name of user
without calling that service...
If you properly designed your entities this should not be an issue.

Is there a better way to handle validation in LINQ to SQL?

Are there any ways, besides throwing exceptions, that one can go about using the partial validation methods in LINQ to SQL to cancel the insert of a record?
I can understand that you don't want to throw an exception directly after a property is set with an invalid value. This approach makes it difficult to communicate correctly to the user what actually is wrong. However, I think it's better to keep away from using those partial validation methods. IMO you want to throw an exception when your model is invalid, but only just before you're persisting your model to the database.
I advise you to use a validation framework and integrate it with your LINQ to SQL DataContext class. Here's an example of how to do this with The Enterprise Library Validation Application Block, but the concept will work for every validation framework you pick:
public partial class NorthwindDataContext
{
public override void SubmitChanges(ConflictMode failureMode)
{
ValidationResult[] = this.Validate();
if (invalidResults.Length > 0)
{
// You should define this exception type
throw new ValidationException(invalidResults);
}
base.SubmitChanges(failureMode);
}
private ValidationResult[] Validate()
{
// here we use the Validation Application Block.
return invalidResults = (
from entity in this.GetChangedEntities()
let type = entity.GetType()
let validator = ValidationFactory.CreateValidator(type)
let results = validator.Validate(entity)
where !results.IsValid
from result in results
select result).ToArray();
}
private IEnumerable<object> GetChangedEntities()
{
ChangeSet changes = this.GetChangeSet();
return changes.Inserts.Concat(changes.Updates);
}
}
[Serializable]
public class ValidationException : Exception
{
public ValidationException(IEnumerable<ValidationResult> results)
: base("There are validation errors.")
{
this.Results = new ReadOnlyCollection<ValidationResult>(
results.ToArray());
}
public ReadOnlyCollection<ValidationResult> Results
{
get; private set;
}
}
There are several validation frameworks available, such as DataAnnotations and
the Enterprise Library Validation Application Block (VAB). VAB is very suited for doing this. With LINQ to SQL your entities are generated, so you'll need to use the configuration based approach that VAB offers (don’t try decorating your entities with attributes). By overriding the SubmitChanges method you can make sure the validation gets triggered just before entities are persisted. My SO answers here and here contain useful information about using VAB.
I've written a few interesting articles about integrating VAB with LINQ to SQL here and here. The nice thing about LINQ to SQL (compared to Entity Framework 1.0) is that a lot of useful metadata is generated. When combining this with VAB you can use this metadata to validate your model, without having to hook up every validation manually. Especially validations as maximum string length and not null can be extracted from the model. Read here how to do this.
VAB to the rescue!
Ultimately this indicates that at you last line of defence (before any database constraints, at least) your data was invalid. If you want to do something other than scream loudly, then perhaps verify the data (via any of a multitude of approaches) before adding it to the insert list.
As an additional thought, you could try overriding SubmitChanges (on the data-context); obtain the change-set, verify the inserts and remove (delete-on-submit, which IIRC checks the insert list and drops them) any that you've decided were mistakes. Then call the base.SubmitChanges. But to me this is a bit backwards.
To illustrate, this only does a single insert (not two as requested), but I don't like this approach. At all. As long as we're clear ;-p
namespace ConsoleApplication1 {
partial class DataClasses1DataContext { // extends the generated data-context
public override void SubmitChanges(
System.Data.Linq.ConflictMode failureMode) {
var delta = GetChangeSet();
foreach (var item in delta.Inserts.OfType<IEntityCheck>()) {
if (!item.IsValid()) {
GetTable(item.GetType()).DeleteOnSubmit(item);
}
}
base.SubmitChanges(failureMode);
}
}
public interface IEntityCheck { // our custom basic validation interface
bool IsValid();
}
partial class SomeTable : IEntityCheck { // extends the generated entity
public bool IsValid() { return this.Val.StartsWith("d"); }
}
static class Program {
static void Main() {
using (var ctx = new DataClasses1DataContext()) {
ctx.Log = Console.Out; // report what it does
ctx.SomeTables.InsertOnSubmit(new SomeTable { Val = "abc" });
ctx.SomeTables.InsertOnSubmit(new SomeTable { Val = "def" });
ctx.SubmitChanges();
}
}
}
}

Categories