I found a question that might be related, but since there's no stack trace I'm not sure.

However, I've got this exception: "System.InvalidOperationException: Sequence contains no matching element"

This is the LINQ query:

this.ItemVersionsUntyped.Set(x => x.TimeStampFromEditVersion, (DateTime)value).Update();

The relevant top of the stack trace:

System.Linq.Enumerable.Single(IEnumerable`1 source, Func`2 predicate) +2715797 
Xtensive.Orm.BulkOperations.Operation`1..ctor(QueryProvider queryProvider) +525
Xtensive.Orm.BulkOperations.BulkUpdateOperation`1..ctor(IUpdatable`1 query) +183
Xtensive.Orm.BulkOperations.BulkExtensions.Update(IUpdatable`1 query) +51

ItemVersionsUntyped is just a plain EntitySet (inherited though, for some validation on adding, but nothing else is different.)

However, ItemVersionsUntyped is EntitySet<interface> (or IQueryable<baseclass>, I've tried both with the same error.) They are not HierarchyRoot.

I think the culprit is in Xtensive.Orm.BulkOperations.Operation - neither the interface not the base class are in the hierarchy. I think the problem is that it's tied down to exactly one type when there could be many matching types?

I've done some modifications just for fun (it's hardly tested at all, at least it executes without errors anymore) to the BulkUpdateOperation class and to the BulkExtensions class. In BulkUpdateOperation I've edited line 118 to look like this:

    descriptors.Add(
      new SetDescriptor(TypeInfo.Fields.First(a =>
          member.DeclaringType.IsAssignableFrom(a.UnderlyingProperty.DeclaringType) &&
          a.UnderlyingProperty.Name == member.Name), lambda.Parameters[0], lambda.Body));

and in BulkExtensions I've modified this method to do create a new BulkUpdateOperation for every hierarchy root, and passing the updateble's query through a .Select(x => x as type), and construct a new updateable of the correct type. It's really, really ugly.. But it's more proof och concept I think :)

public static int Update<T>(this IUpdatable<T> query) where T : class, IEntity
{
    int result = 0;

    var type = typeof(T);
    var session = Session.Current;
    var queryProvider = session.Query.Provider;

    var allRoots = session.Domain.Model.Hierarchies.Select(x => x.Root);
    var matchingRoots = allRoots.Where(x => type.IsAssignableFrom(x.UnderlyingType));

    var typeBulkUpdateOperation = typeof(BulkUpdateOperation<>);

    var updatable = (Updatable<T>)query;
    var typeUpdatable = typeof(Updatable<>);

    var queryableSelectMethodInfo = typeof(System.Linq.Queryable).GetMethods(
                    System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static).Where(x =>
                        x.Name == "Select" &&
                        x.GetParameters().Length == 2 &&
                        x.GetParameters()[1].ParameterType.GetGenericArguments().Length == 1 &&
                        x.GetParameters()[1].ParameterType.GetGenericArguments()[0].GetGenericArguments().Length == 2).Single();
    var typeIQueryable = typeof(IQueryable<>);

    foreach (var root in matchingRoots)
    {
        var c = Expression.Constant(updatable.Query);
        var p = Expression.Parameter(type, "_x");
        var typeAs = Expression.TypeAs(p, root.UnderlyingType);
        var selectLambda = Expression.Lambda(typeAs, p);
        var select = Expression.Call(queryableSelectMethodInfo.MakeGenericMethod(type, root.UnderlyingType), c, selectLambda);

        var ps = Expression.Parameter(typeIQueryable.MakeGenericType(type), "_y");

        var qryLambda = Expression.Lambda(select, ps);
        var qry = qryLambda.Compile().DynamicInvoke(updatable.Query);

        var up = Activator.CreateInstance(
            typeUpdatable.MakeGenericType(root.UnderlyingType), 
            qry, 
            updatable.Expressions.First().Item1,
            updatable.Expressions.First().Item2);
        var typeBulkUpdateOperationTyped = typeBulkUpdateOperation.MakeGenericType(root.UnderlyingType);
        var op = Activator.CreateInstance(typeBulkUpdateOperationTyped, up);
        var executeMethod = typeBulkUpdateOperationTyped.GetMethod("Execute");
        result += (int)executeMethod.Invoke(op, null);
    }

    return result;
  //return new BulkUpdateOperation<T>(query).Execute();
}

I hope my research is useful :)

asked Oct 05 '12 at 09:03

Onkelborg's gravatar image

Onkelborg
677712

edited Oct 05 '12 at 09:03

I've done a small test now, and as far as I can tell the field got updated in the db. And I do know I haven't added support for more then one .Set call, yet

(Oct 05 '12 at 09:16) Onkelborg Onkelborg's gravatar image

One Answer:

Hello Onkelborg,

for the moment your scenario is not supposed to be working.

DO only cares about persistent types: persistent interfaces (those that inherit from IEntity) and persistent classes that reside in hierarchy below class with [HierarchyRoot].

Bulk operations extension similarly is not designed to handle update via base-non-hierarchy-root type.

But otherwise your solution seems simple and straightforward for me.

answered Oct 06 '12 at 02:34

Denis%20Krjuchkov's gravatar image

Denis Krjuchkov
179325

edited Oct 06 '12 at 02:40

Hm, I'm not sure I understand - the interface inherits from IEntity, and has a property with [Key] etc. defined - if I inherit from Entity and my interface, or from some other base class that inherits from Entity and my interface shouldn't make any difference? At least the code doesn't seem to make any difference right now? My interpretation is that Bulk Operations doesn't support targeting an interface, is that correct?

(Oct 06 '12 at 09:33) Onkelborg Onkelborg's gravatar image

For the moment BulkOperations extension does not handle persistent interfaces, but this might change in future.

(Oct 06 '12 at 09:40) Denis Krjuchkov Denis%20Krjuchkov's gravatar image

Ok, good to know that it's not supported. I'm thinking about implementing some custom extension methods instead that just calls the ordinary extension methods, that way I won't have to make changes to your code, ie. creating a "ghost" namespace. If I do that, do you want a copy?

(Oct 06 '12 at 15:07) Onkelborg Onkelborg's gravatar image
1

It's always useful to share your experience. You might post your improvements here as a separate discussion so others can find them.

(Oct 08 '12 at 05:43) Denis Krjuchkov Denis%20Krjuchkov's gravatar image
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

powered by OSQA