I have a SQL Server 2008 database with a geography column which is generated by System.Data.Entity.Spatial.DbGeography in Entity Framework 6.0.0-alpha3.
Now I need to read that column with a SqlDataReader. But I have no idea how to do this. Using the old context is not an option. I tried to cast it as DbGeography:
Location = (DbGeography)reader.GetValue(index)
But I get this error:
Unable to cast object of type 'Microsoft.SqlServer.Types.SqlGeography'
to type 'System.Data.Entity.Spatial.DbGeography'
Do you have any suggestion?
Well, it was simple. I was just confused. But instead of deleting the question, I'll post the answer to others who they have the same question.
// read the value as dynamic:
dynamic temp = reader.GetValue(index);
// the temp contains Lat and Long properties:
var text = string.Format("POINT({0:R} {1:R})", temp.Long, temp.Lat);
// the temp also contains the STSrid as coordinate system id:
var srid = temp.STSrid.Value;
// the rest is really simple:
Location = System.Data.Entity.Spatial.DbGeography.PointFromText(text, srid);
If your geography is a point, you can use:
SELECT MyColumn.Lat, MyColumn.Long ...
reader.GetDouble(0);
reader.GetDouble(1);
Related
I have some images stored in the default cluster in my OrientDB database. I stored them by implementing the code given by the documentation in the case of the use of multiple ORecordByte (for large content): http://orientdb.com/docs/2.1/Binary-Data.html
So, I have two types of object in my default cluster. Binary datas and ODocument whose field 'data' lists to the different record of binary datas.
Some of the ODocument records' RID are used in some other classes. But, the other records are orphanized and I would like to be able to retrieve them.
My idea was to use
select from cluster:default where #rid not in (select myField from MyClass)
But the problem is that I retrieve the other binary datas and I just want the record with the field 'data'.
In addition, I prefer to have a prettier request because I don't think the "not in" clause is really something that should be encouraged. Is there something like a JOIN which return records that are not joined to anything?
Can you help me please?
To resolve my problem, I did like that. However, I don't know if it is the right way (the more optimized one) to do it:
I used the following SQL request:
SELECT rid FROM (FIND REFERENCES (SELECT FROM CLUSTER:default)) WHERE referredBy = []
In Java, I execute it with the use of the couple OCommandSQL/OCommandRequest and I retrieve an OrientDynaElementIterable. I just iterate on this last one to retrieve an OrientVertex, contained in another OrientVertex, from where I retrieve the RID of the orpan.
Now, here is some code if it can help someone, assuming that you have an OrientGraphNoTx or an OrientGraph for the 'graph' variable :)
String cmd = "SELECT rid FROM (FIND REFERENCES (SELECT FROM CLUSTER:default)) WHERE referredBy = []";
List<String> orphanedRid = new ArrayList<String>();
OCommandRequest request = graph.command(new OCommandSQL(cmd));
OrientDynaElementIterable objects = request.execute();
Iterator<Object> iterator = objects.iterator();
while (iterator.hasNext()) {
OrientVertex obj = (OrientVertex) iterator.next();
OrientVertex orphan = obj.getProperty("rid");
orphanedRid.add(orphan.getIdentity().toString());
}
I am creating a small database for 'trial', per se. I have tried a few setting outs ([{key: value}, {key: value}]. But, I need a solution that can be called by an ID (12345), a name (Rob Alsod), an area (A4 (Like an apartment building)), or a job (Manager, Administrator, etc). So, something like a dictionary (which can only be called by one key) will not work. I tried making a 'Person' class, but I need a way to easily keep track of the classes, and also to assign them easily. For example,
for whatever in whatever:
what = Person(name = 'Rob Alsod', id = 12345, job = 'Admin', area = 'A1') # What can I make this iterate with? (Badly formed question)
My point is, as it loops through, I cannot assign it to the same thing again and again.
Could someone try to make sense of what I am saying, and suggest a way to format my database?
You could easily use SQLAlchemy with SQLite. SQL queries are only a few lines of code away:
from sqlalchemy import *
db = create_engine('sqlite:///people.db')
metadata = MetaData()
user = Table('people', metadata,
Column('ID', Integer, primary_key = True),
Column('name', String(16), nullable = False),
Column('area', String(16)),
Column('job', String(60))
)
After that it is easy connection = db.connect(); result = connection.execute("select ID from people")
On Linux, pip and apt-get make the install a breeze as well.
HTH, Phil
If you want small database for storing any objects identified by key you can use shelve module:
http://docs.python.org/3.2/library/shelve.html
This works:
var i = (from x in db.Test
where x.Id == 1
select x).First();
db.Test.DeleteOnSubmit(i);
db.SubmitChanges();
I get a cast error for this (int/string):
var i = db.Test.Single(x => x.Id == 1);
db.Test.DeleteOnSubmit(i);
db.SubmitChanges();
I was also able to make an update using Single sucesssfully on the same table. Any clues?
Update
Here's more of the error message:
Here's more:
[InvalidCastException: Unable to cast object of type 'System.Int32' to type 'System.String'.]
System.Data.Linq.SingleKeyManager2.TryCreateKeyFromValues(Object[] values, V& v) +134
System.Data.Linq.IdentityCache2.Find(Object[] keyValues) +57
System.Data.Linq.StandardIdentityManager.Find(MetaType type, Object[] keyValues) +51
System.Data.Linq.CommonDataServices.GetCachedObject(MetaType type, Object[] keyValues) +113
System.Data.Linq.ChangeProcessor.GetOtherItem(MetaAssociation assoc, Object instance) +235
System.Data.Linq.ChangeProcessor.BuildEdgeMaps() +510
System.Data.Linq.ChangeProcessor.SubmitChanges(ConflictMode failureMode) +137
System.Data.Linq.DataContext.SubmitChanges(ConflictMode failureMode) +453
System.Data.Linq.DataContext.SubmitChanges() +38
What's the data type of your id field?
I just ran the following code against a dummy database that has data in it where my key field is of type int:
using (var db = new dbDataContext())
{
var x = db.items.Single(i => i.fld_key == 1);
db.items.DeleteOnSubmit(x);
db.SubmitChanges();
var y = (from i in db.items
where i.fld_key == 2
select i).First();
db.items.DeleteOnSubmit(y);
db.SubmitChanges();
}
I had no problems with either, and checking the database, items 1 and 2 were both deleted successfully.
Are you attempting to delete an item that doesn't exist in the second code block - as you deleted it with the first. This would mean that y is actually null and you're attempting to DeleteOnSubmit passing null which will give you an invalid cast as you can't cast null.
Try attempting to delete a different item in your second code block, I would imagine it would work in that case.
Change .Single to .SingleOrDefault and wrap your delete lines to check for null:
if(null != x)
{
db.items.DeleteOnSubmit(x);
db.SubmitChanges();
}
I would wager this will fix the problem you're seeing.
Edit Looking at the exception you've posted, I'm wondering if you changed the data type of your table to int after you generated your dbml. Reload the table into your dbml and see if that fixes the issue. Either that, or the issue is the other way around and you changed it to VarChar/NVarChar and haven't updated your dbml.
I would definitely take a look at your entities and make sure that the data types match those in your data table in your underlying database.
Have you changed the datatypes in your database lately? (Maybe those types currently differ from your generated classes)
Similarly, if you're using a stored procedure for deleting items, make sure the parameter types match up to your generated types.
from f in CUSTOMERS
where depts.Contains(f.DEPT_ID)
select f.NAME
depts is a list (IEnumerable<int>) of department ids
This query works fine until you pass a large list (say around 3000 dept ids) .. then I get this error:
The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Too many parameters were provided in this RPC request. The maximum is 2100.
I changed my query to:
var dept_ids = string.Join(" ", depts.ToStringArray());
from f in CUSTOMERS
where dept_ids.IndexOf(Convert.ToString(f.DEPT_id)) != -1
select f.NAME
using IndexOf() fixed the error but made the query slow. Is there any other way to solve this? thanks so much.
My solution (Guids is a list of ids you would like to filter by):
List<MyTestEntity> result = new List<MyTestEntity>();
for(int i = 0; i < Math.Ceiling((double)Guids.Count / 2000); i++)
{
var nextGuids = Guids.Skip(i * 2000).Take(2000);
result.AddRange(db.Tests.Where(x => nextGuids.Contains(x.Id)));
}
this.DataContext = result;
Why not write the query in sql and attach your entity?
It's been awhile since I worked in Linq, but here goes:
IQuery q = Session.CreateQuery(#"
select *
from customerTable f
where f.DEPT_id in (" + string.Join(",", depts.ToStringArray()) + ")");
q.AttachEntity(CUSTOMER);
Of course, you will need to protect against injection, but that shouldn't be too hard.
You will want to check out the LINQKit project since within there somewhere is a technique for batching up such statements to solve this issue. I believe the idea is to use the PredicateBuilder to break the local collection into smaller chuncks but I haven't reviewed the solution in detail because I've instead been looking for a more natural way to handle this.
Unfortunately it appears from Microsoft's response to my suggestion to fix this behavior that there are no plans set to have this addressed for .NET Framework 4.0 or even subsequent service packs.
https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=475984
UPDATE:
I've opened up some discussion regarding whether this was going to be fixed for LINQ to SQL or the ADO.NET Entity Framework on the MSDN forums. Please see these posts for more information regarding these topics and to see the temporary workaround that I've come up with using XML and a SQL UDF.
I had similar problem, and I got two ways to fix it.
Intersect method
join on IDs
To get values that are NOT in list, I used Except method OR left join.
Update
EntityFramework 6.2 runs the following query successfully:
var employeeIDs = Enumerable.Range(3, 5000);
var orders =
from order in Orders
where employeeIDs.Contains((int)order.EmployeeID)
select order;
Your post was from a while ago, but perhaps someone will benefit from this. Entity Framework does a lot of query caching, every time you send in a different parameter count, that gets added to the cache. Using a "Contains" call will cause SQL to generate a clause like "WHERE x IN (#p1, #p2.... #pn)", and bloat the EF cache.
Recently I looked for a new way to handle this, and I found that you can create an entire table of data as a parameter. Here's how to do it:
First, you'll need to create a custom table type, so run this in SQL Server (in my case I called the custom type "TableId"):
CREATE TYPE [dbo].[TableId] AS TABLE(
Id[int] PRIMARY KEY
)
Then, in C#, you can create a DataTable and load it into a structured parameter that matches the type. You can add as many data rows as you want:
DataTable dt = new DataTable();
dt.Columns.Add("id", typeof(int));
This is an arbitrary list of IDs to search on. You can make the list as large as you want:
dt.Rows.Add(24262);
dt.Rows.Add(24267);
dt.Rows.Add(24264);
Create an SqlParameter using the custom table type and your data table:
SqlParameter tableParameter = new SqlParameter("#id", SqlDbType.Structured);
tableParameter.TypeName = "dbo.TableId";
tableParameter.Value = dt;
Then you can call a bit of SQL from your context that joins your existing table to the values from your table parameter. This will give you all records that match your ID list:
var items = context.Dailies.FromSqlRaw<Dailies>("SELECT * FROM dbo.Dailies d INNER JOIN #id id ON d.Daily_ID = id.id", tableParameter).AsNoTracking().ToList();
You could always partition your list of depts into smaller sets before you pass them as parameters to the IN statement generated by Linq. See here:
Divide a large IEnumerable into smaller IEnumerable of a fix amount of item
I'm designing a table in SQL Server 2008 that will store a list of users and a Google Maps co-ordinate (longitude & latitude).
Will I need two fields, or can it be done with 1?
What's the best (or most common) data-type to use for storing this kind of data?
Fair Warning! Before taking the advice to use the GEOGRAPHY type, make sure you are not planning on using Linq or Entity Framework to access the data because it's not supported (as of November 2010) and you will be sad!
Update Jul 2017
For those reading this answer now, it is obsolete as it refers to backdated technology stack. See comments for more details.
Take a look at the new Spatial data-types that were introduced in SQL Server 2008. They are designed for this kind of task and make indexing and querying much easier and more efficient.
More information:
MS TechNet: SQL Server 2008 Spatial Data Types,
MSDN: Working with Spatial Data (Database Engine).
I don't know the answer for SQL Server but...
In MySQL save it as FLOAT( 10, 6 )
This is the official recommendation from the Google developer documentation.
CREATE TABLE `coords` (
`lat` FLOAT( 10, 6 ) NOT NULL ,
`lng` FLOAT( 10, 6 ) NOT NULL ,
) ENGINE = MYISAM ;
The way I do it: I store the latitude and longitude and then I have a third column which is a automatic derived geography type of the 1st two columns. The table looks like this:
CREATE TABLE [dbo].[Geopoint]
(
[GeopointId] BIGINT NOT NULL PRIMARY KEY IDENTITY,
[Latitude] float NOT NULL,
[Longitude] float NOT NULL,
[ts] ROWVERSION NOT NULL,
[GeographyPoint] AS ([geography]::STGeomFromText(((('POINT('+CONVERT([varchar](20),[Longitude]))+' ')+CONVERT([varchar](20),[Latitude]))+')',(4326)))
)
This gives you the flexibility of spatial queries on the geoPoint column and you can also retrieve the latitude and longitude values as you need them for display or extracting for csv purposes.
I hate to be a contrarian to those who said "here is a new type, let's use it". The new SQL Server 2008 spatial types have some pros to it - namely efficiency, however you can't blindly say always use that type. It really depends on some bigger picture issues.
As an example, integration. This type has an equivilent type in .Net - but what about interop? What about supporting or extending older versions of .Net? What about exposing this type across the service layer to other platforms? What about normalization of data - maybe you are interested in lat or long as standalone pieces of information. Perhaps you've already written complex business logic to handle long/lat.
I'm not saying that you shouldn't use the spatial type - in many cases you should. I'm just saying you should ask some more critical questions before going down that path. For me to answer your question most accurately I would need to know more about your specific situation.
Storing long/lat separately or in a spatial type are both viable solutions, and one may be preferable to the other depending on your own circumstances.
What you want to do is store the Latitude and Longitude as the new SQL2008 Spatial type -> GEOGRAPHY.
Here's a screen shot of a table, which I have.
alt text http://img20.imageshack.us/img20/6839/zipcodetable.png
In this table, we have two fields that store geography data.
Boundary: this is the polygon that is the zip code boundary
CentrePoint: this is the Latitude / Longitude point that represents the visual middle point of this polygon.
The main reason why you want to save it to the database as a GEOGRAPHY type is so you can then leverage all the SPATIAL methods off it -> eg. Point in Poly, Distance between two points, etc.
BTW, we also use Google's Maps API to retrieve lat/long data and store that in our Sql 2008 DB -- so this method does work.
SQL Server has support for spatial related information. You can see more at http://www.microsoft.com/sqlserver/2008/en/us/spatial-data.aspx.
Alternativly you can store the information as two basic fields, usually a float is the standard data type reported by most devices and is accurate enough for within an inch or two - more than adequate for Google Maps.
NOTE: This is a recent answer based on recent SQL server, .NET stack updates
latitute and longitude from google Maps should be stored as Point(note capital P) data in SQL server under geography data type.
Assuming your current data is stored in a table Sample as varchar under columns lat and lon, below query will help you convert to geography
alter table Sample add latlong geography
go
update Sample set latlong= geography::Point(lat,lon,4326)
go
PS: Next time when you do a select on this table with geography data, apart from Results and Messages tab, you will also get Spatial results tab like below for visualization
If you are using Entity Framework 5 < you can use DbGeography. Example from MSDN:
public class University
{
public int UniversityID { get; set; }
public string Name { get; set; }
public DbGeography Location { get; set; }
}
public partial class UniversityContext : DbContext
{
public DbSet<University> Universities { get; set; }
}
using (var context = new UniversityContext ())
{
context.Universities.Add(new University()
{
Name = "Graphic Design Institute",
Location = DbGeography.FromText("POINT(-122.336106 47.605049)"),
});
context. Universities.Add(new University()
{
Name = "School of Fine Art",
Location = DbGeography.FromText("POINT(-122.335197 47.646711)"),
});
context.SaveChanges();
var myLocation = DbGeography.FromText("POINT(-122.296623 47.640405)");
var university = (from u in context.Universities
orderby u.Location.Distance(myLocation)
select u).FirstOrDefault();
Console.WriteLine(
"The closest University to you is: {0}.",
university.Name);
}
https://msdn.microsoft.com/en-us/library/hh859721(v=vs.113).aspx
Something I struggled with then I started using DbGeography was the coordinateSystemId. See the answer below for an excellent explanation and source for the code below.
public class GeoHelper
{
public const int SridGoogleMaps = 4326;
public const int SridCustomMap = 3857;
public static DbGeography FromLatLng(double lat, double lng)
{
return DbGeography.PointFromText(
"POINT("
+ lng.ToString() + " "
+ lat.ToString() + ")",
SridGoogleMaps);
}
}
https://stackoverflow.com/a/25563269/3850405
If you are just going to substitute it into a URL I suppose one field would do - so you can form a URL like
http://maps.google.co.uk/maps?q=12.345678,12.345678&z=6
but as it is two pieces of data I would store them in separate fields
Store both as float, and use unique key words on them.i.em
create table coordinates(
coord_uid counter primary key,
latitude float,
longitude float,
constraint la_long unique(latitude, longitude)
);