WCF method's breakpoint not being hit - wcf

I am new to WCF and I am building a service to do CRUD operations. I have created a new void method that takes two parameters. I have set a breakpoint and in debugging mode I have pasted this URL:
http://localhost:55152/WcfDataService.svc/AddNewNote()?ParamNoteTitle='dfdfdf'&ParamNoteText='dfdfdfdf'
This is my code:
[WebGet]
public void AddNewNote(string ParamNoteTitle, string ParamNoteText)
{
//My hardcoded values for now...
int ParentID = 8879;
int JobID = 1000088150;
int ContactID = 309;
Guid UserID = Guid.NewGuid();
string RelatedType = "Advertiser Contact";
bool IsShared = true;
tblNote N = new tblNote
{
NotesTitle = ParamNoteTitle,
NotesText = ParamNoteText,
ParentID = ParentID,
ContactID = ContactID,
JobID = JobID,
UserID = UserID,
GroupID = null,
RelatedType = RelatedType,
IsShared = IsShared
};
this.CurrentDataSource.tblNotes.Add(N);
this.CurrentDataSource.SaveChanges();
}
I am getting a 404 error. Is there a problem with my query string/URL?

First of all, check if the service itself is running correctly by pasting this URL in your browser:
http://localhost:55152/WcfDataService.svc
If so, I would recommend you to have a look at this similar SO question.
Hope that helps!

I ended up changing my method type to IQueryable and calling a method to retrieve the new row with my ID after the insert. I originally wanted to return an integer or bool so in my Javascript I can handle success or failure by looking at the return value.
[WebGet]
public IQueryable<vw_Note> AddNewNote(string ParamNoteTitle, string ParamNoteText)
{
//My hardcoded values for now...
int ParentID = 8879;
int JobID = 1000088150;
int ContactID = 309;
Guid UserID = new Guid("8b0e303a-68aa-49a5-af95-d994e2bdd5ac");
Guid NoteID = Guid.NewGuid();
string RelatedType = "Advertiser Contact";
bool IsShared = true;
tblNote N = new tblNote
{
NotesID = NoteID,
NotesTitle = ParamNoteTitle,
NotesText = ParamNoteText,
ParentID = ParentID,
ContactID = ContactID,
JobID = JobID,
UserID = UserID,
GroupID = null,
RelatedType = RelatedType,
IsShared = IsShared
};
try
{
this.CurrentDataSource.tblNotes.Add(N);
this.CurrentDataSource.SaveChanges();
return GetNoteByID(NoteID);
}
catch (Exception ex)
{
return GetNoteByID(NoteID);
}
}
As you can see it will return a set of data. I was stuck on how to handle inserting data using WCF and then responding to a client request but I managed to solve it. Thanks anyway!

Related

MediaStore select query returns only one row

I'm trying to develop music player, I've made a loader and adapter for my data retreiving from mediastore, but when I call query from my app it only returns one row, I don't know wat's wrong with my code, would u help me fixing that problem?
That's my loader which should return a list I'll use in another place
public static List<Song> getAllArtistSongs(Context context, long artist_id){
List<Song> ArtistSongList = new ArrayList<>();
Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String[] projection = new String[]{
"_id",
"title",
"album_id",
"album",
"artist",
"duration",
"track"
};
String sortorder = MediaStore.Audio.Media.DEFAULT_SORT_ORDER;
String selection = "is_music=1 and artist_id="+artist_id;
Cursor cursor = context.getContentResolver().query(uri, projection, selection, null, sortorder);
assert cursor != null;
if (cursor.moveToFirst()) {
do {
int trackNumber = cursor.getInt(6);
while (trackNumber >= 1000) {
trackNumber -= 1000;
}
Long id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID));
String title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));
Long albumid = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID));
String albumname = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM));
String artistname = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST));
int duration = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION));
ArtistSongList.add(new Song(id, title, albumid, albumname, artist_id, artistname, duration, trackNumber));
} while (cursor.moveToNext());
cursor.close();
}
return ArtistSongList;
}
And this is the adapter which I use to bind to a recyclerview
public void onBindViewHolder(#NonNull VH holder, int position) {
Song song = artistSongList.get(position);
if(song!=null){
holder.ttv.setText(song.title);
holder.dtv.setText(song.artistName);
int trackN = song.trackNumber;
if(trackN==0){
holder.ntv.setText("_");
}else holder.ntv.setText(String.valueOf(trackN));
}
}
And this is where I call the query func
private void setupAlbumList() {
System.out.println(artistId);
songList = ArtistSongLoader.getAllArtistSongs(getActivity(), artistId);
adapter = new ArtistSongAdapter(getActivity(), songList);
recy.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL));
recy.setAdapter(new ArtistSongAdapter(getActivity(), songList));
}
Thx in advance for helping
My example to retrieve all tracks:
private final String track_id = MediaStore.Audio.Media._ID;
private final String track_no = MediaStore.Audio.Media.TRACK;
private final String track_name = MediaStore.Audio.Media.TITLE;
private final String artist = MediaStore.Audio.Media.ARTIST;
private final String artist_id = MediaStore.Audio.Media.ARTIST_ID;
private final String duration = MediaStore.Audio.Media.DURATION;
private final String album = MediaStore.Audio.Media.ALBUM;
private final String composer = MediaStore.Audio.Media.COMPOSER;
private final String year = MediaStore.Audio.Media.YEAR;
private final String path = MediaStore.Audio.Media.DATA;
private final String date_added = MediaStore.Audio.Media.DATE_ADDED;
public Cursor getAllTracks(Context context) {
// gets all tracks
if (context != null) {
ContentResolver cr = context.getContentResolver();
final String[] columns = {track_id, track_no, artist, track_name,
album, duration, path, year, composer};
return cr.query(uri, columns, null, null, null);
} else {
return null;
}
}
then you have
String selection = "is_music=1"
first, you do not need is_music=1. For multiple tracks you of course need more than 1 track by the same artist
The adapter is irrelevant, the query does the selection
To return albums for an artist
public Cursor getArtistsAlbumcursor(Context context, String artistId) {
ContentResolver cr = context.getContentResolver();
final String _id = MediaStore.Audio.Media._ID;
final String album_id = MediaStore.Audio.Media.ALBUM_ID;
final String artistid = MediaStore.Audio.Media.ARTIST_ID;
final String[] columns = {_id, album_id, artistid};
if (artistId != null) {
String where = artistid + " =?";
String[] aId = {artistId};
return cr.query(uri, columns, where, aId, null);
} else {
return null;
}
}

Dapper for NET Core: Insert into a table and return id of inserted row [duplicate]

This question already has answers here:
How do I perform an insert and return inserted identity with Dapper?
(9 answers)
Closed 6 months ago.
I have the following method in my repository. As of now, i believe the int returned is simply one indicating whether or not the operation was successful. I want the int to be the id (which is the single column of the table) to be returned after a successful execution. How do i accomplish this?
public async Task<int> AddNewGroup()
{
using(_connection)
{
_connection.Open();
var id = await _connection.ExecuteAsync("INSERT INTO groups").Single();
}
}
You can run a query which has 2 parts, first is your INSERT part and second is a SELECT part. In the SELECT part, you can return(select) whatever column value you want.
For example, If your group table has a primary key column called GroupId and you have set that column for Identity value generation(automatic value generation), you can call the SCOPE_IDENTITY() to get the generated value.
We will use the QueryAsync method.
public async Task<int> AddNewGroup()
{
using(_connection)
{
_connection.Open();
var q = #"INSERT INTO Groups(Name,Description) VALUES
(#name, #desc); SELECT CAST(SCOPE_IDENTITY() as int)"
var result = await _connection.QueryAsync<int>(q,
new { #name="some name", #desc="some desc"});
return result.Single();
}
}
You don't have to create by hand the insert query, you can use Dapper.Contrib github which helps you to manage CRUD operations.
Using Dapper.Contrib you can do something like:
public async Task<int> AddNewGroup(Group entity)
{
using (_connection)
{
_connection.Open();
var id = await _connection.InsertAsync(entity);
}
}
If you're using SQL Azure / SQL Server, you need to return the inserted value from the query using something like
INSERT INTO groups OUTPUT inserted.id VALUES (...)
and then instead using ExecuteAsync use ExecuteScalarAsync
Reference to the OUTPUT clause here:
https://learn.microsoft.com/en-us/sql/t-sql/queries/output-clause-transact-sql?view=sql-server-2017
public static void Main()
{
string sql = #"INSERT INTO Customers (CustomerName, ContactName, Address, City, PostalCode, Country)
Values (#CustomerName, #ContactName, #Address, #City, #PostalCode, #Country);
SELECT CustomerID FROM Customers WHERE CustomerID = SCOPE_IDENTITY();";
using (var connection = new SqlConnection(GetConnectionString()))
{
Customer c = new Customer("Brian Adams", "Brian", "12 Jones Place", "New York", "NY12", "CA");
var id = connection.QueryFirstOrDefault<int>(sql, c);
Console.WriteLine("The Customer ID is " + id);
sql = "Select * FROM CUSTOMERS WHERE CustomerID = #ID";
var rc = connection.QueryFirstOrDefault<Customer>(sql, new{ #ID = id });
}
}
}
The "correct" way that I took is (Showing repository method using Guid Id to return):
public async Task<Guid> CreateClient(ClientEntity clientModel)
{
const string sql = #"
INSERT INTO dbo.Clients
(
ClientCode,
Name,
IsActive
)
OUTPUT Inserted.ClientId
VALUES
(
#ClientCode,
#Name,
#IsActive
)";
using var dbConnection = await _databaseProvider.GetConnection();
var result = await dbConnection.ExecuteScalarAsync(sql, new
{
ClientCode = clientModel.Code,
Name = clientModel.Name,
IsActive = clientModel.IsActive
});
if (result != null)
{
return Guid.Parse(result.ToString());
}
else {
return Guid.Empty;
}
}
You can use the RETURNING id in the insert statement. This is using C# and dapper.
private readonly NpgsqlConnection _connection = new NpgsqlConnection();
var sqlInsert = $""""
INSERT INTO tabel_name (column_name)
VALUES ('value')
RETURNING id;
"""";
var id = await _connection.ExecuteScalarAsync(sqlInsert);
And if you want to determine the key type coming back you can use:
var id = await _connection.ExecuteScalarAsync<int>(sqlInsert);
Where you specify the type in the <> brackets. If you do not specify, it will return the object type.

How to run a count in Entity Framework

I have a Real Estate site with a Properties table and a PropertyImages table.
When the user uploads a picture, I want to run a query in the PropertyImages. then append that number to the picturename.
public IQueryable GetPictureCount()
{
int propertyId = Convert.ToInt16(ddlSelectProperty.SelectedValue);
var _db = new RESolution.Models.PropertyContext();
IQueryable query = _db.PropertyImages;
var mypic = (from c in _db.PropertyImages
where c.PropertyID == propertyId
select c).FirstOrDefault();
lblCount.Text = Convert.ToString(query);
}
I get this error: "Not all code paths return a value"
My environment is as follows:
VS Express 2013
Sql Express
win 8.1 development computer
when I change IQuaryable to "void" I get the error
'lblCount.text = query.Count().ToString();'
System.Linq.IQueryable does not contain a definition for 'Count' I have looked for a using directive but found non
I am still a little confused, though you guys are a god send.
Here is where I was able to get to work.
public void GetPictureCount()
{
lblfnameCheck.Text = ddlSelectProperty.SelectedValue;
int propertyId = Convert.ToInt32(lblfnameCheck.Text);
var _db = new RESolution.Models.PropertyContext();
var count = _db.PropertyImages.Count(t => t.PropertyID == propertyId);
lblCount.Text = Convert.ToString(count);
}
As dasblinkenlight mentioned, it seems that you do not want to return something, so make the method a void.
You probable want something like this:
public void GetPictureCount()
{
int propertyId = Convert.ToInt16(ddlSelectProperty.SelectedValue);
var _db = new RESolution.Models.PropertyContext();
IQueryable query = _db.PropertyImages;
var mypic = (from c in _db.PropertyImages
where c.PropertyID == propertyId
select c).FirstOrDefault();
var nrOfPics = Convert.ToString(query.Count());
lblCount.Text = nrOfPics;
mypic.name = mypic.name + nrOfPics; // I am guessing the name property
_db.SaveChanges();
}

Apex test method error

we are having an issue with apex test method. we have an http request in my apex method. i have to creates a test method for this. but we are having an issue
Apex code:
public void ReceiveBestAnswer(Id repID){
reply = GlobalFunctions.getReplyCreatorDetails(repID);
System.debug(reply);
String bestReplyUser = reply.CreatedById;
profileUser = [select Username,Email from User where Id = :bestReplyUser];
System.debug('profileUser.Name-->'+profileUser.Username);
HttpRequest req = new HttpRequest();
HttpResponse res = new HttpResponse();
Http http = new Http();
req.setEndpoint('http://sandbox.v2.badgeville.com/api/berlin/b95839370dca983955e550296450ec03/activities.json');
req.setMethod('GET');
req.setBody('activity[verb]=receive best answer&site=www.grazitti.com&user='+ profileUser.Email );
try {
http.send(req);
System.debug('res-->');
} catch(System.CalloutException e) {
System.debug('Callout error: '+ e);
System.debug(res.toString());
}
}
Test method :
public static testMethod void testReceiveBestAnswer() {
Reply reply = new Reply();
String replyId = reply[0].Id;
// Id questionID = '906P0000000010KIAQ';
// String sql = 'SELECT Id, Title FROM Question';
// Question[] ques = [SELECT Id FROM Question];
// Question quest = ques[0];
BadgeVilleWebServiceCallout bv = new BadgeVilleWebServiceCallout();
bv.badgvilleReceiveBestAnswer(replyId );
}
2nd test method:
public static testmethod void testReceiveBestAnswer1()
{
ID ProfileID = [ Select id from Profile where name = 'Named Marketo Community Portal'].id;
System.debug('ProfileID --->'+ProfileID );
Account A1 = new Account(Name = 'Test Account');
insert A1;
List<Contact> ContactList = new List<Contact>();
Contact C1 = new Contact( AccountID = A1.id, FirstName = 'Test', LastName = 'User',
email = 'test-user#fakeemail.com' );
insert C1;
User u1 = new User( email='test-user#fakeemail.com', contactid = c1.id, profileid = profileid,
UserName='test-user#fakeemail.com', alias='tuser1', CommunityNickName='tuser1', TimeZoneSidKey='America/New_York', LocaleSidKey='en_US', EmailEncodingKey='ISO-8859-1', LanguageLocaleKey='en_US', FirstName = 'Test', LastName = 'User' );
insert u1;
System.debug('u1--->'+u1 );
/* User[] users = [SELECT Id FROM User];
User u1 = users[0];
// Question[] ques = [SELECT Id FROM Question];
//System.debug('ques --->'+ques );
// Question quest = ques[0];
// BadgeVilleWebServiceCallout bv = new BadgeVilleWebServiceCallout();
// bv.badgvilleReceiveBestAnswer(quest.Id);
Id questId ;
Test.startTest();
System.RunAs(u1)
{
Question question = new Question();
question.Title = 'Test';
question.Body = 'Test';
question.CommunityId = '09a50000000PNNr';//GlobalFunctions.communityId;
insert question;
System.debug('question-->'+question);
questId = question.Id;
System.debug('questId --->'+questId );
Reply reply1 = new Reply();
reply1.Body = 'reply no 1 ques 1';
reply1.QuestionId = question.Id;
System.debug('reply1--->'+reply1);
insert reply1;
}
// BadgeVilleWebServiceCallout bv = new BadgeVilleWebServiceCallout();
// bv.badgvilleReceiveBestAnswer(questId);
Test.stopTest();
}
We are trying to implement this with both test methods. but we are unable to do it. can nay body have any idea how to do this or where i am doing wrong??
Salesforce doesn't allow you to Test external WebServices.
Here, you have an idea of how to test WebServices.
And here you have a Salesforce Idea.
I hope this help you.

How can I make a nested projection in a Linq query when using the group by clause?

I'm trying to work with grouped data coming back from SQL.
The method I'm writing is to provide the data for a "Case Status Overview" screen.
It must produce a nested XML document.
Now, I could do it the easy way, but I'm trying to learn whether it's possible to use the linq "group by" statement and then to project the data already nested. (the easy way would be just to pull back the data in a tabular fashion from the database and then for-loop through it forming the Xml document for output)
Here is the data hierarchy:
Every Case has a DebtType and every DebtType has a Client.
Here is the SQL that retrieves the data:
SELECT ClientNames.ClientID ,
ClientNames.ClientCode ,
ClientNames.ClientName ,
DebtTypes.DebtTypeID ,
DebtTypes.DebtTypeShortDesc ,
DebtTypes.DebtTypeLongDesc ,
Cases.CurrentStateCode ,
SUM(1 - CAST(Cases.CaseClosed AS INT)) AS OpenCaseCount ,
SUM(CAST(Cases.CaseClosed AS INT)) AS ClosedCaseCount ,
SUM(CAST(Cases.CaseOnHold AS INT)) AS OnHoldCaseCount ,
SUM(CAST(Cases.CaseReferred AS INT)) AS ReferredCaseCount ,
COUNT(Cases.CaseID) AS TotalCaseCount ,
SUM(Cases.CaseTotalPaid) AS TotalAmountPaid ,
SUM(Cases.CaseCurrentOutstandingAmount) AS TotalAmountOutstanding,
SUM(Cases.CaseTotalDebtWrittenOff) AS TotalAmountWrittenOff ,
SUM(Cases.CaseTotalDebtCancelled) AS TotalAmountCancelled
FROM ClientNames
INNER JOIN ClientDebtTypes
ON ClientNames.ClientID = ClientDebtTypes.ClientID
INNER JOIN DebtTypes
ON ClientDebtTypes.DebtTypeID = DebtTypes.DebtTypeID
INNER JOIN Cases
ON ClientDebtTypes.ClientDebtTypeID = Cases.CaseClientDebtTypeID
GROUP BY ClientNames.ClientID ,
ClientNames.ClientCode ,
ClientNames.ClientName ,
DebtTypes.DebtTypeID ,
DebtTypes.DebtTypeShortDesc,
DebtTypes.DebtTypeLongDesc ,
Cases.CurrentStateCode
ORDER BY ClientNames.ClientID,
DebtTypes.DebtTypeID,
CurrentStateCode
Using Linqer it converts it to:
from clientnames in db.ClientNames
join clientdebttypes in db.ClientDebtTypes on clientnames.ClientID equals clientdebttypes.ClientID
join debttypes in db.DebtTypes on clientdebttypes.DebtTypeID equals debttypes.DebtTypeID
join cases in db.Cases on new { ClientDebtTypeID = clientdebttypes.ClientDebtTypeID } equals new { ClientDebtTypeID = cases.CaseClientDebtTypeID }
group new {clientnames, debttypes, cases} by new {
clientnames.ClientID,
clientnames.ClientCode,
clientnames.ClientName1,
debttypes.DebtTypeID,
debttypes.DebtTypeShortDesc,
debttypes.DebtTypeLongDesc,
cases.CurrentStateCode
} into g
orderby
g.Key.ClientID,
g.Key.DebtTypeID,
g.Key.CurrentStateCode
select new {
ClientID = (System.Int32?)g.Key.ClientID,
g.Key.ClientCode,
g.Key.ClientName1,
DebtTypeID = (System.Int32?)g.Key.DebtTypeID,
g.Key.DebtTypeShortDesc,
g.Key.DebtTypeLongDesc,
g.Key.CurrentStateCode,
OpenCaseCount = (System.Int64?)g.Sum(p => 1 - Convert.ToInt32(p.cases.CaseClosed)),
ClosedCaseCount = (Int32?)g.Sum(p => Convert.ToInt32(p.cases.CaseClosed)),
OnHoldCaseCount = (Int32?)g.Sum(p => Convert.ToInt32(p.cases.CaseOnHold)),
ReferredCaseCount = (Int32?)g.Sum(p => Convert.ToInt32(p.cases.CaseReferred)),
TotalCaseCount = (Int64?)g.Count(p => p.cases.CaseID != null),
TotalAmountPaid = (System.Decimal?)g.Sum(p => p.cases.CaseTotalPaid),
TotalAmountOutstanding = (System.Decimal?)g.Sum(p => p.cases.CaseCurrentOutstandingAmount),
TotalAmountWrittenOff = (System.Decimal?)g.Sum(p => p.cases.CaseTotalDebtWrittenOff),
TotalAmountCancelled = (System.Decimal?)g.Sum(p => p.cases.CaseTotalDebtCancelled)
}
Now as I mentioned, I could stop there and right a for loop to create the Xml data.
But I'm trying to create a nested group (IGrouping<ClientName,IGrouping<DebtType,SummaryClass>>)
and then project the data in a nested format.
Now we're using LinqToXsd to create strong type wrappers for out Xml documents, but essentially all this means is that out output type is:
private class ClientSummary
{
public string ClientName { get; set; }
public IList<DebtTypeSummary> DebtTypes { get; set; }
}
private class DebtTypeSummary
{
public string DebtType { get; set; }
public IList<StateCodeSummary> StateCodes { get; set; }
}
private class StateCodeSummary
{
public string StateCode { get; set; }
public int TotalCount { get; set; }
public decimal TotalAmountPaid { get; set; }
//etc
//etc
//etc
}
Now I got as far as writing the following Linq:
var grouping = from cases in db.Cases
join clientdebttypes in db.ClientDebtTypes on cases.CaseClientDebtTypeID equals clientdebttypes.ClientID
join debttypes in db.DebtTypes on clientdebttypes.DebtTypeID equals debttypes.DebtTypeID
group cases by new ClientDebtTypePair() { ClientDebtType = clientdebttypes, DebtType = debttypes } into casesByClientDebtTypes
join clientnames in db.ClientNames on casesByClientDebtTypes.Key.ClientDebtType.ClientName equals clientnames
group casesByClientDebtTypes by clientnames;
var projected = from casesByClientDebtTypes in grouping
let client = casesByClientDebtTypes.Key
select new LoadCaseStatusOverviewScreenOutput.ClientsLocalType()
{
Client = new Client()
{
ClientID = client.ClientID,
DisplayName = client.ClientName1,
},
DebtTypes = from cases in casesByClientDebtTypes
let debttype = cases.Key.DebtType
select new LoadCaseStatusOverviewScreenOutput.ClientsLocalType.DebtTypesLocalType()
{
DebtType = new DebtType()
{
DebtTypeID = debttype.DebtTypeID,
Description = debttype.DebtTypeLongDesc,
DisplayName = debttype.DebtTypeShortDesc,
},
StatesCodes = from cases2 in cases
select new LoadCaseStatusOverviewScreenOutput.ClientsLocalType.DebtTypesLocalType.StatesCodesLocalType()
{
ClosedCasesCount = cases2.Sum(p => Convert.ToInt32(p.cases.CaseClosed))
which joins and groups the database tables and then tries to project the result a ClientSummary (the class names are different but that's because the above is a simplified view of the output classes). I fail completely when I've drilled all the way down to the Cases table and I find that I don't really understand how to do agregate functions. They appear to only be available on IGrouping<K, T>s and it seems I've just got confused.
I need to also ensure that the summaries are calculated server side, pulling back millions of cases would be bad.
Can anybody help me with this one? Is this even possible?
Regards,
James.
-------### UPDATE 1 ###-------
OK, been working on this again today.
I decided to use Linq2SQL to pull pack 2D data and then reformat it using Linq2Objects.
Here is what I started with:
var sql = from clientnames in db.ClientNames
join clientdebttypes in db.ClientDebtTypes on clientnames.ClientID equals clientdebttypes.ClientID
join debttypes in db.DebtTypes on clientdebttypes.DebtTypeID equals debttypes.DebtTypeID
join cases in db.Cases on new { ClientDebtTypeID = clientdebttypes.ClientDebtTypeID } equals new { ClientDebtTypeID = cases.CaseClientDebtTypeID }
group new { clientnames, debttypes, cases } by new
{
clientnames.ClientID,
clientnames.ClientCode,
clientnames.ClientName1,
debttypes.DebtTypeID,
debttypes.DebtTypeShortDesc,
debttypes.DebtTypeLongDesc,
cases.CurrentStateCode
} into g
orderby
g.Key.ClientID,
g.Key.DebtTypeID,
g.Key.CurrentStateCode
select new
{
Client = new Client{ ClientID = g.Key.ClientID, DisplayName = g.Key.ClientName1 },
DebtType = new DebtType{ DebtTypeID = g.Key.DebtTypeID, DisplayName = g.Key.DebtTypeShortDesc, Description = g.Key.DebtTypeLongDesc },
StateSummary = new LoadCaseStatusOverviewScreenOutput.ClientsLocalType.DebtTypesLocalType.StatesCodesLocalType()
{
StateCode = g.Key.CurrentStateCode,
OpenCasesCount = g.Sum(p => 1 - Convert.ToInt32(p.cases.CaseClosed)),
ClosedCasesCount = g.Sum(p => Convert.ToInt32(p.cases.CaseClosed)),
OnHoldCasesCount = g.Sum(p => Convert.ToInt32(p.cases.CaseOnHold)),
ReferredCasesCount = g.Sum(p => Convert.ToInt32(p.cases.CaseReferred)),
TotalCasesCount = g.Count(p => p.cases.CaseID != null),
TotalAmountPaid = g.Sum(p => p.cases.CaseTotalPaid),
TotalAmountOutstanding = g.Sum(p => p.cases.CaseCurrentOutstandingAmount),
TotalAmountWrittenOff = g.Sum(p => p.cases.CaseTotalDebtWrittenOff),
TotalAmountCancelled = g.Sum(p => p.cases.CaseTotalDebtCancelled),
}
};
var res = sql.ToList();
output.Clients = (from results in res
group results by results.Client into resultsByClient
from resultsByDebtType in
(from results in resultsByClient
group results by results.DebtType)
group resultsByDebtType by resultsByClient.Key into resultsByDebtTypeByClient
select new LoadCaseStatusOverviewScreenOutput.ClientsLocalType()
{
Client = resultsByDebtTypeByClient.Key,
DebtTypes = (from resultsByDebtType in resultsByDebtTypeByClient
select new LoadCaseStatusOverviewScreenOutput.ClientsLocalType.DebtTypesLocalType()
{
DebtType = resultsByDebtType.Key,
StatesCodes = (from results in resultsByDebtType
let summary = results.StateSummary
select results.StateSummary).ToList()
}).ToList()
}).ToList();
That runs, but produces one Client/DebtType/Summary set for every result. So even though there is only one client in this case, I end up with 1300 clients, all identical.
I simplified it to the following:
output.Clients = (from results in res
group results by results.Client into resultsByClient
select new LoadCaseStatusOverviewScreenOutput.ClientsLocalType()
{
Client = resultsByClient.Key,
DebtTypes = null,
}).ToList();
That produces 1300 clients. Next I tried this:
output.Clients = (from results in res
group results by results.Client.ClientID into resultsByClient
select new LoadCaseStatusOverviewScreenOutput.ClientsLocalType()
{
Client = new Client { ClientID = resultsByClient.Key },
DebtTypes = null,
}).ToList();
And THAT produces ONE client (hurray!). Except I loose all the client information (boo!)
Guessing that as it's comparing client by refernce instead of by content I wrote the following:
public partial class Client
{
public static bool operator ==(Client left, Client right)
{
return left.ClientID == right.ClientID;
}
public static bool operator !=(Client left, Client right)
{
return left.ClientID != right.ClientID;
}
public override int GetHashCode()
{
return ClientID;
}
}
That did nothing. It repeatedly calls GetHashCode(), which I fudged to force it to return the same hash code for any matching ClientID, but it still created 1300 Client groups.
Regards,
James.
-------### UPDATE 2 ###-------
OK, I thought I would have a go at making the Linq2Sql output only simple values for grouping by:
g.Key.ClientID,
g.Key.ClientName1,
g.Key.DebtTypeID,
g.Key.DebtTypeShortDesc,
g.Key.DebtTypeLongDesc,
And then changed the test Linq2Objects to:
output.Clients = (from results in res
group results by new { ClientID = results.ClientID, DisplayName = results.ClientName1 } into resultsByClient
select new LoadCaseStatusOverviewScreenOutput.ClientsLocalType()
{
Client = new Client { ClientID = resultsByClient.Key.ClientID, DisplayName = resultsByClient.Key.DisplayName },
DebtTypes = null,
}).ToList();
That works. So anonymous types compare in the way I want them to, by content not reference (apparently)
This does not:
output.Clients = (from results in res
group results by new SiDemClient { ClientID = results.ClientID, DisplayName = results.ClientName1 } into resultsByClient
select new LoadCaseStatusOverviewScreenOutput.ClientsLocalType()
{
Client = resultsByClient.Key,//new Client { ClientID = resultsByClient.Key.ClientID, DisplayName = resultsByClient.Key.DisplayName },
DebtTypes = null,
}).ToList();
That still creates 1300 groups.
So, anonymous types compare in a magical way that I don't understand. How can I make my Client class compare like an anonymous type?
Regards,
James.
-------### SOLUTION FOUND ###-------
-------### MANY THANKS TO Enigmativity ###-------
I needed to override the Equals() method instead of implementing the == operator.
Now the grouping works and I have a wonderful Xml document to reutrn!
public partial class SiDemClient
{
public override bool Equals(object obj)
{
if (obj is SiDemClient)
{
return this.ClientID.Equals(((SiDemClient)obj).ClientID);
}
return false;
}
public override int GetHashCode()
{
return ClientID;
}
}
Many Thanks,
James.
When you override GetHashCode you must also override Equals. The == & != operators are irrelevant.
Try with this:
public partial class Client
{
public override bool Equals(object obj)
{
if (obj is Client)
{
return this.ClientID.Equals(((Client)obj).ClientID);
}
return false;
}
public override int GetHashCode()
{
return this.ClientID.GetHashCode();
}
}
See if that helps.