Dapper with postgresql error (sequence contains more than one element) - c#

I am getting this error when I run an integration test against my Postgresql DB using a stored procedure.
Result Message: System.InvalidOperationException : Sequence contains more than one element
Here is the repo file:
public card_view SelectView(int card_id)
{
using (var connection = new NpgsqlConnection(ConfigurationSettings.GetConnectionString()))
{
var p = new DynamicParameters();
p.Add("#card_id", card_id);
using (var multi = connection.QueryMultiple("f_card_select_view", p, commandType: CommandType.StoredProcedure))
{
card_view view = multi.Read<card_view>().Single();
view.Categories = multi.Read<category>().ToList();
view.Modifiers = multi.Read<card_modifier_view>().ToList();
return view;
}
}
}
The card test file:
[Test]
public void SelectViewTest()
{
var repo = new CardRepository();
var result = repo.SelectView(31); // witch
Assert.AreEqual(2, result.Categories.Count);
Assert.AreEqual(2, result.Modifiers.Count);
}
Cardview file:
public class card_view
{
public int card_id { get; set; }
public int cardset_id { get; set; }
public string cardset_title { get; set; }
public string image_path { get; set; }
public string cardset_name { get; set; }
public int card_cost { get; set; }
public List<card_modifier_view> Modifiers { get; set; }
public List<category> Categories { get; set; }
}
cardmodifierview file:
public class card_modifier_view
{
public int card_modifier_id { get; set; }
public int card_id { get; set; }
public int modifier_type_id { get; set; }
public int? modifier_value { get; set; }
public string instruction_text { get; set; }
public string modifier_type_name { get; set; }
}
The DB function (The original SQL was T-SQL and I have done my best to translate it into regular SQL for postgres.)
CREATE FUNCTION f_card_select_view (card_id int)
RETURNS TABLE(card_id bigint, modifier_type_id integer,
instruction_text integer, modifier_type_name integer, card_modifier character varying, modifier_type character varying)
AS $$
SELECT card_id,cardset.cardset_id,card_title,image_path,card_cost,cardset_name
FROM card
INNER JOIN cardset ON card.cardset_id = cardset.cardset_id
WHERE card_id = #card_id;
SELECT card.category_id,category_name
FROM category card
INNER JOIN card_category ON card.category_id = card_category.category_id
WHERE card_category.card_id = #card_id;
SELECT f_card_modifier_selectby_card_id (#card_id);
$$ LANGUAGE sql;
Here is f_card_modifier_selectby_card_id:
CREATE FUNCTION f_card_modifier_selectby_card_id(card_id int)
RETURNS TABLE(
card_id bigint,
modifier_type_id int,
instruction_text int,
modifier_type_name int,
card_modifier varchar,
modifier_type varchar
)
AS $$
SELECT
card_modifier_id,
card_id,
card_modifier.modifier_type_id,
modifier_value,
instruction_text,
modifier_type_name
FROM card_modifier INNER JOIN modifier_type ON card_modifier.modifier_type_id = modifier_type.modifier_type_id
WHERE card_id = card_id
$$ LANGUAGE sql;

If the error is Sequence contains more than one element, then the problem is that your first query (where you have the .Single()) is returning more than one row. I can't tell you why that is, but you need to try running:
SELECT card_id,cardset.cardset_id,card_title,image_path,card_cost,cardset_name
FROM card
INNER JOIN cardset ON card.cardset_id = cardset.cardset_id
WHERE card_id = #card_id;
(with your expected #card_id) in your SQL toolkit to see what happens. Or better: call the stored procedure itself - presumably via:
EXEC f_card_select_view {your id here}

It looks like your statement brings back a join containing multiple records for card_view or 0.
In other frameworks certainly you can use SingleOrDefault() to allow for 0 records. Note this will still error for multiple records.
If its getting multiple records you need to establish if this is correct and rework it to be a collection like you have done with .ToList() or correct your data in the database and possibly your keys.

Related

C# EF Core Procedure use with Class

I tried to use EF Core to execute a procedure, so I define a class as ACT, then use ACT class to catch procedure output, but I got this error:
Return : The required column 'A02' was not present in the results of a 'FromSql' operation.
I already know class fields and procedure have match field, but I have many procedures to use, I do I have to create a new class for each procedure ?
ACT.cs
namespace E-shop.Shared
{
public class ACT
{
[Key]
public string A01 { get; set; } = string.Empty;
public string S01 { get; set; } = string.Empty;
public string A02 { get; set; } = string.Empty;
public string A03 { get; set; } = string.Empty;
public string A04 { get; set; } = string.Empty;
public string A05 { get; set; } = string.Empty;
public int A06 { get; set; }
public int A07 { get; set; }
public int A08 { get; set; }
public DateTime A09 { get; set; }
public int A10 { get; set; }
public int? A11 { get; set; }
public int? A12 { get; set; }
public int? A13 { get; set; }
}
}
Procedure
ALTER PROCEDURE [dbo].[usp_Sel_SAcc1]
(#A01 Varchar(50))
AS
SELECT A01
FROM ACT
WHERE A01 = #A01
The method
public void CheckAccount (string Email)
{
string ProcdureName = "usp_Sel_SAcc1";
var parameter = new SqlParameter("#A01", Email);
var user = Model1_context.ACT
.FromSqlInterpolated($"EXECUTE {ProcdureName} {parameter}")
.ToList();
Console.WriteLine(user.ToString());
}
=======Update 2023/02/13 =========
Thanks every one reply!
I think I need to describe the problem in more detail!!
I got many procedure in our-project Sql, like picture at under.
Every procedure select data is not the same, But it most of use ACT Class, ACT Class is the class corresponding to the database table.
And that procedure most of them also use this table, So I wanna find a way to just use one class to catch many procedure return.
procedures example:
SACC3
ALTER Procedure [dbo].[usp_Sel_SAcc3]
(
#A01 Varchar(50)
)
AS
SELECT ACT.A04,ACT.A05,ACT.A07,ACT.A09,ACT.A03,Store.S02,Store.S09,Store.S10,Store.S13,ACT.A06,ACT.S01 FROM ACT INNER JOIN Store ON ACT.S01 =Store.S01 WHERE ACT.A01 = #A01
SAcc7
ALTER Procedure [dbo].[usp_Sel_SAcc7]
(
#S01 Char(8),
#A01 Varchar(50)
)
AS
SELECT A03,A04,A05 FROM ACT WHERE S01 = #S01 and A01=#A01
Why would you have a stored procedure that returns one column from a table, selecting by that same column? If it is more a case of selecting a record using the AO1 value then the Stored Proc would be more like:
CREATE PROCEDURE [dbo].[usp_SelectBySAcc1]
(#A01 Varchar(50))
AS
SELECT A01, A02, A03, S01, ... /* fill in all applicable fields */
FROM ACT
WHERE A01 = #A01
Then EF can populate your desired entity from the result set coming back from the stored proc. With .Net Core if any of those string columns are null-able, you will want to declare the fields as string? to avoid compiler warnings.
Your code will also likely not work as you expect. ToList() intends to load a collection of matching entities. If you are loading an entity by Key and expect just one row (or zero rows if not found) then use Single or SingleOrDefault.
var user = Model1_context.ACT
.FromSqlInterpolated($"EXECUTE {ProcdureName} {parameter}")
.Single();
Ultimately the point of EF is to not need to write SQL or stored procedures. Getting an ACT record by A01 is as simple as:
var user = Model1_context.ACT
.Single(x => x.A01 == Email);
EF will write the SQL needed to fetch the record. If you just want to check if a record exists:
var userExists = Model1_context.ACT
.Any(x => x.A01 == Email);

SQL store procedure executes in SQL but not in code

I need your help. I have a store procedure that executes perfectly in SQL but when I call her from an IActionResult from a .net controller with FromSqlRaw(), returns the following error :
System.InvalidOperationException: 'The required column 'SeasonGroupClientCode' was not present in the results of a 'FromSql' operation.'
in Action:
var sql = "EXECUTE sp_seasonDataGroupedHorizonatlallyByCompany";
var list = _context.SeasonGroupedByCompanies.FromSqlRaw(sql).AsNoTracking().ToList();
Store Procedure:
select * from
(SELECT [SeasonGroupSeasonDescription] AS FirstSeason,[SeasonGroupClientCode] AS
FirstSeasonClientCode,[SeasonGroupClientName] AS
FistSeasonClientName,SeasonGroupCompanyId],
SUM([SeasonGroupStartValue]) AS FiSTotalStartValue ,
SUM([SeasonGroupFinalValue]) AS FiSTotalFinalValue
FROM [dbo].[SeasonGroupedByCompanies]
WHERE [SeasonGroupSeasonDescription]='SS21'
GROUP BY [SeasonGroupClientCode],[SeasonGroupClientName],
[SeasonGroupSeasonDescription],[SeasonGroupCompanyId]) as td
full join
(SELECT [SeasonGroupSeasonDescription] AS SecondSeason,[SeasonGroupClientCode] AS
SecondSeasonClientCode,[SeasonGroupClientName] AS SecondSeasonClientName,
[SeasonGroupCompanyId] AS SecondId,
SUM([SeasonGroupStartValue]) AS SeSTotalStartValue ,
SUM([SeasonGroupFinalValue]) AS SeSTotalFinalValue
FROM [dbo].[SeasonGroupedByCompanies]
WHERE [SeasonGroupSeasonDescription]='FW21'
GROUP BY [SeasonGroupClientCode],[SeasonGroupClientName],[SeasonGroupSeasonDescription],
[SeasonGroupCompanyId]) as tf
on td.FistSeasonClientName = tf.SecondSeasonClientName
full join
(SELECT [SeasonGroupSeasonDescription] AS ThirdSeason,[SeasonGroupClientCode] AS
ThirdSeasonClientCode,[SeasonGroupClientName] AS ThirdSeasonClientName,
[SeasonGroupCompanyId] AS ThirdId,
SUM([SeasonGroupStartValue]) AS ThSTotalStartValue ,
SUM([SeasonGroupFinalValue]) AS ThSTotalFinalValue
FROM [dbo].[SeasonGroupedByCompanies]
WHERE [SeasonGroupSeasonDescription]='SS22'
GROUP BY [SeasonGroupClientCode],[SeasonGroupClientName],[SeasonGroupSeasonDescription],
[SeasonGroupCompanyId]) as ts
on td.FistSeasonClientName = ts.ThirdSeasonClientName
full join
(SELECT [SeasonGroupSeasonDescription] AS FourthSeason,[SeasonGroupClientCode]AS
FourthSeasonClientCode,[SeasonGroupClientName] AS FourthSeasonClientName,
[SeasonGroupCompanyId] AS FourthId,
SUM([SeasonGroupStartValue]) AS FoSTotalStartValue ,
SUM([SeasonGroupFinalValue]) AS FoSTtotalFinalValue
FROM [dbo].[SeasonGroupedByCompanies]
WHERE [SeasonGroupSeasonDescription]='FW22'
GROUP BY [SeasonGroupClientCode],[SeasonGroupClientName],[SeasonGroupSeasonDescription],
[SeasonGroupCompanyId]) as tx
on td.FistSeasonClientName = tx.FourthSeasonClientName
Entity
public class SeasonGroupByCompany
{
[Key]
public int SeasonGroupCompanyId { get; set; }
public string? SeasonGroupSeasonDescription { get; set; }
public string? SeasonGroupSalesman { get; set; }
public int SeasonGroupClientCode { get; set; }
public string? SeasonGroupClientName { get; set; }
public int SeasonGroupQuantity { get; set; }
public int SeasonGroupDiscount { get; set; }
public double SeasonGroupStartValue { get; set; }
public double SeasonGroupFinalValue { get; set; }
The same error comes up, even if I run the query directly from FromRawSql().
Thanks in advance, any help will be appreciated
It looks like in the SeasonGroupedByCompany entity you have a field named SeasonGroupClientCode, but in the SQL query you have renamed it into FirstSeasonClientCode:
...[SeasonGroupClientCode] AS FirstSeasonClientCode...
Either rename the field in the entity, or don't rename the column in SQL, because originally the naming looks to be consistent.

Debugging Call Stack Exceeded C# Backend

I have a get route a that is going to get data of wells and well tests, when I execute the call on swagger, it will take awhile and then give me a call stack error. My problem is I cant figure out how to get a log or idea of where this is happening. The best I have been able to do so far is use point breaks at every step to see how far it gets. I've gotten to the controller route so I know that its grabbing the data just fine, my understanding is that it now has the data, and should use the view model to match and display the data. I have gone through about 100 data samples in the view model and it seems fine but there is 2400 units, all with 5 arrays inside of them. However it will simply error out with no message. Any ideas of whats going on or how to debug this? Is there a way in VS Code so see a better log of something like this or another tool that will do that will help in this situation?
** Service Code: **
public async Task<IEnumerable<SapDispatchViewModel>> GetDispatchDeliveryForSap()
{
var result = await _dispatchRepo.GetDispatchDeliveryForSap(TenantId);
var view = new List<SapDispatchViewModel>();
foreach (SapDispatch row in result)
{
var sapView = _mapper.Map<SapDispatch, SapDispatchViewModel>(row);
var items = await _dispatchItemRepo.GetDispatchItemsByTruckForSap(row.DispatchTruckId);
var viewItems = _mapper.Map<IEnumerable<SapDispatchItem>, IEnumerable<SapDispatchItemViewModel>>(items);
sapView.Items = viewItems;
view.Add(sapView);
}
return view;
}
** It calls this GetDispatchDeliveryForSap first: **
public async Task<IEnumerable<SapDispatch>> GetDispatchDeliveryForSap(string TenantId)
{
string deliveryType = "Delivery";
//resort to raw SQL to assist with performance improvements
FormattableString sql = $#"
WITH cte_latestStatus AS
( SELECT * FROM (
SELECT
s.TenantId,
s.DispatchId,
s.DispatchHeaderId,
s.RequestedArrival,
s.EstimatedArrival,
s.Status,
u.FirstName + ' ' + u.LastName UserName,
s.CreateDate StatusChangeDate,
row_number() over(partition by DispatchHeaderId order by CreateDate desc) as rn
FROM
DispatchStatus s
JOIN AspNetUsers u on s.CreateUserId = u.Id
) t
WHERE t.rn = 1
)
select w.wellid,
w.wellname,
wo.ErpId,
wc.ContractorName + ' ' + w.RigNumber Rig,
w.CountyParish County,
w.State,
d.type DispatchType,
u.LastName + ',' + u.FirstName OrderedBy,
ds.RequestedArrival RequestedDate,
dt.DriverName,
dt.SwamperName,
dt.TicketNumber,
dt.DispatchTruckId
from well w
join Dispatch d on w.wellid = d.DestinationWellId
join cte_latestStatus ds on d.DispatchId = ds.DispatchId and d.HeaderId = ds.DispatchHeaderId
join DispatchTruck dt on d.DispatchId = dt.DispatchId
join AspNetUsers u on d.CreateUserId = u.Id
left join WellContractorRef wcr on w.WellId = wcr.WellId
left join Contractor wc on wcr.ContractorId = wc.ContractorId
left join WellOperatorRef wor on w.WellId = wor.WellId
left join Operator wo on wor.OperatorId = wo.OperatorId
--join DispatchItem di on dt.DispatchTruckId = di.DispatchTruckId
where d.TenantId = {TenantId}
and d.type = {deliveryType}
and (ds.Status = 'Completed' or dt.status = 'Completed')
order by w.wellname"
;
var result = await context.SapDispatches.FromSqlInterpolated(sql).AsNoTracking().ToListAsync();
return result;
}
}
}
** Then maps via the view model to create the list: **
namespace Mudman.Model.ViewModels
{
public class SapDispatchViewModel
{
public string WellId { get; set; }
public string WellName { get; set; }
public string ErpId { get; set; }
public string Rig { get; set; }
public string County { get; set; }
public string State { get; set; }
public string DispatchType { get; set; }
public string OrderedBy { get; set; }
public DateTime? RequestedDate { get; set; }
public string DriverName { get; set; }
public string SwamperName { get; set; }
public long? TicketNumber { get; set; }
public IEnumerable<SapDispatchItemViewModel> Items { get; set; }
}
public class SapDispatchItemViewModel
{
public string ErpId { get; set; }
public Decimal? Price { get; set; }
public Decimal? Quantity { get; set; }
public string Size { get; set; }
public string Unit { get; set; }
}
}
** From there, it runs the foreach on the GetDispatchItemsForTruckSap: **
public async Task<IEnumerable<SapDispatchItem>> GetDispatchItemsByTruckForSap(string dispatchTruckId)
{
//resort to raw SQL to assist with performance improvements
FormattableString sql = $#"
WITH cte as (
SELECT
COALESCE(ProductId, ExpenseId) AS SalesItemID,
Price,
Quantity
FROM DispatchItem
WHERE DispatchTruckId = {dispatchTruckId}
)
SELECT si.ErpId,
cte.Price,
cte.Quantity,
si.Size,
si.Unit
FROM SalesItem si
INNER JOIN cte on cte.SalesItemID = si.SalesItemId"
;
var result = await context.SapDispatchItems.FromSqlInterpolated(sql).AsNoTracking().ToListAsync();
return result;
}
}
}
** Maps with the Item View Model: **
public class SapDispatchItemViewModel
{
public string ErpId { get; set; }
public Decimal? Price { get; set; }
public Decimal? Quantity { get; set; }
public string Size { get; set; }
public string Unit { get; set; }
}
}
** Then it will hit the return and thats where it will error out.
Also, here is what the callstack is looking like when you hit that return.
Try turning on Break When Thrown on Common Language Runtime Exceptions and it should break at the error:

Foreach statement cannot operate on variables of type '?' because '?' does not contain a public definition for 'GetEnumerator'

I did some updates to a new copy of a database that I am using and generated the edmx file from the database, I fixed a couple stored procs that were giving me issues, however in this one method I keep getting this error
Error CS1579 foreach statement cannot operate on variables of type '?' because '?' does not contain a public definition for 'GetEnumerator'
and I think its tied in with this error
Error CS1936 Could not find an implementation of the query pattern for source type 'int'. 'Select' not found
I have looked around for a solution to this and haven't really found anything solid.
The method in my DAL that is throwing the error is
public List<NewCustomer> GetCustomerToEditByID(int id)
{
ExoEntities = new ExoEntities();
List<NewCustomer> lst = new List<NewCustomer>();
var query = from a in ExoEntities.usp_GetCustomerByID(id)
select a;
foreach (var b in query)
{
lst.Add(new NewCustomer
{
CustomerID = b.CustomerID,
FirstName = b.FirstName,
LastName = b.LastName,
YearBuilt = b.YearBuilt,
Line1 = b.Line1,
Line2 = b.Line2,
City = b.City,
ZipCode = b.ZipCode,
StateID = (int)b.StateID,
StateName = b.StateName,
County = b.County,
SubDivisionID = (int)b.SubDivisionID,
ContactName = b.ContactName,
Phone = b.Phone,
OtherPhone = b.OtherPhone,
Cell = b.Cell,
Fax = b.Fax,
Email = b.Email
});
}
return lst;
}
the data class is
public class NewCustomer
{
public int CustomerID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string DateCreated { get; set; }
public int CreatedBy { get; set; }
public string YearBuilt { get; set; }
public byte IsActive { get; set; }
public int CustomerTypeID { get; set; }
// Address
public string Line1 { get; set; }
public string Line2 { get; set; }
public string Line3 { get; set; }
public string City { get; set; }
public string ZipCode { get; set; }
public int StateID { get; set; }
public string StateName { get; set; }
public string County { get; set; }
public int SubDivisionID { get; set; }
// Contact
public string ContactName { get; set; }
public string Phone { get; set; }
public string OtherPhone { get; set; }
public string Cell { get; set; }
public string Pager { get; set; }
public string Fax { get; set; }
public string Email { get; set; }
public byte ContactIsActive { get; set; }
}
This is the EF context class for the stored procedure
public virtual int usp_GetCustomerByID(Nullable<int> customerID)
{
var customerIDParameter = customerID.HasValue ?
new ObjectParameter("CustomerID", customerID) :
new ObjectParameter("CustomerID", typeof(int));
return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction("usp_GetCustomerByID", customerIDParameter);
}
and my stored procedure is
Create procedure [dbo].[usp_GetCustomerByID]
(
#CustomerID int
)
AS
SET NOCOUNT OFF
SET TRANSACTION ISOLATION LEVEL READ COMMITTED
DECLARE #ERROR_SEVERITY int,
#MESSAGE varchar(1000),
#ERROR_NUMBER int,
#ERROR_PROCEDURE nvarchar(200),
#ERROR_LINE int,
#ERROR_MESSAGE nvarchar(4000);
begin try
select
caxref.CustomerID,
caxref.AddressID,
customer.FirstName,
customer.LastName,
customer.YearBuilt,
address.Line1,
address.Line2,
address.City,
address.ZipCode,
address.StateID,
address.County,
state.StateName,
address.SubDivisionID,
contact.ContactName,
contact.Phone,
contact.OtherPhone,
contact.Cell,
contact.Fax,
contact.Email
from [CustomerAddressXREF] caxref
left join [Customer] customer on customer.CustomerID = caxref.CustomerID
left join [Address] address on address.AddressID = caxref.AddressID
left join [SubDivision] subdivision on subdivision.SubDivisionID = address.SubDivisionID
left join [CustomerContactXREF] ccxref on ccxref.CustomerID = customer.CustomerID
left join [Contact] contact on contact.ContactID = ccxref.ContactID
inner join [State] state on state.StateID = address.StateID
where customer.CustomerID = #CustomerID
end try
BEGIN CATCH
SET #ERROR_SEVERITY = ISNULL(ERROR_SEVERITY(),'');
SET #ERROR_NUMBER = ISNULL(ERROR_NUMBER(),'');
SET #ERROR_PROCEDURE = ISNULL(ERROR_PROCEDURE(),'');
SET #ERROR_LINE = ISNULL(ERROR_LINE(),'');
SET #ERROR_MESSAGE = ISNULL(ERROR_MESSAGE(),'');
-- Test if the transaction is uncommittable.
IF (XACT_STATE()) = -1
BEGIN
--PRINT N'The transaction is in an uncommittable state. Rolling back transaction.'
ROLLBACK TRANSACTION;
END;
-- Test if the transaction is active and valid.
IF (XACT_STATE()) = 1
BEGIN
--PRINT N'The transaction is committable. Committing transaction.'
COMMIT TRANSACTION;
END;
SET #MESSAGE = 'Error Occured in Stored Procedure ' + cast(#ERROR_PROCEDURE as varchar(200)) +
'; Line Number ' + cast(#ERROR_LINE as varchar) +
'; Message: [' + cast(#ERROR_NUMBER as varchar) + '] - '
+ cast(#ERROR_MESSAGE as varchar(255))
RAISERROR(#MESSAGE, #ERROR_SEVERITY, 1);
END CATCH;
I have tested the stored procedure in SSMS and it returns the all the data that I need
So I am not sure where the problem is lying.
I have deleted my EDMX file a couple times and regenerated it from the database, but that isn't working, I still get those 2 errors. Its probably blantantly obvious to a solution, but its not popping out at me
Your usp_GetCustomerByID C# function returns a single int value, not a collection of objects, so you can't use it as the source of a query. from a in ExoEntities.usp_GetCustomerByID(id) is not valid.
The return type of usp_GetCustomerByID should be an IEnumerable<sometype> if you want to query it. You might me able to use IEnumerable<NewCustomer> as the return type but it's not clear if that is your entity type. Then you wouldn't have to iterate it and create new NewCustomer objects. You could just call return usp_GetCustomerByID(id).ToList()
Also, the query from a in <source> select a is essentially returning an iterator over <source>, so you could just use:
foreach (var b in <source>)

How to map many to many relation to a list with dapper

I have a class AnalysisRule
public class AnalysisRule
{
public long Id { get; set; }
public Analysis Analysis { get; set; }
public AnalysisCategory AnalysisCategory { get; set; }
public Gender Gender { get; set; }
public bool FatherHerdBookRequired { get; set; }
public bool MotherHerdBookRequired { get; set; }
public List<Breed> AllowedBreeds { get; set; }
}
That has a list of Breeds
public class Breed
{
public long BreedId { get; set; }
public long AnimalTypeId { get; set; }
public long BreedCode { get; set; }
public string BreedName { get; set; }
public string BreedAcronym { get; set; }
}
This is a many to many relationship that I bind together with a DB table
AnalysisRulesBreeds
Breeds
And AnalysisRules
With Dapper I have tried
var sql = #"select *
from ""AnalysisRules""
join ""AnalysisCategory"" on ""AnalysisRules"".""AnalysisCategoryId"" = ""AnalysisCategory"".""Id""
join ""Analysis"" on ""AnalysisRules"".""AnalysisId"" = ""Analysis"".""Id""
left join ""AnalysisRulesBreeds"" on ""AnalysisRulesBreeds"".""AnalysisRuleId"" = ""AnalysisRules"".""Id""
left join ""Breed"" on ""AnalysisRulesBreeds"".""BreedId"" = ""Breed"".""BreedId""
where ""AnalysisId"" = :AnalysisId";
rules = sqlConnection.QueryAsync<AnalysisRule, AnalysisCategory, Analysis, Breed, AnalysisRule>(
sql,
(ar, c, a, b) =>
{
ar.AnalysisCategory = c;
ar.Analysis = a;
ar.Breeds.Add(b);
return ar;
},
new
{
AnalysisId = analysisId
},
splitOn:"BreedId");
Which gives me
´When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id
Parameter name: splitOn
If I run the same query in SQL Developer I get 2 rows out with same Id but with different data in Breed, so the query should be good enough.
So how do I get these 2 rows into one AnalysisRule entity where Breeds consist of 2 Breed entities?
EDIT
I now have
sqlConnection.Open();
var sql = #"select ar.*,
ac.*,
b.*
from ""AnalysisRules"" ar
join ""AnalysisCategory"" ac on ar.""AnalysisCategoryId"" = ac.""Id""
join ""Analysis"" a on ar.""AnalysisId"" = a.""Id""
left join ""AnalysisRulesBreeds"" on ""AnalysisRulesBreeds"".""AnalysisRuleId"" = ar.""Id""
left join ""Breed"" b on ""AnalysisRulesBreeds"".""BreedId"" = b.""Id""
where ""AnalysisId"" = :AnalysisId";
var rules = sqlConnection.QueryAsync<AnalysisRule, AnalysisCategory, Analysis, Breed, AnalysisRule>(
sql,
(ar, c, a, b) =>
{
ar.AnalysisCategory = c;
ar.Analysis = a;
ar.Breeds.Add(b);
return ar;
},
new
{
AnalysisId = analysisId
});
return await rules;
Removed the splitOn, changed AnalysisRulesBreedsId to Id but I still get
When using the multi-mapping APIs ensure you set the splitOn param if you have keys other than Id
Parameter name: splitOn
If I do the same query in SQLDev I get
By selecting * you get the columns of every joined table. Also you set splitOnto BreedId. Now Dapper expects that to separate the row columns of one joined table from the next, it should look for a column named BreedId.
This does not work because all tables except AnalysisRulesBreeds use Id as id column name.
Try removing the splitOn parameter, then it will default to Id. Then adjust your select-clause to only select from the tables you actually need in the result, eg.
select AnalysisRule.*, AnalysisCategory.*, Analysis.*, Breed.*
(assuming that your Analysis table and AnalysisCategory table follow the convention of having an Id column named ´Id´).

Categories

Resources