středa 24. listopadu 2010

The specified method 'xxx' on the type 'yyy' cannot be translated into a LINQ to Entities store expression because the instance over which it is invoked is not the ObjectContext over which the query in which it is used is evaluated.

I have a method defined in the partial class for one of the entities like this:

public bool IsAnonymous() {
  return (this.Roles_ & UserRoles.Registered) == 0;
 }



This of course doesn't work within LINQ to Entities queries, because the EF provider for LINQ doesn't know how to translate that to SQL so that it could send that to the database. No problem according to the docs. You can define a "Model Defined Function" and tell the EF to use that:

        <Function Name="IsAnonymous" ReturnType="Edm.Boolean" >
          <Parameter Name="user" Type="Kosmas.Models.User"/>
          <DefiningExpression>
            BitWiseAnd(user.Roles, 1) = 0
         </DefiningExpression>
        </Function>
and

[System.Data.Objects.DataClasses.EdmFunction("Kosmas.Models""IsAnonymous")]
 public bool IsAnonymous() {
  return (this.Roles_ & UserRoles.Registered) == 0;
 }


Or can you?


Well you can't. If you try this you get a very informative error: "The specified method 'Boolean IsAnonymous()' on the type 'Kosmas.Models.User' cannot be translated into a LINQ to Entities store expression because the instance over which it is invoked is not the ObjectContext over which the query in which it is used is evaluated."


A quick Google search did not get anything useful. The only suggestions were to either change the method so that instead of a User instance I call it on the context (making the syntax rather ... silly). Or use Entity SQL (which I'd rather not either).


So I tried whether the example in Programming Entity Framework book actually works and noticed one difference. In the example they were adding the [EdmFunction] attribute to an extension method, while I'm adding it to a plain old ordinary one.


OK, let'ts try that. Let's remove the IsAnonymous method from the partial class and add


public static class Functions {
  [System.Data.Objects.DataClasses.EdmFunction("Kosmas.Models""IsAnonymous")]
  public static bool IsAnonymous(this User obj) {
   return (obj.Roles_ & UserRoles.Registered) == 0;
  }
 }
et voila, it works. I would not call this bug ... if it wasn't. But it is! Anyway, if you do get the nonsensical error message, check whether the method is an ordinary method or an extension one. And see if you can change it from one to the other.

středa 10. listopadu 2010

CSharpCodeProvider.CreateEscapedIdentifier() is it good for anything?

Mkay, so I have an extended T4 template for Entity Framework. One of the things it does is generating enums out of a few marked tables. I mark the table in the Designer and specify the column to use for the enum item names, the values are taken from the primary key. The template then connects to the database, fetches the data from those (static loookup) tables and generates the enums. And I use the code.Escape(name) that the original template uses all over the place to escape the names of the entities and properties. So I am safe right? The options will not be exactly the same as the values in the database, because they have to be valid identifiers, but what the heck. We have intellisense.
Right?

Wrong!
The code.Escape() calls CSharpCodeProvider.CreateEscapedIdentifier() which does ... well nothing really. If the string contains a space, the result will contain a space. If it contains a dash, the result will contain a dash. Etc. etc. etc.
So far it seems the only thing it does is ... if the whole string matches a C# keyword, the method prepends @.

OK, let's see the docs.

Public methodCreateEscapedIdentifierCreates an escaped identifier for the specified value. (Inherited from CodeDomProvider.)
Yeah, sure.
Any other candidates?

Public methodCreateValidIdentifierCreates a valid identifier for the specified value. (Inherited from CodeDomProvider.)
OK, let's try ... nope. Seems the only difference is that instead of @ we get an underscore. But just like the CreateEscapedIdentifier()
code.CreateValidIdentifier("Hello world") == "Hello world"

How's that a valid identifier I really do not know.
Funny thing is that code.IsValidIdentifier("Hello world") returns false. Just like code.IsValidIdentifier(code.CreateValidIdentifier("Hello world")) of course.

Thank you very much Microsoft once again!

pondělí 1. listopadu 2010

Closures and properties -> problems

OK. So C# has lambdas and closures. Fine. You've got to be carefull though! I have not tested all options, but one thing I know for sure already. It's unable to close over an object whose property I access. So if you have something like

foreach (var column in tracked.TrackedColumns) {
 if (column.Type.Contains("char")) {
  column.AsChar = (t => "'\"'+" + (t.Contains("[") ? t + column.Name + "]" : t + ".[" + column.Name + "]") + "+'\"'");
 } else if (column.Type.Contains("date")) {
  column.AsChar = (t => "'\"'" + (t.Contains("[") ? HistoryDatetimeFormat(t + column.Name + "]") : HistoryDatetimeFormat(t + ".[" + column.Name + "]")));
 } else if ...

you will find out that this doesn't work. It'll behave as if all the objects in the collection had the same function, and you end up with the column name of the first one. You have to copy the column name into a local variable. 

foreach (var column in tracked.TrackedColumns) {
 string name = column.Name;
 if (column.Type.Contains("char")) {
  column.AsChar = (t => "'\"'+" + (t.Contains("[") ? t + name + "]" : t + ".[" + name + "]") + "+'\"'");
 } else if (column.Type.Contains("date")) {
  column.AsChar = (t => "'\"'" + (t.Contains("[") ? HistoryDatetimeFormat(t + name + "]") : HistoryDatetimeFormat(t + ".[" + name + "]")));
 } else if 

sobota 16. října 2010

int == System.Int32. Or not?

So they say int is an alias to System.Int32, right? So I should be able to use one where ever I use the other, right? Or not?

Try this:


public enum AddressTypes : System.Int32 { Delivery = 1, Invoicing = 2};



==>

Error 89 Type byte, sbyte, short, ushort, int, uint, long, or ulong expected D:\...\DataModel\DB.Designer2.cs 799 34 DataModel

Oh Microsoft, how I love thee.
So the Entity Framework dutifully converts the database type to the .Net type, I take the type, use it in generated code and kaboooom. Thank you!

void Dictionary.Add(TK key, TV value)

Oh my. Why the heck could not they let this method return the dictionary being added to? Why?

Not only I could write

aLongAndComplicatedDictionaryName
    .Add("foo", foo)
    .Add("bar", bar);

but ... more importantly ... I would not have to jump through hoops as soon as I need to add one more key into a dictionary createdy by .ToDictionary() in the middle of a complex LINQ query.

Oh well.

úterý 12. října 2010

IsNumeric is not numeric (MS SQL)

Run this on MS SQL (2008 RC2, but I don't think the version matters):


select isnumeric('.')
select convert(int,'.')

Sweet isn't it? And no, it's not because integers are not supposed to have a decimal dot.

select convert(float,'.')

explodes too. 
Let's continue the fun.

select isnumeric('-')

returns 1.

select convert(int,'-')

returns 0 and

select convert(float,'-')

explodes. Why???

On the other hand

select convert(int, '14.878')

explodes, while

select convert(int, convert(float,'14.878'))

returns 14.

pondělí 11. října 2010

AttributeProviderAttribute rendered useless ... or maybe not?


OK, so I'm writing some ASP.Net MVC application, I've got some data model classes generated by Entity Framework (with a custom template and loads of attributes added to the properties) and of course for the views I've got some viewmodels. Nothing unusual right? Most of the properties of the viewmodels are of course based on the properties of the datamodel so I'd like to copy the attributes from the datamodel classes to the viewmodel classes so that instead of repeating the loads of attributes I could just point to the other property in the other class and have them copied.
Enter System.ComponentModel.AttributeProviderAttribute. The docs are rather bad, babbling something unrelated about some silly DataGridView.DataSource property, but nevertheless it looks like it might be the thing I'm looking for. Let's see ... so you can specify the type to get the attribute from as [AttributeProvider(typeof(Project.Models.Whatever)], you can also specify the type name as a string ("The name of the type to specify." per docs, with no example whatsoever) or you can specify the type name as string and the property name as string (again, no examples).

OK, I need to specify the property name, so I have to use the AttributeProviderAttribute(String, String) constructor.

public class Sumfin {
 [AttributeProvider("Project.Models.Whatever", "FullName"]
 public string Name {get;set;}
 ...

Nope, no chance. No error, but the attributes are not there. OK, let's see if I can get it to take the attributes from the type itself.
[AttributeProvider("Project.Models.Whatever"]
Nope. OK, let's try the other syntax ...
[AttributeProvider( typeof(Project.Models.Whatever)]
Hey, that works. But how the heck do I specify the property?? And why doesn't the string version of the type name work?

A few Google searches later ... I need what?!? An assembly qualified name of the type? What the fsck is that?

OK, typeof(Project.Models.Whatever).AssemblyQualifiedName returns the thing. Something like  "Project.Models.Whatever, DataModel, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null". Lovely. And
[AttributeProvider( typeof(Project.Models.Whatever).AssemblyQualifiedName, "FullName"] of course doesn't work: "An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type".

Thank you very much Microsoft! It would be too much work to provide a fourth constructor AttributeProviderAttribute(Type, String), right?

OK. The docs say that AttributeProviderAttribute class cannot be inherited, but what if I try??

public class BasedOnAttribute : AttributeProviderAttribute {
public BasedOnAttribute(Type type, string property)
: base(type.AssemblyQualifiedName, property) {
}
}

[BasedOn( typeof(Project.Models.Whatever), "FullName"]
public string Name {get;set;}

Build, run ... hey, it works and does what I wanted all along! What does the "This class cannot be inherited." in the AttributeProviderAttribute class mean then? Never mind. I got my cookie.