How to send regular in-line SQL in entity framework - sql

Now don't go getting all huffy just yet. This is only for a very specific situation. So rather than asking why I would EVER want to send in-line string SQL through EF, let's just try to stay on the topic of "how".
Do I need to do it the old-fashioned route using regular old ADO.NET or does EF provide me with a way to execute a straight sql select/nonquery?
Thanks in advance.

Have you investigated Entity SQL?
Entity Framework Q&A:
string city = "London";
using (Entities entities = new Entities())
{
ObjectQuery<Customers> query = entities.CreateQuery<Customers>(
"SELECT VALUE c FROM Customers AS c WHERE c.Address.City = #city",
new ObjectParameter("city", city)
);
foreach (Customers c in query)
Console.WriteLine(c.CompanyName);
}
Since Entity SQL currently lacks any
DML constructs, it is not possible to
issue Insert, Update, or Delete
commands using Entity SQL and Object
Services

As an alternative to ObjectQuery if you need more options to cross over multiple database tables, make use of context.Database.SqlQuery<> and SqlParameter.
Example:
var searchId = new Guid("16144A52-A092-4876-9C55-A0AD0109F08A");
var sqlparam = new SqlParameter("sid", searchId);
using (var context = new Entities())
{
var sql = #"SELECT t1.Name, t2.ChildName
FROM dbo.Table1 as t1
INNER JOIN dbo.Table1 as t2
ON t1.Id = t2.ParentId
WHERE t1.Id = #sid"
var result = context.Database.SqlQuery<Combined>(sql, sqlparam);
foreach(var r in result)
{
Console.WriteLine(r.Name + r.ChildName)
}
}
public class Combined
{
public string Name { get; set; }
public string ChildName { get; set; }
}

Related

Getting Custom Column from IQueryable DB First Approach EF

I am working on Database First Approach in Entity Framework where I have to retrieve specific columns from the Entity.
Public IQueryable<Entity.Employees> GetEmployeeName(String FName,String LName)
{
var query = (from s in Employees
where s.firstName = FName && s.lastName = LName
select new {s.firstName, s.middleName});
return query;
}
Here return statement is throwing an error where it seems that its not matching with Employees (entity) columns. Could you please help me in sorting out this issue? Thanks in advance.
You need to use == for comparison, also you need to use dynamic type as return type since you are returning a custom anonymous type. Try this
Public IQueryable<dynamic> GetEmployeeName(String FName,String LName)
{
var query=(from s in Employees
where s.firstName==FName && s.lastName==LName
select new {s.firstName,s.middleName});
return query.AsQueryable();
}
Finally you will use it like below, keep in mind that intelisense won't work on dynamic object.
var query = GetEmployeeName("Jake", "Smith");
List<dynamic> results = query.ToList();
foreach (dynamic result in results)
{
string fristName = result.FirstName;
string lastName = result.MiddleName;
}

I'm copying data from my database, how do I know which tables to copy from first to avoid reference errors?

I have a database schema which has numerous tables and relationships between tables - you know, the standard SQL database setup.
I want to generate insert statements to "copy" the data from one database to another which has the same schema, but none of the data.
The problem is that if I do this in any order, it might not work since data being inserted first might depend on data which doesn't get scripted until later.
How can I order the insert statements so the data dependencies are in the correct order?
The sort you want is known as a topological sort. This orders elements (in your case, tables) so that depending elements come after dependency elements. One common technique to perform this kind of sorting is to build a graph structure and apply the sorting algorithm on it. Many frameworks have libraries which build graphs and have algorithms to perform the topological sort for you (.Net, Java, Python, C++).
One problem you'll face is if your tables have cyclical relationships. For example:
[a] --> [b] --> [a]
This cycle prevents the graph from being topologically sorted, and unless you know that none of the entities in [a] refer to entities in [b] which in turn refer to the same entities in [a] again, you can't be certain you will avoid a referential conflict.
Here is an example script in C# (using LinqPad) which queries the relationships in a database schema, uses Quickgraph to build a graph, then topologically sorts it and lists the sorted tables (from which you can build your insert statements), or lists the tables with dependency cycles if it can't be topologically sorted:
http://share.linqpad.net/47qds2.linq
void Main()
{
var targetDb = "MyDb";
var relationSql = #"SELECT pk.TABLE_NAME as PrimaryKeyTable,
fk.TABLE_NAME as ForeignKeyRefTable
FROM
INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS r
INNER JOIN
INFORMATION_SCHEMA.TABLE_CONSTRAINTS fk
ON
fk.CONSTRAINT_NAME = r.CONSTRAINT_NAME
INNER JOIN
INFORMATION_SCHEMA.TABLE_CONSTRAINTS pk
ON
pk.CONSTRAINT_NAME = r.UNIQUE_CONSTRAINT_NAME
ORDER BY
ForeignKeyRefTable";
var tableSql = #"select t.[object_id] as TableId, t.[name] as TableName, c.[name] as ColName
from sys.tables t
inner join sys.columns c
on t.object_id = c.object_id";
using(var conn = new SqlConnection(String.Format(#"Data Source=(localdb)\v11.0;Initial Catalog={0}", targetDb)))
{
var tables = conn.Query<Table>(tableSql);
var relations = conn.Query<Relation>(relationSql);
var relationGraph = new QuickGraph.AdjacencyGraph<String, Edge<String>>();
relationGraph.AddVertexRange(tables.Select(t => t.TableName));
var relationEdges = from r in relations
where r.ForeignKeyRefTable != r.PrimaryKeyTable
select new QuickGraph.Edge<String>(r.PrimaryKeyTable, r.ForeignKeyRefTable);
relationGraph.AddEdgeRange(relationEdges);
// The graph can be topologically sorted only if it is acyclic
if (relationGraph.IsDirectedAcyclicGraph())
{
var inRelationOrder = relationGraph.TopologicalSort();
inRelationOrder.Dump("Sorted Tables");
}
else
{
var connected = AlgorithmExtensions.CondensateStronglyConnected<String, Edge<String>, AdjacencyGraph<String, Edge<String>>>(relationGraph);
var cycles = from v in connected.Vertices
where v.VertexCount > 1
select v.Vertices;
cycles.Dump("Dependency Cycles");
}
}
}
public class Table
{
public Int32 TableId { get; set; }
public String TableName { get; set; }
public String ColName{ get; set; }
}
public class Relation
{
public String PrimaryKeyTable { get; set; }
public String ForeignKeyRefTable { get; set; }
}

Entity Framework 4.1 Eager Loading on complext object

In a current MVC4.0 project I am using Entity Framework 4.1 Database first model.
Part of this structure includes the following tables
compGroupData
SurveyData
SecondaryData
compGroupData and SurveyData are not joined in the database
SecondaryData is joined to SurveyData on a one to one relationship via a Foreign Key SurveyData.surveydatakey = SecondaryData.surveydatakey
In my project I have a class ComparisonWithData defined as:
public class ComparisonWithData
{
public compGroupData compgrp { get; set; }
public SurveyData surveydata { get; set; }
public ComparisonWithData()
{
compgrp = new compGroupData();
surveydata = new SurveyData();
}
}
This gives me a result set for a specific Comparison group and the data that matches this.
In the past I have retrieved the data for this via the following query:
List<ComparisonWithData> comparisonwithdata = ((from compgrp in db.compGroupDatas
where compgrp.grpYear == rptyear && compgrp.CompGroupID == ccompgrp.CompGrpID
join surveydata in db.SurveyDatas on new { compgrp.companyid, SurveyYear = (Int32)compgrp.SurveyYear } equals new { companyid = surveydata.companyid, SurveyYear = surveydata.surveyyear }
select new ComparisonWithData
{
compgrp = compgrp,
surveydata = surveydata,
}
)).ToList();
With a recent change in data I now need to also reference the SecondaryData but due to the number of records really need this to load Eagerly instead of Lazy. (Lazy loading during the loop results in thousands of DB calls)
I have looked at using the "Include" method on surveydata as well as casting the initial query as an ObjectQuery and doing the Include off that.
The first method doesn't eager load and the second method seems to always return a null object as a result.
Is there a method to Eager load the SecondaryData for SurveyData or should I be looking at a different approach all together.
My only restriction on this is that I can't go up to EF5 because of a limitation we have on .Net 4.5
Any assistance would be greatly appreciated.
Thank you.
You could try to project into an anonymous object first and use also SecondaryData in that projection, materialize this result and then project again into your final result object. Automatic Relationship Fixup that the EF context provides should populate the navigation property surveyData.SecondaryData of your ComparisonWithData object (as long as you don't disable change tracking in your query):
var data = (( // ... part up to select unchanged ...
select new // anonymous object
{
compgrp = compgrp,
surveydata = surveydata,
secondarydata = surveydata.SecondaryData
}
)).AsEnumerable();
// part until here is DB query, the rest from here is query in memory
List<ComparisonWithData> comparisonwithdata =
(from d in data
select new ComparisonWithData
{
compgrp = d.compgrp,
surveydata = d.surveydata
}
)).ToList();

Write SQL queries using LINQ from linq object List

I have a linq object and I want to write the query using linq.
please help me.
INPUT:
var tags = (from row in tempChildData.AsEnumerable()
join tagOrder in tupleInfoDataset.Tables["TagHierarchy"].AsEnumerable() on row.Field<Int64>("TAGID") equals tagOrder.Field<Int64>("TAGID")
join tagName in tupleInfoDataset.Tables["SequenceChoiceList"].AsEnumerable() on tagOrder.Field<Int64>("PARENTTAGID") equals tagName.Field<Int64>("TAGID")
join facet in tupleInfoDataset.Tables["FacetType"].AsEnumerable() on tagName.Field<string>("Tag_Name") equals facet.Field<string>("Facetname")
join tagIdInfo in schDataTogetTagid.AsEnumerable() on row.Field<string>("refTagName").Contains(":") ? row.Field<string>("refTagName").Split(':').Last():row.Field<string>("refTagName") equals tagIdInfo.Field<string>("TAGNAME")
where ( childList.Contains(row.Field<Int64>("TAGID")) && facet.Field<string>("FacetType").ToLower().Equals("ctype"))
select new
{
Tagid = row.Field<Int64>("TAGID"),
TagIdToInsert=tagIdInfo.Field<Int64>("TAGID"),
MaxOccur = row.Field<string>("Maxoccurs"),
MinOccur =Convert.ToInt32(Convert.ToString(row.Field<string>("Minoccur"))),
ParentTagId=tagOrder.Field<Int64>("PARENTTAGID"),
Order=tagOrder.Field<Int64>("TAG_ORDER"),
ParentTagname = tagName.Field<string>("Tag_Name"),
FacetId=facet.Field<Int64>("FacetID")
}).ToList();
var parentTagID = (from tagIdInfo in tupleInfoDataset.Tables["Tuple"].AsEnumerable()
where tagIdInfo.Field<Int64>("TAGID").Equals(key.Key)
select tagIdInfo.Field<Int64>("ConceptID")).ToList();
long parentID =Convert.ToInt64(parentTagID[0]);
Now i want the query out of the above code as:
INSERT INTO TUPLE_MAP (TagId,ParentTagId,ParentTagname,MinOccur,MaxOccur,Order)
VALUES (TagIdToInsert,ParentTagId,ParentTagname,MinOccur,MaxOccur,Order)
Please help me I don't know how to write SQL queries using linq
Maybe something like this:
using(var db=new DataContext("YourConnectionStringHERE"))
{
db.TUPLE_MAP.InsertAllOnSubmit(tags.Select (t =>
new TUPLE_MAP()
{
TagId=t.TagIdToInsert,
ParentTagId=t.ParentTagId,
ParentTagname=t.ParentTagname,
MinOccur=t.MinOccur,
MaxOccur=t.MaxOccur,
Order=t.Order
}));
db.SubmitChanges();
}
Or if you want to use the parentID then something like this:
using(var db=new DataContext("YourConnectionStringHERE"))
{
db.TUPLE_MAP.InsertAllOnSubmit(tags.Select (t =>
new TUPLE_MAP()
{
TagId=t.TagIdToInsert,
ParentTagId=parentID,
ParentTagname=t.ParentTagname,
MinOccur=t.MinOccur,
MaxOccur=t.MaxOccur,
Order=t.Order
}));
db.SubmitChanges();
}
where db is your linq data context
Useful references:
How to: Insert Rows Into the Database (LINQ to SQL)
EDIT
So if you are using the Compact database 3.5 then many something like this:
using (var conn =new SqlCeConnection("Data Source = test.sdf; Password ='pass'"))
{
foreach (var tag in tags)
{
using(var cmd = conn.CreateCommand())
{
cmd.CommandText = #"INSERT INTO TUPLE_MAP (TagId,ParentTagId,ParentTagname,MinOccur,MaxOccur,Order)
VALUES (#TagIdToInsert,#ParentTagId,#ParentTagname,#MinOccur,#MaxOccur,#Order)";
cmd.Parameters.AddWithValue("#TagIdToInsert", tag.TagIdToInsert);
cmd.Parameters.AddWithValue("#ParentTagId", tag.ParentTagId);
cmd.Parameters.AddWithValue("#ParentTagname", tag.ParentTagname);
cmd.Parameters.AddWithValue("#MinOccur", tag.MinOccur);
cmd.Parameters.AddWithValue("#MaxOccur", tag.MaxOccur);
cmd.Parameters.AddWithValue("#Order", tag.Order);
cmd.ExecuteNonQuery();
}
}
}
Useful references:
Why can't I insert a record into my SQL Compact 3.5 database?
SqlCeCommand.Parameters Property
SqlCeCommand Class
SqlParameterCollection.AddWithValue Method
Use linq Pad or sql profiler to see the generated SQL.
You can also use visual studio for that purpose. In the debug mode,hold cursor on the variable "tags", you will be able to see the SQL.
I am assuming you are using Linq to SQL, if you are doing so you would have entity called Tuple_map in you xxxDataContext. Then you would just have to create object of that entity something like this....
using (XXXDataContext context = new XXXDataContext())
{
Tuple_map obj = new Tuple_map();
//Populate obj properties like obj.tabid = from objects you got it from above query
context.Tuple_map.InsertOnSubmit(obj);
context.SubmitChanges();
}

How do I map lists of nested objects with Dapper

I'm currently using Entity Framework for my db access but want to have a look at Dapper. I have classes like this:
public class Course{
public string Title{get;set;}
public IList<Location> Locations {get;set;}
...
}
public class Location{
public string Name {get;set;}
...
}
So one course can be taught at several locations. Entity Framework does the mapping for me so my Course object is populated with a list of locations. How would I go about this with Dapper, is it even possible or do I have to do it in several query steps?
Alternatively, you can use one query with a lookup:
var lookup = new Dictionary<int, Course>();
conn.Query<Course, Location, Course>(#"
SELECT c.*, l.*
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id
", (c, l) => {
Course course;
if (!lookup.TryGetValue(c.Id, out course))
lookup.Add(c.Id, course = c);
if (course.Locations == null)
course.Locations = new List<Location>();
course.Locations.Add(l); /* Add locations to course */
return course;
}).AsQueryable();
var resultList = lookup.Values;
See here https://www.tritac.com/blog/dappernet-by-example/
Dapper is not a full blown ORM it does not handle magic generation of queries and such.
For your particular example the following would probably work:
Grab the courses:
var courses = cnn.Query<Course>("select * from Courses where Category = 1 Order by CreationDate");
Grab the relevant mapping:
var mappings = cnn.Query<CourseLocation>(
"select * from CourseLocations where CourseId in #Ids",
new {Ids = courses.Select(c => c.Id).Distinct()});
Grab the relevant locations
var locations = cnn.Query<Location>(
"select * from Locations where Id in #Ids",
new {Ids = mappings.Select(m => m.LocationId).Distinct()}
);
Map it all up
Leaving this to the reader, you create a few maps and iterate through your courses populating with the locations.
Caveat the in trick will work if you have less than 2100 lookups (Sql Server), if you have more you probably want to amend the query to select * from CourseLocations where CourseId in (select Id from Courses ... ) if that is the case you may as well yank all the results in one go using QueryMultiple
No need for lookup Dictionary
var coursesWithLocations =
conn.Query<Course, Location, Course>(#"
SELECT c.*, l.*
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id
", (course, location) => {
course.Locations = course.Locations ?? new List<Location>();
course.Locations.Add(location);
return course;
}).AsQueryable();
I know I'm really late to this, but there is another option. You can use QueryMultiple here. Something like this:
var results = cnn.QueryMultiple(#"
SELECT *
FROM Courses
WHERE Category = 1
ORDER BY CreationDate
;
SELECT A.*
,B.CourseId
FROM Locations A
INNER JOIN CourseLocations B
ON A.LocationId = B.LocationId
INNER JOIN Course C
ON B.CourseId = B.CourseId
AND C.Category = 1
");
var courses = results.Read<Course>();
var locations = results.Read<Location>(); //(Location will have that extra CourseId on it for the next part)
foreach (var course in courses) {
course.Locations = locations.Where(a => a.CourseId == course.CourseId).ToList();
}
Sorry to be late to the party (like always). For me, it's easier to use a Dictionary, like Jeroen K did, in terms of performance and readability. Also, to avoid header multiplication across locations, I use Distinct() to remove potential dups:
string query = #"SELECT c.*, l.*
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id";
using (SqlConnection conn = DB.getConnection())
{
conn.Open();
var courseDictionary = new Dictionary<Guid, Course>();
var list = conn.Query<Course, Location, Course>(
query,
(course, location) =>
{
if (!courseDictionary.TryGetValue(course.Id, out Course courseEntry))
{
courseEntry = course;
courseEntry.Locations = courseEntry.Locations ?? new List<Location>();
courseDictionary.Add(courseEntry.Id, courseEntry);
}
courseEntry.Locations.Add(location);
return courseEntry;
},
splitOn: "Id")
.Distinct()
.ToList();
return list;
}
Something is missing. If you do not specify each field from Locations in the SQL query, the object Location cannot be filled. Take a look:
var lookup = new Dictionary<int, Course>()
conn.Query<Course, Location, Course>(#"
SELECT c.*, l.Name, l.otherField, l.secondField
FROM Course c
INNER JOIN Location l ON c.LocationId = l.Id
", (c, l) => {
Course course;
if (!lookup.TryGetValue(c.Id, out course)) {
lookup.Add(c.Id, course = c);
}
if (course.Locations == null)
course.Locations = new List<Location>();
course.Locations.Add(a);
return course;
},
).AsQueryable();
var resultList = lookup.Values;
Using l.* in the query, I had the list of locations but without data.
Not sure if anybody needs it, but I have dynamic version of it without Model for quick & flexible coding.
var lookup = new Dictionary<int, dynamic>();
conn.Query<dynamic, dynamic, dynamic>(#"
SELECT A.*, B.*
FROM Client A
INNER JOIN Instance B ON A.ClientID = B.ClientID
", (A, B) => {
// If dict has no key, allocate new obj
// with another level of array
if (!lookup.ContainsKey(A.ClientID)) {
lookup[A.ClientID] = new {
ClientID = A.ClientID,
ClientName = A.Name,
Instances = new List<dynamic>()
};
}
// Add each instance
lookup[A.ClientID].Instances.Add(new {
InstanceName = B.Name,
BaseURL = B.BaseURL,
WebAppPath = B.WebAppPath
});
return lookup[A.ClientID];
}, splitOn: "ClientID,InstanceID").AsQueryable();
var resultList = lookup.Values;
return resultList;
There is another approach using the JSON result. Even though the accepted answer and others are well explained, I just thought about an another approach to get the result.
Create a stored procedure or a select qry to return the result in json format. then Deserialize the the result object to required class format. please go through the sample code.
using (var db = connection.OpenConnection())
{
var results = await db.QueryAsync("your_sp_name",..);
var result = results.FirstOrDefault();
string Json = result?.your_result_json_row;
if (!string.IsNullOrEmpty(Json))
{
List<Course> Courses= JsonConvert.DeserializeObject<List<Course>>(Json);
}
//map to your custom class and dto then return the result
}
This is an another thought process. Please review the same.