Some questions about constraints:

  • when or how can we upload localized resource files to be included in the release ... (i can do the dutch translations)

  • specify a resource manager string for a constraint message in the Attribute property Message for constraints (or other ways?)

  • less technical exception when using a too long string.. see this stacktrace failed: System.InvalidOperationException : 'Length' constraint violation on field 'FirstName (FieldInfo)'. c:\TeamCity\buildAgent\work\6a5d81309423096e\Xtensive.Storage\Xtensive.Storage\Internals\DefaultFieldAccessor.cs(50,0): at Xtensive.Storage.Internals.DefaultFieldAccessor`1.SetValue(Persistent obj, FieldInfo field, T value) c:\TeamCity\buildAgent\work\6a5d81309423096e\Xtensive.Storage\Xtensive.Storage\Persistent.cs(286,0): at Xtensive.Storage.Persistent.SetFieldValueT c:\TeamCity\buildAgent\work\6a5d81309423096e\Xtensive.Storage\Xtensive.Storage\Persistent.cs(245,0): at Xtensive.Storage.Persistent.SetFieldValueT at ...Model.Person.set_FirstName(String value)

Maybe pull the constraints classes into the namespace Xtensive.Integrity.Constraints; (remove the Aspects.. its not clear that you have to add this namespace for constraints (or add them to the Xtensive.Storage namespace, because you need them always when defining your persistent classes)

Marco

(edit: added another remark)


Updated at 04.12.2009 22:35:56

This is the declaration of the persistent property:

[Field(Length = 75)]
        [LengthConstraint(Max=75)]
        [NotNullOrEmptyConstraint()]

I expected an AggregateException, but the code ..FirstName = too loonggg stringg..... is raising the exception..


Updated at 05.12.2009 9:42:12

About length constraint: may be you're getting the exception while validation is disabled? Stack trace shows actually it is thrown by our integrated constraints (they're called constraints as well ;) ),

This is the property.

[Field(Length = 50)]
        public string FirstName { get; set; }

I expected an LengthConstraint is automatically added... maybe add an overload to [Field(Length=50, Constraint=true) and the will add a constraint like [LengthConstraint(Max=50)]


Updated at 06.12.2009 21:10:05

So I'm not sure what to do... On one hand, you'd like to get the same exception; on another, you disable the 2nd type of constraints, so even if they would be there, you have the chance to get just an exception generated by 1st type constraint. Can you advise something here?

the SetValue exception is raised after assigning the value. So when you use a disabled validation region (to collect all the validation errors instead of only the first error) you have a problem, because if an user enters a too long string value, the SetValue exception is raised instead of an normal validation error... So how would you solve this?


Updated at 07.12.2009 16:13:28

No, that will not help me and all the other (good and bad) programmers :lol:

You should get an exception when saving too long texts, maybe you should move the XxxFieldAccessor<t>.SetValue constraints (or at least the Lenght constrain, don't know the others) to transaction.Complete() method.. That way you have an option to call <entity>.CheckConstaints() before completing the transaction

Thanks


Updated at 07.12.2009 22:24:21

Truncating can be a solution in some situations, but especially in the case of Name / FirstName / LastName you dont want to automatically truncate the value.. Lets return to my first remark: - less technical exception when using a too long string.. see this stacktrace failed: System.InvalidOperationException : 'Length' constraint violation on field 'FirstName (FieldInfo)'.

Maybe just return the same Exception message (and type) as in the LengthConstraint? Normally you set a maxlength in the UI (using the FieldInfo information from the Entity), but when using the upcoming Entity-DTO-Mapper (especially in a service environment) you are not in control of the UI..

Are there more constraints in this layer? like DateTime (sql server limitation)?

This thread was imported from our support forum. The original discussion may contain more detailed answer.

asked Dec 04 '09 at 22:06

Marco's gravatar image

Marco
22161618


One Answer:

Alex (Xtensive) wrote:

About localization: that's the only reason why we're keeping http://code.google.com/p/dataobjectsdot ... tail?id=73 not closed yet. As you may find, few days ago I asked to finish this ASAP.

About length constraint: may be you're getting the exception while validation is disabled? Stack trace shows actually it is thrown by our integrated constraints (they're called constraints as well ;) ), but not by [LengthConstraint] attribute. So I suspects validation in aspect is skipped because you're in Validation.Disable() block. That's why exception type here is different.

About Xtensive.Integrity.Constraints: in near future there will be no Xtensive.Integrity at all - http://code.google.com/p/dataobjectsdot ... ail?id=488


Alex (Xtensive) wrote:

There are two different types of constraints:

  • Integrated field constraints. E.g. max string\array length for a particular field, nullability for string fields... They're enforced under any circumstances, because underlying storage will not allow to violate them on persist (even temporary - i.e. until commit). These constraints are normally enforced inside XxxFieldAccessor<t>.SetValue method, that is executed to convert property value to tuple field value (column value).

  • Validation constraints. These are BLL-level constraints, and they can be temporarily disabled. You may find the validation layer we have there is fully separated from Xtensive.Storage - i.e. this is a pluggable component, actually it knows nothing about Storage at all (but obviously, Storage knows about it, because it uses it). Constraints there are different: they're implemented as property level aspects integrated within common validation framework.

That's why 1st-type constraints do not involve 2nd-type: 1st are much stronger, there is no way to violate them temporarily.

So I'm not sure what to do... On one hand, you'd like to get the same exception; on another, you disable the 2nd type of constraints, so even if they would be there, you have the chance to get just an exception generated by 1st type constraint. Can you advise something here?

If you're looking for error message, may be this might help (Persistent.GetErrorMessage method code):

private static string GetErrorMessage(Exception error)
    {
      string result;
      if (error==null)
        result = string.Empty;
      else if (error is AggregateException) {
        var ae = error as AggregateException;
        var errors = ae.GetFlatExceptions();
        if (errors.Count==1)
          result = errors[0].Message;
        else
          result = ae.Message;
      }
      else 
        result = error.Message;
      return result ?? string.Empty;
    }

Alex (Xtensive) wrote:

Btw, if you won't specify the length for the string field, everything should work as you want - underlying column type will be nvarchar(max), but validation constraint will allow you to persist strings of specified length only. But I'm not sure if this is acceptable in your case.


Alex Kofman wrote:

I suggest following solution: 1. Internally cut string field value to maximum possible length before persist 2. Use field constraints in usual way

However, this approach brings some disadvantages also:

public class Person : Entity
  {
    [Field(Length = 8)]
    public string Name { get; set; }
  }

using (var transactionScope = Transaction.Open()) {

    var marco = new Person { Name = "Marco Dissel" };
    var coolProgrammers = Query<All>.Where(person => person.Name.EndsWith("Dissel"));
    // There are no cool programmers here, but at least one is expected to be :(

    transactionScope.Complete();
  }

So making this behavior optional can be partial solution of the problem. Marco, would such option help you or not?


Alex (Xtensive) wrote:

We can't move these constraints to tx.Complete():

  • violation of any of these constraints indicates there is no way to persist the value at all. So basically, these constraints check if value is "persistable" at all, i.e. there will be no exception on attempt to persist it.

  • DO4 must be able to persist any provided value at any moment of time. Queries, too big cached sequence of updates, etc., might lead to flush. So we assume if value is successfully set, we're able to persist it. But this does not necessarily mean it won't break validation rules, these checks are postponed so far.

v3.9 has a nice attribute: [Corrector] (or something like this ;) ). If there would be such an attribute (btw, it's pretty easy to implement it on PostSharp), the following code would solve the problem:

[Field(Length=11)]
[LengthCorrector(Max=11)]
[LengthConstraint(Max=10)]
public string Name { get; set; }

What do you think about this? Likely, Alex Kofman was talking about nearly this approach.


Alex (Xtensive) wrote:

As you see, the idea is to: a) Ensure the underlying value will fit into the storage column anyway b) Add regular validation constraint checking this.

Btw, likely, it will be simple to support of behavior like this right by [LengthConstraint(..., Truncate = true)].


Alex (Xtensive) wrote:

E.g. it can truncate "Marco Dissel" (12 chars) to "Marco Diss..." (11 chars, = 10+1). It does not matter what value to persist further, since validation constraint is anyway broken, so you won't be able to commit it. But such values looks a bit nicer in UI, if they're fetched later in the same transaction.


Alex Kofman wrote:

I've just implemented localizable error messages in property constraints. It looks like this now:

[RegexConstraint(Pattern = @"^(\(\d+\))?[-\d ]+$", 
  MessageResourceType = typeof(MyResources) , MessageResourceName = "IncorrectPhoneFormat")]
public string Phone { get; set;}

Alex Kofman wrote:

  • less technical exception when using a too long string...

I've created appropriate issue: http://code.google.com/p/dataobjectsdot ... ail?id=517

First of all I plan to remove that check from field accessor, so it will not throw exception on property setting. This will help you in case you don't call Session.Persist() (i.e. execute any query) between assignment property value and performing validation.


Alex (Xtensive) wrote:

> Are there more constraints in this layer? like DateTime (sql server limitation)?

public override void SetValue(Persistent obj, FieldInfo field, T value)
    {
      if (!field.IsNullable && value==null)
        throw new InvalidOperationException(string.Format(
          Strings.ExNotNullableConstraintViolationOnFieldX, field));

      if (value!=null && field.Length > 0) {
        if (isString && field.Length < ((string) (object) value).Length)
          throw new InvalidOperationException(string.Format(
            Strings.ExLengthConstraintViolationOnFieldX, field));
        if (isByteArray && field.Length < ((byte[]) (object) value).Length)
          throw new InvalidOperationException(string.Format(
            Strings.ExLengthConstraintViolationOnFieldX, field));
      }

      EnsureGenericParameterIsValid(field);
      obj.Tuple.SetValue(field.MappingInfo.Offset, value);
    }

So for now we check just nullability & length (everything else will lead to delayed persist failure). And, as you see, constraints there aren't database specific.

answered Dec 05 '09 at 04:55

Editor's gravatar image

Editor
46151156157

Your answer
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!
toggle preview

Subscription:

Once you sign in you will be able to subscribe for any updates here

Tags:

×572

Asked: Dec 04 '09 at 22:06

Seen: 2,559 times

Last updated: Dec 04 '09 at 22:06

powered by OSQA