I have a this model:
public partial class UserPosition
{
...
public DbGeometry Position { get; set; }
...
}
I create this object and save it to the MySql DB (db is my DBContext):
var pos = new UserPosition
{
...
Position = GeographyHelper.CreatePoint(request.Latitude, request.Longitude),
...
};
db.UserPositions.Add(pos);
db.SaveChanges();
GeographyHelper.CreatePoint:
/// <summary>
/// Creates a DbGeometry point from latitude and longitude values
/// </summary>
/// <param name="latitude"></param>
/// <param name="longitude"></param>
/// <returns></returns>
public static DbGeometry CreatePoint(double latitude, double longitude)
{
var text = string.Format(CultureInfo.InvariantCulture.NumberFormat, "POINT({0} {1})", longitude, latitude);
// 4326 is most common coordinate system used by GPS/Maps
return DbGeometry.PointFromText(text, 4326);
}
The value is perfectly saved to to DB:
POINT (19.038934 47.531)
But when I read the value from the DB, the Position is null, other fields are OK:
var rec = db.UserPositions.FirstOrDefault()
I get the right record because the ID matches.
Why is the Position null?
Visual Studio 2015, .NET 4.5.2, EntityFramework 6.2.0, MySql.Data.Entity.EF6 6.9.12, MySql database version: 5.6.15.
It was globalization problem: decimal point was saved to the DB but couldn't parse the same geometry data when read because decimal comma was needed (for Double.TryParse()) because of the globalization. The fast solution was to set the globalization to en-US in web.config but I accept if you know better solution :)
<configuration>
<system.web>
<globalization uiCulture="en-US" culture="en-US"/>
...
Related
Per this Using System.Text.Json to Serialize an IConfiguration back to Json it seems that the limited type information that you can put into JSON is discarded.
You seem to be under the impression that IConfiguration objects are storing ints, bools, etc. (for example) corresponding to the JSON Element type. This is incorrect. All data within an IConfiguration is stored in stringified form. The base Configuration Provider classes all expect an IDictionary<string, string> filled with data. Even the JSON Configuration Providers perform an explicit ToString on the values.
I noticed this when I addressed the problem posed in that question with this extension method.
using System.Collections.Generic;
using System.Dynamic;
using Microsoft.Extensions.Configuration;
public static class ExtendConfig
{
public static dynamic AsDynamic(this IConfigurationRoot cr)
{
var result = new ExpandoObject();
var resultAsDict = result as IDictionary<string, object>;
foreach (var item in cr.AsEnumerable())
{
resultAsDict.Add(item.Key, item.Value);
}
return result;
}
}
This method reconstructs the graph but everything is now a string.
I could write my own parser and apply it to the original JSON string but that's a bit dire. Is there any way I can get this metadata so I can improve the fidelity of merged configs? I'm passing it through for consumption by JS which does notice the difference.
Merging is why I'm using the config extensions builder.
As IConfiguration doesn't provide the information about types, but System.Text.Json used by JsonConfigurationProvider does, the working solution (or a workaround) would be using the System.Text.Json deserializer directly to read the configuration file and match the types to the configuration keys.
But we have some minor issues to solve first. Like - where is the configuration file? We don't want to duplicate that information in code, we have to extract it from IConfiguration instance.
Then - match the concrete existing configuration key to the JSON document tree node. That will require either DFS or BFS tree traversal algorithm. I'll go for DFS (Depth First Search). In a nutshell - if you have expandable nodes, you put them in the stack in reverse order. Then you have a while loop that takes a node from the stack, if it has children, you put them on the same stack, if not - you just yield the node. As simple as that, and BFS is pretty similar, but nevermind.
There's one thing more: Newtonsoft.Json - a popular Nuget package that was at a time used even by Microsoft. That JSON serializer is a little slower than System.Text.Json, but it's more advanced, allowing the user to build a JSON document tree node by node.
Having this powerful tool makes creating a writeable JSON IConfiguration relatively easy, especially using some helpers like mine below.
Look at the SaveChanges() method. It walks through the IConfiguration nodes, matches appropriate JObject nodes by their path and copies the changes from IConfiguration instance to the JObject instance. Then you can just write the JSON file.
There's an ugly hack used to get the file. I get the private field containing the IConfigurationRoot instance, but that could be skipped if you already have the configuration root. Having the root you can get JsonConfigurationProvider from it, then it's just Source.Path property.
So here's the code. It's a part of the Woof.Toolkit and Woof.Config Nuget package, that provides writeable JSON configurations, some helper methods to them, and also Azure Key Vault client that uses a JSON configuration, with some helper methods to encrypt and decrypt sensitive data with keys stored on AKV.
This is the first release of ConfigurationExtensions class, so it might be sub-optimal in performance terms, but it works and illustrates how you can match the IConfiguration instance nodes with JObject nodes to get the types of the configuration properties.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
using Newtonsoft.Json.Linq;
namespace Woof.Config;
/// <summary>
/// Extensions for <see cref="IConfiguration"/> making JSON type <see cref="IConfiguration"/> writeable.
/// </summary>
public static class ConfigurationExtensions {
/// <summary>
/// Gets the configuration root element.
/// </summary>
/// <param name="configuration">Any <see cref="IConfiguration"/> part.</param>
/// <returns>Root element.</returns>
public static IConfigurationRoot? GetRoot(this IConfiguration configuration) {
if (configuration is IConfigurationRoot root) return root;
var rootField = configuration.GetType().GetField("_root", BindingFlags.Instance | BindingFlags.NonPublic);
return rootField?.GetValue(configuration) as IConfigurationRoot;
}
/// <summary>
/// Gets the first <see cref="JsonConfigurationProvider"/> if exists, null otherwise.
/// </summary>
/// <param name="root">Configuration root element.</param>
/// <returns><see cref="JsonConfigurationProvider"/> or null.</returns>
public static JsonConfigurationProvider? GetJsonConfigurationProvider(this IConfigurationRoot root)
=> root.Providers.OfType<JsonConfigurationProvider>().FirstOrDefault();
/// <summary>
/// Gets the first <see cref="JsonConfigurationProvider"/> if exists, null otherwise.
/// </summary>
/// <param name="config">Any <see cref="IConfiguration"/> part.</param>
/// <returns><see cref="JsonConfigurationProvider"/> or null.</returns>
public static JsonConfigurationProvider? GetJsonConfigurationProvider(this IConfiguration config)
=> config.GetRoot()?.GetJsonConfigurationProvider();
/// <summary>
/// Saves changes made to <see cref="IConfiguration"/> to the JSON file if exists.
/// </summary>
/// <param name="config">Any <see cref="IConfiguration"/> part.</param>
/// <exception cref="InvalidOperationException">Configuration does not have <see cref="JsonConfigurationProvider"/>.</exception>
public static void SaveChanges(this IConfiguration config) {
var provider = config.GetJsonConfigurationProvider();
if (provider is null) throw new InvalidOperationException("Can't get JsonConfigurationProvider");
var sourceJson = File.ReadAllText(provider.Source.Path);
var target = JObject.Parse(sourceJson);
var stack = new Stack<IConfigurationSection>();
foreach (IConfigurationSection section in config.GetChildren().Reverse()) stack.Push(section);
while (stack.TryPop(out var node)) {
var children = node.GetChildren();
if (children.Any()) foreach (var child in children.Reverse()) stack.Push(child);
else {
var jPath = GetJPath(node.Path);
var element = target.SelectToken(jPath);
var valueString =
element!.Type == JTokenType.Null
? "null" :
element!.Type == JTokenType.String ? $"\"{node.Value}\"" : node.Value;
element!.Replace(JToken.Parse(valueString));
}
}
File.WriteAllText(provider.Source.Path, target.ToString());
}
/// <summary>
/// Sets <paramref name="configuration"/>'s <paramref name="key"/> with specified <paramref name="value"/>.
/// </summary>
/// <param name="configuration">The configuration.</param>
/// <param name="key">The key of the configuration section.</param>
/// <param name="value">Value to set.</param>
/// <exception cref="InvalidOperationException">Not supported type as value.</exception>
public static void SetValue(this IConfiguration configuration, string key, object? value) {
var c = CultureInfo.InvariantCulture;
var valueString = value switch {
null => null,
string v => v,
Uri v => v.ToString(),
byte[] v => Convert.ToBase64String(v),
bool v => v.ToString(c),
int v => v.ToString(c),
decimal v => v.ToString(c),
double v => v.ToString(c),
uint v => v.ToString(c),
long v => v.ToString(c),
ulong v => v.ToString(c),
short v => v.ToString(c),
ushort v => v.ToString(c),
byte v => v.ToString(c),
sbyte v => v.ToString(c),
float v => v.ToString(c),
_ => throw new InvalidOperationException($"Cannot set value of type {value.GetType()}")
};
configuration[key] = valueString;
}
/// <summary>
/// Gets the path for JObject.SelectToken method.
/// </summary>
/// <param name="path"><see cref="IConfiguration"/> path.</param>
/// <returns><see cref="JObject"/> path.</returns>
private static string GetJPath(string path) => RxIConfigurationIndex.Replace(path, "[$1]").Replace(':', '.');
/// <summary>
/// Matches the <see cref="IConfiguration"/> indices.
/// </summary>
private static readonly Regex RxIConfigurationIndex = new(#":(\d+)", RegexOptions.Compiled);
}
Why JObject? Can JSON file represent only an object? No - it can represent any value, including null. But the JSON configuration MUST be an object. That's why I use JObject as my secondary configuration root.
I have a database table which currently holds geometric data in SRID 27700 (British National Grid). While retrieving the data however I need to transform it to SRID 4326 (WGS84). Is there any way to apply a function such as ST_Transform found in PostGIS to my data in order to get the result I need?
NOTE: The solution needs to be able to be implemented using T-SQL and not stored procedures etc. I have to be able to construct a statement and have it saved in a table as a string field for retrieval later. This is because my solution is database agnostic.
The way I am currently doing this in Oracle is as follows:
select CLUSTER_ID,
NUM_POINTS,
FEATURE_PK,
A.CELL_CENTROID.SDO_POINT.X,
A.CELL_CENTROID.SDO_POINT.Y,
A.CLUSTER_CENTROID.SDO_POINT.X,
A.CLUSTER_CENTROID.SDO_POINT.Y,
TO_CHAR (A.CLUSTER_EXTENT.GET_WKT ()),
TO_CHAR (A.CELL_GEOM.GET_WKT ()),
A.CLUSTER_EXTENT.SDO_SRID
from (SELECT CLUSTER_ID,
NUM_POINTS,
FEATURE_PK,
SDO_CS.transform (CLUSTER_CENTROID, 4326) cluster_centroid,
CLUSTER_EXTENT,
SDO_CS.transform (CELL_CENTROID, 4326) cell_centroid,
CELL_GEOM FROM :0) a
where sdo_filter( A.CELL_GEOM,
SDO_CS.transform(mdsys.sdo_geometry(2003, :1, NULL, mdsys.sdo_elem_info_array(1,1003,3),mdsys.sdo_ordinate_array(:2, :3, :4, :5)),81989)) = 'TRUE'
In PostgreSQL using PostGIS I am doing it like this:
select CLUSTER_ID,
NUM_POINTS,
FEATURE_PK, ST_X(a.CELL_CENTROID),
ST_Y(a.CELL_CENTROID),
ST_X(ST_TRANSFORM(a.CLUSTER_CENTROID, 4326)),
ST_Y(ST_TRANSFORM(a.CLUSTER_CENTROID, 4326)),
ST_AsText(a.CLUSTER_EXTENT),
ST_AsText(a.CELL_GEOM),
ST_SRID(a.CLUSTER_EXTENT)
FROM (SELECT CLUSTER_ID,
NUM_POINTS,
FEATURE_PK,
ST_TRANSFORM(ST_SetSRID(CLUSTER_CENTROID, 27700), 4326) cluster_centroid,
CLUSTER_EXTENT,
ST_TRANSFORM(ST_SetSRID(CELL_CENTROID, 27700), 4326) cell_centroid,
CELL_GEOM
from :0) AS a
where ST_Intersects(ST_Transform(ST_SetSRID(a.CELL_GEOM, 27700), :1), ST_Transform(ST_GeomFromText('POLYGON(('||:2||' '||:3||', '||:4||' '||:3||', '||:4||' '||:5||', '||:2||' '||:5||', '||:2||' '||:3||'))', 4326), :1))
You could wrap something like DotNetCoords in a SQL CLR function to do this.
See here:- http://www.doogal.co.uk/dotnetcoords.php
I've wrapped it in a CLR function to convert coordinates from Easting/Northing to Lat/Long which I think is what you are asking for. Once the CLR function is implemented it is a pure SQL solution (i.e. you can run it all in a Stored Procedure or View).
EDIT: I will post some sample code up here when I get to work tomorrow, hopefully it will help.
EDIT: You'll need to download the source code from http://www.doogal.co.uk/dotnetcoords.php and you will need Visual Studio to open and modify it. Documentation for the library is here http://www.doogal.co.uk/Help/Index.html
What you can do then is you can add a new class to the source files similar to this:-
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.SqlTypes;
using DotNetCoords;
using Microsoft.SqlServer.Server;
/// <summary>
/// Sql Server CLR functions for the DotNetCoords library.
/// </summary>
public class CLRFunctions
{
/// <summary>
/// Coordinateses the enumerable.
/// </summary>
/// <param name="Easting">The easting.</param>
/// <param name="Northing">The northing.</param>
/// <returns></returns>
private static IEnumerable<OSRef> CoordinatesEnumerable(double Easting, double Northing)
{
return new List<OSRef> { new OSRef(Easting,Northing) };
}
/// <summary>
/// Toes the lat long.
/// </summary>
/// <param name="Easting">The easting.</param>
/// <param name="Northing">The northing.</param>
/// <returns></returns>
[SqlFunction(FillRowMethodName = "FillRow")]
public static IEnumerable ToLatLong(double Easting, double Northing)
{
return CoordinatesEnumerable(Easting, Northing);
}
/// <summary>
/// Fills the row.
/// </summary>
/// <param name="obj">The obj.</param>
/// <param name="Lat">The lat.</param>
/// <param name="Long">The long.</param>
private static void FillRow(Object obj, out SqlDouble Lat, out SqlDouble Long)
{
OSRef Coordinates = (OSRef)obj;
LatLng latlong = Coordinates.ToLatLng();
latlong.ToWGS84();
Lat = new SqlDouble(latlong.Latitude);
Long = new SqlDouble(latlong.Longitude);
}
}
You will then need to build and import the assembly into SQL Server (replace paths with your own locations) (for some reason I cannot get the assembly to install when PERMISSION_SET is 'SAFE' so I would sort this first before installing in a production environment).
CREATE ASSEMBLY DotNetCoords
FROM N'C:\Projects\DotNetCoords\bin\Debug\DotNetCoords.dll'
WITH PERMISSION_SET = UNSAFE
GO
You'll then need to create a SQL Server function to interface to the CLR function:-
CREATE FUNCTION dbo.ToLatLong(#Easting float, #Northing float)
RETURNS TABLE
(Latitude float null, Longitude float null) with execute as caller
AS
EXTERNAL NAME [DotNetCoords].[CLRFunctions].[ToLatLong]
This is the CLR function installed then.
You should then be able to call the function direct from SQL Server to do your conversion (I have mixed up the numbers in this post too keep anonymity so they might not make sense here but the function does work fine).
/*------------------------
SELECT Latitude, Longitude FROM dbo.ToLatLong(327262, 357394)
------------------------*/
Latitude Longitude
52.13413530182533 -9.34267170569508
(1 row(s) affected)
To use it in a resultset you need to use the CROSS APPLY clause:-
/*------------------------
SELECT TOP 2 a.[Column 0] AS osaddessp,
a.[Column 9] AS east,
a.[Column 10] AS north,
c.[Latitude] AS lat,
c.[Longitude] AS long
FROM MyTable AS a CROSS APPLY ToLatLong (a.[Column 9], a.[Column 10]) AS c;
------------------------*/
osaddessp east north lat long
100134385607 327862 334794 52.3434530182533 -2.19342342569508
100123433149 780268 353406 52.3453417606796 -3.19252323679263
(10 row(s) affected)
Unfortunately, this simply isn't possible. SQL Server Spatial Tools provides a few reprojection functions, but they are only for a very few number of projections (and not the one you require).
There is an example from SQL server tools -- https://bitbucket.org/geographika/sql-server-spatial-tools/src/5ca44b55d3f3/SQL%20Scripts/projection_example.sql -- but it won't help you because they don't support the projection you are talking about.
So, you'll need to adopt a different solution -- either pre-process the data to add a new column with projected values, or reproject in your code.
Having a horrible time reading a custom configuration section from web.config:
I am using Configuration Section Designer (http://csd.codeplex.com/).
UPDATE:
here is the error I am getting:
System.Configuration.ConfigurationErrorsException: An error occurred creating the configuration section handler for SendToTestConfig/sendToTestIndexConfig: No parameterless constructor defined for this object. (C:\TFS\Mainline\Business.Utility.SendToTest\Business.Utility.SendToTest\web.config line 20) ---> System.MissingMethodException: No parameterless constructor defined for this object.
at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandle& ctor, Boolean& bNeedSecurityCheck)
at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean fillCache)
at System.RuntimeType.CreateInstanceImpl(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean fillCache)
Here is my auto-generated configuration section:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.225
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Business.Utility.SendToTest.Configuration
{
/// <summary>
/// The SendToTestIndexConfig Configuration Section.
/// </summary>
public partial class SendToTestIndexConfig : global::System.Configuration.ConfigurationSection
{
#region Singleton Instance
/// <summary>
/// The XML name of the SendToTestIndexConfig Configuration Section.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("ConfigurationSectionDesigner.CsdFileGenerator", "2.0.0.0")]
internal const string SendToTestIndexConfigSectionName = "sendToTestIndexConfig";
/// <summary>
/// Gets the SendToTestIndexConfig instance.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("ConfigurationSectionDesigner.CsdFileGenerator", "2.0.0.0")]
public static global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig Instance
{
get
{
return ((global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig)(global::System.Configuration.ConfigurationManager.GetSection(global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig.SendToTestIndexConfigSectionName)));
}
}
#endregion
#region Xmlns Property
/// <summary>
/// The XML name of the <see cref="Xmlns"/> property.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("ConfigurationSectionDesigner.CsdFileGenerator", "2.0.0.0")]
internal const string XmlnsPropertyName = "xmlns";
/// <summary>
/// Gets the XML namespace of this Configuration Section.
/// </summary>
/// <remarks>
/// This property makes sure that if the configuration file contains the XML namespace,
/// the parser doesn't throw an exception because it encounters the unknown "xmlns" attribute.
/// </remarks>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("ConfigurationSectionDesigner.CsdFileGenerator", "2.0.0.0")]
[global::System.Configuration.ConfigurationPropertyAttribute(global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig.XmlnsPropertyName, IsRequired=false, IsKey=false, IsDefaultCollection=false)]
public string Xmlns
{
get
{
return ((string)(base[global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig.XmlnsPropertyName]));
}
}
#endregion
#region IsReadOnly override
/// <summary>
/// Gets a value indicating whether the element is read-only.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("ConfigurationSectionDesigner.CsdFileGenerator", "2.0.0.0")]
public override bool IsReadOnly()
{
return false;
}
#endregion
#region appGroups Property
/// <summary>
/// The XML name of the <see cref="appGroups"/> property.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("ConfigurationSectionDesigner.CsdFileGenerator", "2.0.0.0")]
internal const string appGroupsPropertyName = "appGroups";
/// <summary>
/// Gets or sets the appGroups.
/// </summary>
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("ConfigurationSectionDesigner.CsdFileGenerator", "2.0.0.0")]
[global::System.ComponentModel.DescriptionAttribute("The appGroups.")]
[global::System.Configuration.ConfigurationPropertyAttribute(global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig.appGroupsPropertyName, IsRequired=true, IsKey=false, IsDefaultCollection=false)]
public global::Business.Utility.SendToTest.Configuration.AppGroupSettingsCollection appGroups
{
get
{
return ((global::Business.Utility.SendToTest.Configuration.AppGroupSettingsCollection)(base[global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig.appGroupsPropertyName]));
}
set
{
base[global::Business.Utility.SendToTest.Configuration.SendToTestIndexConfig.appGroupsPropertyName] = value;
}
}
#endregion
}
}
namespace Business.Utility.SendToTest.Configuration
{
...
And here is my config file:
<?xml version="1.0"?>
<configuration>
<configSections>
<sectionGroup name="SendToTestConfig" type="System.Configuration.ConfigurationSectionGroup, System.Configuration, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<section name="sendToTestIndexConfig" type="Business.Utility.SendToTest.Configuration.SendToTestIndexConfig, Business.Utility.SendToTest.Configuration"/>
</sectionGroup>
etc.
</configSections>
<SendToTestConfig xmlns="Business.Utility.SendToTest.Configuration">
<sendToTestIndexConfig>
<appGroups>
<!-- TODO Change indexFileNamePrefix to desired value-->
<appGroupSettings name="ItemBased" indexFileNamePrefix="S" indexFolderLocation="C:\TFS\Mainline\Business.Utility.SendToTest\Business.Utility.SendToTest" imageFolderLocation="C:\TFS\Mainline\Business.Utility.SendToTest\Business.Utility.SendToTest">
<topicEntries>
<topicEntry topicIDSuffix="DATE" addDuplicateVSAMEntry="true">
<itemSubFields>
<subField index="0" typeName="Date"/>
</itemSubFields>
</topicEntry>
<topicEntry topicIDSuffix="Item" addDuplicateVSAMEntry="true">
<itemSubFields>
<subField index="0" typeName="ItemNumber"/>
</itemSubFields>
</topicEntry>
<topicEntry topicIDSuffix="DESCR">
<itemSubFields>
<subField index="0" typeName="LongDescription"/>
</itemSubFields>
</topicEntry>
</topicEntries>
</appGroupSettings>
</appGroups>
</sendToTestIndexConfig>
</SendToTestConfig>
...
</configuration>
I have tried all of the following, but I keep getting null for the first few config (which I think makes sense), and getting the abovementioned exception for the rest.
//These return null:
SendToTestIndexConfig config = SendToTestIndexConfig.Instance;
//SendToTestIndexConfig config = (SendToTestIndexConfig) ConfigurationManager.GetSection("sendToTestIndexConfig");
//SendToTestIndexConfig configb = (SendToTestIndexConfig)WebConfigurationManager.GetSection("sendToTestIndexConfig");
//SendToTestIndexConfig configc = (SendToTestIndexConfig)WebConfigurationManager.OpenWebConfiguration(null).GetSection("sendToTestIndexConfig");
//SendToTestIndexConfig configd = (SendToTestIndexConfig)WebConfigurationManager.GetWebApplicationSection("sendToTestIndexConfig");
//SendToTestIndexConfig configf = (SendToTestIndexConfig)WebConfigurationManager.GetSection("sendToTestIndexConfig");
////These throw a "parameterless constructor error" on object "SendToTestConfig/sendToTestIndexConfig"
//SendToTestIndexConfig configg = (SendToTestIndexConfig)WebConfigurationManager.GetSection("SendToTestConfig/sendToTestIndexConfig");
//SendToTestIndexConfig configh = (SendToTestIndexConfig)WebConfigurationManager.OpenWebConfiguration(null).GetSection("SendToTestConfig/sendToTestIndexConfig");
//SendToTestIndexConfig configi = (SendToTestIndexConfig)WebConfigurationManager.GetWebApplicationSection("SendToTestConfig/sendToTestIndexConfig");
//SendToTestIndexConfig configj = (SendToTestIndexConfig)WebConfigurationManager.GetSection("SendToTestConfig/sendToTestIndexConfig");
I'm guessing it has something to do with my naming. ConfigurationManager.AppSettings works fine, so I know I have the right web.config.
Actually, the solution to the problem is succinctly stated in an example for the GenericEnumConverter on MSDN. I did the same thing you probably did and explicitly set the [TypeConverter(typeof(GenericEnumConverter))] attrbibute on one of my configuration section properties and got the same error. According to the documentation for GenericEnumConverter linked to above, you do not need to set this attribute in order to use the GenericEnumConverter type converter--it is called implicitly by the framework. Remove that attribute from your configuration property specifications, and this error should disappear and everything should just work.
Here is an example of a configuration section property that uses an Enum:
public enum UsernameFormat
{
DownLevelDomainName,
UsernameOnly,
UserPrincipalName
}
public class WindowsADElement : ConfigurationElement
{
// This property will implicitly use the GenericEnumConverter type converter.
[ConfigurationProperty("usernameFormat", IsRequired=true, DefaultValue=UsernameFormat.UserPrincipalName)]
public UsernameFormat UsernameFormat
{
get { return (UsernameFormat)this["usernameFormat"]; }
set { this["usernameFormat"] = value; }
}
Then, to use it in code:
MyConfigurationSection config = ConfigurationManager.GetSection("myConfigurationSection") as MyConfigurationSection;
UsernameFormat format = config.UsernameLookup.WindowsAD.UsernameFormat;
Hope that helps.
I am not sure if this is the best solution, but I was able to get around the problem. The error was because I was using the GenericEnumTypeConverter class to convert the config strings to the AppGroup and SubFieldTypes enums. I created my own custom TypeConverters, and it solved the problem. Apparently, GenericEnumTypeConverter has no parameterless constructors, requiring the enum type for the constructor. Would love to know if there is a way to use GenericEnumTypeConverter, but this worked for me.
This clued me in on the answer: Configuration Error With Custom Behaviour
I have implemented Generic repository which only depends on IUnitOfWork (in Infrastructure.Repositories Library) and also haven't used any references to Entity Framework 4.1 dll. Entity Framework's DbContext wrapped with EFUnitOfWork which is in different class library called Infrastructure.EntityFramework. However, I came across some difficulty with Linq to Entity query that may force me to include direct dependancy with repository and EF 4.1 library.
In one of my class repository I need to use following query with a join. How can I overcome DBContext usage in my repository ?
var result = from cc in ProjectXEFDbContext.CurrentContext().PurchaseOrderLineItemCollection
join bb in GetQuery() on cc.PurchaseOrderId equals bb.Id
where bb.Id == purchaseOrder.Id && cc.Total > 50
select cc;
I made my repositories to expose IQuerables over DbContext. DbContext wrap with UnitOfWork as follows
My Repository base goes like this
/// <summary>
/// Gets the query.
/// </summary>
/// <returns></returns>
public IQueryable<TEntity> GetQuery()
{
return this.UnitOfWork.GetQuery<TEntity>();
}
/// <summary>
/// Loads the type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public IQueryable<T> LoadType<T>() where T : class
{
return this.UnitOfWork.GetQuery<T>();
}
my unit of work implementation goes here
/// <summary>
/// Gets the query.
/// </summary>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <returns></returns>
public IQueryable<TEntity> GetQuery<TEntity>() where TEntity:class
{
return this.DbContext.Set<TEntity>();
}
Changes to my query as follows
var result = from cc in GetQuery()
join bb in LoadType<PurchaseOrderLineItem>() on cc.Id equals bb.PurchaseOrderId
where cc.Id == purchaseOrder.Id && bb.Total > 50
select bb;
I want to dynamically create a database using my DataContext's CreateDatabase Method. I have manually created mapping classes and tested them. But as soon as I add the Expression Column (see below) the creation fails with an SqlCeException and I am unable to find out the exact reason.
/// <summary>
/// The sum of ratings for this document.
/// </summary>
[Column]
public Nullable<float> RatingSum { get; set; }
/// <summary>
/// The number of times the document was rated.
/// </summary>
[Column]
public Nullable<float> RatingCount { get; set; }
/// <summary>
/// Average rating as calculated from RatingSum and RatingCount
/// </summary>
[Column(AutoSync=AutoSync.Never, IsDbGenerated=true, Expression = "RatingSum * RatingCount", DbType="REAL")]
public Nullable<float> Rating { get; set; }
Omitting DbType or changing it to FLOAT didn't help, AutoSync.Always doesn't help either, omitting AutoSync and IsDbGenerated attributes doesn't work either. What am I doing wrong? Or is this simply not supported for a dynamically created db?
Linq to Sql is itself very poor in creating the whole database, at least, it does not create indecies, so the first thing you must do, is to store the SQL code which does and run it whenever you need to create a database.