I want to be able to do the following:
I have a model and inside there I do have an entity.
This entity has the following structure:
public class Client
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
What I want now, is to just get the client name based on the id.
Therefore I wrote a stored procedure which is doing this.
CREATE PROCEDURE [Client].[GetBasics]
#Id INT
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT
Name
FROM Client.Client
INNER JOIN Client.Validity ON ClientId = Client.Id
WHERE
Client.Id = #Id;
END
Now, going back to VS, I do update the model from the database with the stored procedure included.
Next step is to map this stored procedure to the client entity as a function import.
This also works fine.
Trying now to load one client's name results into an error during runtime...
"The data reader is incompatible with
the specified 'CSTestModel.Client'. A
member of the type, 'Id', does not
have a corresponding column in the
data reader with the same name."
I am OK with the message. I know how to fix this (returning as result set Id, Name, Description).
My idea behind this question is the following:
I just want to load parts of the entity, not the complete entity itself.
I have a restriction here to just use stored procedures for the entire communication towards/from the database.
Is there a solution to my problem (except creating complex types, LINQ on the result set itself)?
And if yes, can someone point me to the right direction?
Many thanks,
Dimi
Just project onto a POCO:
var q = from c in Context.Clients
select new NameOnlyPresentation
{
Id = c.Id,
Name = c.Name
};
... or just the name:
public string ClientName(int id)
{
return (from c in Context.Clients
where c.Id == id
select c.Name).FirstOrDefault();
}
Related
Here is a truncated example of what I'm trying to do:
var stuffTOSave = new List<SomeObject> {
public int OtherTableId { get; set; }
public List<Guid> ComponentIds { get; set; }
};
var sql = #"CREATE TABLE Components( ComponentId uniqueidentifier PRIMARY KEY )
INSERT INTO Components VALUES (#WhatGoesHere?)
SELECT * FROM OtherTable ot
JOIN Components c on c.ComponentId = ot.ComponentId
WHERE Id = #OtherTableId
DROP TABLE Components"
Connection.Execute(sql, stuffToSave);
I know from other SO questions that you can pass a list into an insert statement with Dapper, but I can't find any examples that pass a list as well as another parameter (in my example, OtherTableId), or that have a non-object list (List<Guid> as opposed to a List<SomeObject> that has properties with names to reference).
For the second issue, I could select the ComponentIds into a list to give them a name like:
stuffToSave.ComponentIds.Select(c => new { ComponentId = c })
but then I'm not sure what to put in my sql query so that dapper understands to get the ComponentId property from my list of ComponentIds (Line 7)
I would still like to know the real way of accomplishing this, but I have this workaround that uses string interpolation:
var sql = $#"CREATE TABLE Components( ComponentId uniqueidentifier PRIMARY KEY )
INSERT INTO Components VALUES ('{string.Join($"'),{Environment.NewLine}('", request.ComponentIds)}')
SELECT * FROM OtherTable ot
JOIN Components c on c.ComponentId = ot.ComponentId
WHERE Id = #OtherTableId
DROP TABLE Components"
I'm not worried about SQL Injection since this is just interpolating a list of Guids, but I'd rather avoid this method if possible.
I met situation I have to write app where I'm taking bunch of records from tableA then for each of record I have to do lookup against tableB to pull extra information (get another 3 columns).
TableA is a small table (<1000 records), but tableB is much bigger. Also, these resides in separate DB on the same DB server.
What would be best approach to get it optimized?
There is no option to get all records into list of objects from tableB then operate on it, rather I would need to run LINQ query for each of tableA element(object) against tableB. This is part of my MVC so could you please provide me an draft of solution, described at high level, rather than providing code.
EDIT
The tableA records need to be "enriched" all against tableB before they are displayed, in effecitve this may be +/- 500 tableA records to be lookup against tableB. Also, limitation is I have only read access to the tableB..no option to write procedures, etc
You could create a view in one of the databases that combines data in table A and B. Then map your entity to that view. Check this out https://www.mssqltips.com/sqlservertip/1990/how-to-use-sql-server-views-with-the-entity-framework/
Without seeing your models and query, it is hard to provide an accurate answer. However, the best start is within the database (I would assume SQL Server 2012+).
From your description, the generated query should look, in a very simplified way, like the following:
SELECT A.*, B.Col1, B.Col2, B.Col3
FROM Db1.dbo.TableA AS A
JOIN Db2.dbo.TableB AS B ON B.Id = A.FkToBId
According to this question and its accepted answer, there is no big difference between selecting from the same database vs. selecting from another database within the same instance.
If TableB is big, you should avoid table scans, so the following index should be a good start:
CREATE INDEX IDX_TableB_Id ON TableB (Id) INCLUDE (Col1, Col2, Col3)
However, if the schema is properly normalized, the lookup key should also be a primary key and this index should not be required. I think that if it is clustered, it might bring extra benefit.
Of course, there is a chance that your LINQ generates a slightly different query. If this is the case, edit your question and include table schema, LINQ and generated query.
[EDIT]
Using SqlQuery is an option, but I am thinking about another way:
1) Generate a database context for each database. Lets call them DbContextA and DbContextB
2) Get only required information from TableB, store it in a dictionary for fast lookups and use in an aggregated query.
var ids = DbContextA.TableA.AsNoTracking().Select(item => item.FkToBId).ToList();
var bInfo = DbContextB.TableB.AsNoTracking()
.Where(item => ids.Contains(item.id))
.ToDictionary(
item => item.Id,
item => new { item.Col1, item.Col2, item.Col3 }
);
var aggrInfo = DbContextA.TableA.AsNoTracking()
.ToList()
.Select(item => new
{
AField = item,
Col1 = bInfo[item.FkToBId],
Col2 = bInfo[item.FkToBId],
Col3 = bInfo[item.FkToBId]
};
If this does not provide the required efficiently, SqlQuery remains the only option, as a database context cannot work with two databases at once (source).
You should create a one class means .cs file and add all the columns of TableA and TableB which is required.
Let see and Example Here i am having two tables category and sub category i want the name of the category and want to show the list.
public class SubCategory
{
public int Id { get; set; }
public string Name { get; set; }
public string Image { get; set; }
public Nullable<bool> Isdisplay_ { get; set; }
public Nullable<int> CatId { get; set; }
public string CatName { get; set; }
}
var data = (from t in db.SubCategories
join ts in db.Categories on t.CatId equals ts.CategoryId
select new { a1 = t.SubId, a2 = t.SubImage, a3 = t.SubIsdisplay_, a4 =
t.SubName, a5 = ts.CategoryName }).ToList();
List<SubCategory> p1 = new List<SubCategory();
foreach (var i in data)
{
SubCategory p2 = new SubCategory();
p2.Id = i.a1;
p2.Name = i.a4;
p2.Image = i.a2;
p2.Isdisplay_ = i.a3;
p2.CatName = i.a5;
p1.Add(p2);
}
return p1;
Now you can use the list p1 to show your data.
I'm using Entity Framework v6. I have a stored procedure as shown below
CREATE PROCEDURE [dbo].[GetCountryList]
(
#CustomerName VARCHAR(MAX),
#SearchCriteria VARCHAR(MAX)
)
AS
BEGIN
SET NOCOUNT ON
SELECT CountryID, CountryName FROM dbo.Table1
WHERE CustomerName = #CustomerName AND CountryName = #SearchCriteria
END
Now I have a model class
public class CountryName
{
public int CountryId { get; set; }
public string CountryName { get; set; }
}
So I want to get the result of the SELECT query in a List<CountryName> type
List<CountryName> countryList = null;
using (DbEntities dbContext = new DbEntities())
{
countryList = //my code to collect the result
}
Well, I could have run a LINQ to SQL directly on the table but unfortunately my requirement in to get the data from stored procedure. So, how can I do it?
You need to Import the stored procedure as a Function. Right-click on the workspace area of your Entity model and choose Add -> Function Import.
In the Add Function Import dialog, enter the name you want your stored procedure to be referred to in your model for example GetCountryListSP, choose your procedure from the drop down list, and choose the return value of the procedure to be Entities and choose CountryName from the drop down list.
Then in the code:
var result = db.GetCountryListSP();//Send parameters too
With this approach you prevent returning -1 of the stored procedure. Please check this post for more details about stored procedure problem with Entity Framework.
You can do it without importing. Something like that:
var countryList = dbContext.Database.SqlQuery<CountryName>("[GetCountryList]").ToList();
EntityFramework sometimes won't recognize or import SPs ))) So, that's why I saving my hours with this snippet.
Sorry for the bad title, I havent come up with a better one yet.
I am currently optimising a tool which basically does thousands of selects and inserts.
Assume the following relation
class A
{
public long ID; // This is an automatic key by the sqlserver
...Some other values
}
class B
{
public long RefID // Reference to A.ID;
... some other values...
}
class C
{
public long RefID // Reference to A.ID;
... some other values
}
What currently is happening is a SELECT to get ObjectA,
if it doesnt exist, create a new one. The Query returns the ID (OUTPUT INSERTED.ID)
Then it selects (inserts if not existant) the objects B and C.
Is there a way to compress this into a single SQL statement?
Im struggling at the part where the automatic generation of object A happens.
So it must do something like this:
IF NOT EXISTS(SELECT * FROM TableA WHERE someConditions)
INSERT... and get the ID
ELSE
REMEMBER THE ID?
IF NOT EXISTS(SELECT * FROM TableB WERE RefID = ourRememberedID)
INSERT...
IF NOT EXISTS(SELECT * FROM TableC WERE RefID = ourRememberedID)
INSERT...
Please note, stored procedures cannot be used.
A little help
You could issue these three in one statement
Use a DataReader NextResult
select ID from FROM TableA WHERE someConditions
select count(*) from TableB where refID = (select ID from FROM TableA WHERE someConditions)
select count(*) from TableC where refID = (select ID from FROM TableA WHERE someConditions)
But even then you take a risk the TableB or TableC had an insert before you got to it
If I could not use a stored procedure I think I would load the data into a #temp using a TVP
I think you could craft 4 statements in a transaction
This is regarding Dapper in ASP.NET MVC3.
I have two tables tblBranchMaster, tblZoneMaster in my database and two class file with same details.
tblBranchMaster(ID, ZoneID, Name);
tblZoneMaster(ID, Name);
Both table having primary key foreign key relationship.
I have a stored procedure which has following query :
Select * from tblBranchMaster;
with some other logical stuff.
Now how should i get result of zone with its related Branch in list.
I have following code base:
List<tblBranchMaster> lstResult = Query<tblBranchMaster, tblZoneMaster, tblBranchMaster>
(tblBranchMaster.uspGetBranchListPagination, (objBranch, objZone) =>
{ objBranch.ZoneMaster = objZone; return objBranch; },
param, splitOn:"Id").ToList();
This code gives me following error:
When using the multi-mapping APIs ensure you set the splitOn param if
you have keys other than Id
Parameter name: splitOn
What am I missing???
The query result needs to have the same format and order as the objects that you want to populate. In this case dapper expects you to have "Id, Name, Id, Name", where the first two would be the Id and Name for branch master and the 3rd and 4th columns would be the Id and Name for zone master.
Given the following classes:
class Branch
{
int Id { get; set }
string Name { get; set }
}
class Zone
{
int Id { get; set }
string Name { get; set }
}
And the following SQL query format in your stored procedure:
SELECT b.Id, b.Name, z.Id, z.Name
FROM Branch b LEFT JOIN Zone z ON b.ZoneId = z.Id
You should be able to use this dapper code:
var result = connection.Query<Branch, Zone, Branch> ("myStoredProcName",
(objBranch, objZone) => { objBranch.ZoneMaster = objZone; return objBranch; },
param, commandType: CommandType.StoredProcedure).ToList();