{"action":"created","issue":{"url":"https://api.github.com/repos/npgsql/npgsql/issues/458","labels_url":"https://api.github.com/repos/npgsql/npgsql/issues/458/labels{/name}","comments_url":"https://api.github.com/repos/npgsql/npgsql/issues/458/comments","events_url":"https://api.github.com/repos/npgsql/npgsql/issues/458/events","html_url":"https://github.com/npgsql/npgsql/pull/458","id":54645641,"number":458,"title":"Add support for user-defined enum types","user":{"login":"Emill","id":1660778,"avatar_url":"https://avatars.githubusercontent.com/u/1660778?v=3","gravatar_id":"","url":"https://api.github.com/users/Emill","html_url":"https://github.com/Emill","followers_url":"https://api.github.com/users/Emill/followers","following_url":"https://api.github.com/users/Emill/following{/other_user}","gists_url":"https://api.github.com/users/Emill/gists{/gist_id}","starred_url":"https://api.github.com/users/Emill/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/Emill/subscriptions","organizations_url":"https://api.github.com/users/Emill/orgs","repos_url":"https://api.github.com/users/Emill/repos","events_url":"https://api.github.com/users/Emill/events{/privacy}","received_events_url":"https://api.github.com/users/Emill/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":16,"created_at":"2015-01-17T01:33:39Z","updated_at":"2015-01-20T14:46:17Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/npgsql/npgsql/pulls/458","html_url":"https://github.com/npgsql/npgsql/pull/458","diff_url":"https://github.com/npgsql/npgsql/pull/458.diff","patch_url":"https://github.com/npgsql/npgsql/pull/458.patch"},"body":"This adds support for user-defined enum types.\r\n\r\nIf a user has created an enum like this in the database:\r\n\r\n    CREATE TYPE test_enum AS ENUM ('label1', 'label2', 'label3')\r\n\r\nand has defined a .NET type like this:\r\n\r\n    enum TestEnum\r\n    {\r\n        label1,\r\n        \r\n        label2,\r\n        \r\n        [EnumLabel(\"label3\")]\r\n        Label3\r\n    }\r\n\r\nthe user can register it for use in Npgsql like this:\r\n\r\n    NpgsqlUserTypes.RegisterEnum<TestEnum>(\"test_enum\");\r\n\r\nThen it can be used in parameters in these different ways:\r\n\r\n    cmd.CommandText = \"Select :p1, :p2, :p3, :p4, :p5\";\r\n    \r\n    cmd.Parameters.AddWithValue(\"p1\", TestEnum.label1);\r\n    cmd.Parameters.Add(new NpgsqlParameter { ParameterName = \"p2\", NpgsqlDbType = NpgsqlDbType.Enum, EnumType = typeof(TestEnum), Value = TestEnum.label2 });\r\n    cmd.Parameters.AddWithValue(\"p3\", new[] { TestEnum.label1, TestEnum.Label3 });\r\n    cmd.Parameters.Add(new NpgsqlParameter { ParameterName = \"p4\", NpgsqlDbType = NpgsqlDbType.Array | NpgsqlDbType.Enum, EnumType = typeof(TestEnum), Value = new[] { TestEnum.label1, TestEnum.Label3 } });\r\n    cmd.Parameters.Add(new NpgsqlParameter { ParameterName = \"p5\", NpgsqlDbType = NpgsqlDbType.Enum, EnumType = typeof(TestEnum), Value = DBNull.Value });\r\n\r\nA data reader's GetValue() will return the value converted to the enum type the user has registered.\r\n\r\nSome questions:\r\n* EnumLabelAttribute can be used to specify that the value is named differently in the datbase, for example to deal with case style. But now the name of the type is passed as an argument to RegisterEnum. Would it be better to have an attribute of the enum class instead, and if such an attribute is missing, the name of the enum type is used instead?\r\n* Now RegisterEnum is a generic method. Would it be better to have the method take a Type parameter instead?\r\n* RegisterEnum is not thread safe. Is that a problem?\r\n* RegisterEnum must also be called before the Connector is created (before the type handler registry is set up). This is so the type handler registry can find the type OIDs to use. Another possibility would be to decrease the demands so it must be called before the Connection is created, but that would add complexity to the type handler registry if OIDs could be added after creation (must be added to each idle connector in the pool). What do you think here? It shouldn't be that hard for the users to add their mappings at launch of their applications, I think..."},"comment":{"url":"https://api.github.com/repos/npgsql/npgsql/issues/comments/70663778","html_url":"https://github.com/npgsql/npgsql/pull/458#issuecomment-70663778","issue_url":"https://api.github.com/repos/npgsql/npgsql/issues/458","id":70663778,"user":{"login":"franciscojunior","id":829924,"avatar_url":"https://avatars.githubusercontent.com/u/829924?v=3","gravatar_id":"","url":"https://api.github.com/users/franciscojunior","html_url":"https://github.com/franciscojunior","followers_url":"https://api.github.com/users/franciscojunior/followers","following_url":"https://api.github.com/users/franciscojunior/following{/other_user}","gists_url":"https://api.github.com/users/franciscojunior/gists{/gist_id}","starred_url":"https://api.github.com/users/franciscojunior/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/franciscojunior/subscriptions","organizations_url":"https://api.github.com/users/franciscojunior/orgs","repos_url":"https://api.github.com/users/franciscojunior/repos","events_url":"https://api.github.com/users/franciscojunior/events{/privacy}","received_events_url":"https://api.github.com/users/franciscojunior/received_events","type":"User","site_admin":false},"created_at":"2015-01-20T14:46:17Z","updated_at":"2015-01-20T14:46:17Z","body":"> I don't think C# allows you to extend enums...\r\n\r\nWhooops, sorry about that. (Feeling ashamed for saying such thing... :(   )\r\n\r\nI think I had in mind the idea of having a custom type to hold the enum value which would have all the information (I think this is the role of the EnumType)\r\n\r\n> The EnumType field would be totally optional when sending the enum, if the user leaves it unset it's still inferred from the Value (or NpgsqlValue) fields. It's just a way to let the user specify the enum type without loading an actual value - not terribly important but why not.\r\n\r\nI see. I was worried this could add a very different way of interacting with the data provider. But I think, as usual, that I'm over thinking about it. :)\r\n\r\n\r\n\r\n> Well, we need to choose one of the options: either throw the exception on startup, or when it happens (on actual reading of a value). I guess that if we can throw exceptions from the read handler, it may be better to do as @emill suggested and throw them on actual reading (although there are arguments for the other options as well...)\r\n\r\nI see. Ok, +1 to throw the exception on actual reading. \r\n\r\nThe advantage I saw doing the checking up front was that we wouldn't need to handle the problem later and the user would know there is a problem when registering the enum.\r\n\r\nOn the other side, if we handle it in the actual reading, we don't need to make any check upfront and we would only report an error when it really occurs. (I mean, even if the enums are out of sync, we would only get a problem if the out of sync value is actually used.)\r\n"}}