I'm stuck at something that seemed easy but became a headache pretty fast:
Here is a class that represent a structure I'm using:
public class LocumJobDistanceDifferenceObject {
public LocumJobDistanceDifferenceObject(Int64 ALocumID, Int64 AJobID, Decimal ADistanceMiles, Int32 ARateDifference, Boolean AIsDistanceUnderMax) {
LocumID = ALocumID;
JobID = AJobID;
DistanceMiles = ADistanceMiles;
RateDifference = ARateDifference;
IsDistanceUnderMax = AIsDistanceUnderMax;
}
public Int64 LocumID {
get;
set;
}
public Int64 JobID {
get;
set;
}
public Decimal DistanceMiles {
get;
set;
}
public Int32 RateDifference {
get;
set;
}
public Boolean IsDistanceUnderMax {
get;
set;
}
}
I create a List to store a matrix of information. Locum is a worker and he needs to be placed at a Job. Lest say I have 50 Jobs and 75 Locums. I build my matrix by running a Locums x Jobs algo that stores LocumID + JobID + Detrmine DistanceMiles between Locum and Job + Determine Rate that Job pays/hour and Locum wants/hour + If dostance to Job exceeds Locum's max distance he/she willing to travel
So, basically, since it's a Locums (75) x Jobs (50) number of rows in the Matrix.
Now, I need to run a loop (ForEach) on my Matrix (I call it MindMapTier01) as follows:
foreach (LocumJobDistanceDifferenceObject LocumJobDistanceDifferenceItem in MindMapTier01.OrderBy(order=>order.JobID)) {
/**
* Build a list (KeyValuePair<JobID, LocumID>) such that for each unique JobID,
* I can assign the Locum closest to that Job. I need to keep in mind that
* once a job is assigned, I dont want that JobID or LocumID for the next iteration
**/
}
I hope I explained myself. I need to get over this within an hour or two. Please help.
Regards.
I don't know that I fully understand your problem, but if you want to ensure that a job is assigned to the closest locum then your code could look like this:
Dictionary<Int64, Int64> dicJobLocum = New Dictionary<Int64, Int64>(); // This is the key value pair list
Dictionary<Int64, Int64> dicJobDistance = New Dictionary<Int64, Decimal>(); // This is to track the distance of the currently assigned locum
foreach (LocumJobDistanceDifferenceObject locum in MindMapTier01) {
if (dicJobDistance.ContainsKey(locum.JobID) {
Decimal distance = dicJobDistance(locum.JobID);
// If the job has been assigned, check if the current locum is closer
if (locum.DistanceMiles < distance) {
dicJobDistance(locum.JobID) = locum.Distance;
dicJobLocum(locum.JobID) = locum.LocumID;
}
}
else {
// If the job has not been assigned yet
dicJobDistance.Add(locum.JobID, locum.DistanceMiles);
dicJobLocum.Add(locum.JobID, locum.LocumID);
}
}
Please excuse any minor syntax errors, I have not been using c# recently.
Related
Recently I'm using ASP Boiler Plate for my project. I found that the audit log was created automatically after the API function ended. However, the audit value inserted into the db was truncated and replaced with three dots.
I've gone through a lot of forum but there isn't any solution found.
Is there any setting that we can make to change the behavior of replacing the data with three dots?
example as below:-
{"input":{"id":12345,"modificationDT":"2022-08-15T10:00:00.000000+08:00","isChangesMade":false,"details":{"status":13,"emailSentDT":"2021-05-0T00:00:00","emailSentId":12,"msgSentDT":"ASAP","remark":null,"itemsRepresentativeId":15,"itemsRepresentativeName":"xxxxxx"},"itemsChosen":[{"id":527,"itemDescription":"xxxxx xxxxx xxxxx xxxxxxx ","isItemInUse":true,"itemPrice":13.2,"itemQuantity":25,"remarks":null}],"return":[{"itemId":595,"itemCategory":"11","itemReturnRemarks":"","isItemBroken":false,"returningPolicyAcceptanceStatus":true,"isCompensationNeeded":false,"compensationMethod":0,"adminRemarksOnItem":"","CompensationAmount":0.0,"userUpdate":true,"itemImage":[{"compensationId":900,"itemImageURL":"https://ksuHVUJH-jnsadkna.KBidbwJBK!#.OLjba7s87/HBBDA/hbjdas-!#!!##!j-jb3123-31231knc^&kn/jkkdwqjbdkjq(-60.jpg","imageId":109231}],"imageCreationDT":"2021-01-01T15:29:23.728136","itemModifiedDT":"2021-05-05T10:10:10.120912+08:00","UserAcceptanceId":"JDSAJBD-FKAJBFKB-FKQJFBKBWF-KSJABKFBAS-XXXX","adminId":89182},{"itemId":907,"itemCategory":"21","itemReturnRemarks":"XXXXXX","isItemBroken":true,"returningPolicyAcceptanceStatus":true,"isCompensationNeeded":true,"compensationMethod":3,...
You can increase AuditLogActionConsts.MaxParametersLength in the PreConfigureServices method of your module.
The default values from AuditLogActionConsts.cs are:
public static int MaxServiceNameLength { get; set; } = 256;
public static int MaxMethodNameLength { get; set; } = 128;
public static int MaxParametersLength { get; set; } = 2000;
So I'm using the C# nuget wrapper around Azure Search. My problem is I have a index of products:
public class ProductDocument
{
[System.ComponentModel.DataAnnotations.Key]
public string Key { get; set; }
[IsSearchable]
public string Sku { get; set; }
[IsSearchable]
public string Name { get; set; }
[IsSearchable]
public string FullDescription { get; set; }
[IsSearchable]
public List<CustomerSkuDocument> CustomerSkus { get; set; }
}
public class CustomerSkuDocument
{
[IsSearchable]
public int AccountId { get; set; }
[IsSearchable]
public string Sku { get; set; }
}
Example data would be:
new Product() { Key= 100,Name="Nail 101",Sku = "CCCCCCCC", CustomerSkus = new List<ProductCustomerSku>()
{
new ProductCustomerSku() {AccountId = 222, CustomerSku = "BBBB"},
new ProductCustomerSku() {AccountId = 333, CustomerSku = "EEEEEEE"}
}
So the problem is around CustomerSkuDocument.
When I Search I need to pass the AccountId in as well as the search term, however the AccountId is only used for when searching the ProductCustomerSkus.
Basically an Account can have different customer skus but it's only associated to that account - I don't want a separate index per account.
So my call would be something like /AccountId=222&term=BBBB which would find the match.
However /AccountId=333&term=BBBB would not find a match.
So I'm calling it like:
SearchParameters sp = new SearchParameters();
sp.SearchMode = SearchMode.Any;
sp.QueryType = QueryType.Full;
DocumentSearchResult<ProductDocument> results =
productIndexClient.Documents.Search<ProductDocument>(term, sp);
Where term is the normal search term, tried it with adding the AccountId but it doesn't work.
Azure Search does not support repeating data structures nested under a property of the outer document. We're working on this (see https://feedback.azure.com/forums/263029-azure-search/suggestions/6670910-modelling-complex-types-in-indexes), but we still have some work to do before we can release that.
Given that, the example you're showing is not probably indexing the nested parts. Can you post the search index definition you're using? While we work in direct support for complex types, you can see your options for approach here: https://learn.microsoft.com/en-us/azure/search/search-howto-complex-data-types
From the above you'll arribe at a index structure that will also guide your query options. If all you need is equality, perhaps you can simply include the accountId and the SKU in the same field and use a collection field so you can have multiple instances. For your query you would issue a search query that requires the accountId and has the rest as optional keywords.
Afternoon,
I'm writing a custom median function (without looking at existing solutions, i like the challenge), after lots of fiddling I'm most of the way there. I cannot however pass in a column that contains a null value. I'm handling this in the c# Code but it seems to be being stopped by SQL before it gets there.
You get this error...
Msg 6569, Level 16, State 1, Line 11 'Median' failed because parameter 1 is not allowed to be null.
C#:
namespace SQLMedianAggregate
{
[System.Serializable]
[Microsoft.SqlServer.Server.SqlUserDefinedAggregate(
Microsoft.SqlServer.Server.Format.UserDefined,
IsInvariantToDuplicates = false, // duplicates may change results
IsInvariantToNulls = true, // receiving a NULL is handled later in code
IsInvariantToOrder = true, // is sorted later
IsNullIfEmpty = true, // if no values are given the result is null
MaxByteSize = -1,
Name = "Median" // name of the aggregate
)]
public struct Median : IBinarySerialize
{
public double Result { get; private set; }
public bool HasValue { get; private set; }
public DataTable DT_Values { get; private set; } //only exists for merge essentially
public static DataTable DT_Final { get; private set; } //Need a static version so its accesible within terminate
public void Init()
{
Result = double.NaN;
HasValue = false;
DT_Values = new DataTable();
DT_Values.Columns.Add("Values", typeof(double));
DT_Final = new DataTable();
DT_Final.Columns.Add("Values", typeof(double));
}
public void Accumulate(double number)
{
if (double.IsNaN(number))
{
//skip
}
else
{
//add to tables
DataRow NR = DT_Values.NewRow();
NR[0] = number;
DT_Values.Rows.Add(NR);
DataRow NR2 = DT_Final.NewRow();
NR2[0] = number;
DT_Final.Rows.Add(NR2);
HasValue = true;
}
}
public void Merge(Median group)
{
// Count the product only if the other group has values
if (group.HasValue)
{
DT_Final.Merge(group.DT_Values);
//DT_Final = DT_Values;
}
}
public double Terminate()
{
if (DT_Final.Rows.Count == 0) //Just to handle roll up so it doesn't crash (doesnt actually work
{
DataRow DR = DT_Final.NewRow();
DR[0] = 0;
DT_Final.Rows.Add(DR);
}
//Sort Results
DataView DV = DT_Final.DefaultView;
DV.Sort = "Values asc";
DataTable DTF = new DataTable();
DTF = DV.ToTable();
////Calculate median and submit result
double MiddleRow = (DT_Final.Rows.Count -1.0) / 2.0;
if (MiddleRow % 2 != 0)
{
double upper = (double)(DT_Final.Rows[Convert.ToInt32(Math.Ceiling(MiddleRow))]["Values"]);
double lower = (double)(DT_Final.Rows[Convert.ToInt32(Math.Floor(MiddleRow))]["Values"]);
Result = lower + ((upper - lower) / 2);
} else
{
Result = (double)(DT_Final.Rows[Convert.ToInt32(MiddleRow)]["Values"]);
}
return Result;
}
public void Read(BinaryReader SerializationReader)
{
//Needed to get this working for some reason
}
public void Write(BinaryWriter SerializationWriter)
{
//Needed to get this working for some reason
}
}
}
SQL:
DROP AGGREGATE dbo.Median
DROP ASSEMBLY MedianAggregate
CREATE ASSEMBLY MedianAggregate
AUTHORIZATION dbo
FROM 'C:\Users\#######\Documents\Visual Studio 2017\Projects\SQLMedianAggregate\SQLMedianAggregate\bin\Debug\SQLMedianAggregate.dll'
WITH PERMISSION_SET = UNSAFE;
CREATE AGGREGATE dbo.Median (#number FLOAT) RETURNS FLOAT
EXTERNAL NAME [MedianAggregate]."SQLMedianAggregate.Median";
Any ideas of what setting or code i'm missing that will allow this. I pretty much just want it to ignore nulls.
SQL Version is SQL2008 R2 btw
The problem is your datatype. You need to use the Sql* types for SQLCLR parameters, return values, and result set columns. In this case, you need to change:
Accumulate(double number)
into:
Accumulate(SqlDouble number)
Then, you access the double value using the Value property that all Sql* types have (i.e. number.Value in this case).
And then, at the beginning of the Accumulate method, you need to check for NULL using the IsNull property:
if (number.IsNull)
{
return;
}
Also, for more information on using SQLCLR in general, please see the series I am writing on this topic on SQL Server Central: Stairway to SQLCLR (free registration is required to read content on that site, but it's worth it :-).
And, since we are talking about median calculations here, please see the article I wrote (also on SQL Server Central) on the topic of UDAs and UDTs that uses Median as the example: Getting The Most Out of SQL Server 2005 UDTs and UDAs. Please keep in mind that the article was written for SQL Server 2005 which has a hard limit of 8000 bytes of memory for UDTs and UDAs. That limit was lifted in SQL Server 2008, so rather than using the compression technique shown in that article, you could simply set MaxByteSize in the SqlUserDefinedAggregate to -1 (as you are currently doing) or SqlMetaData.MaxSize (or something very close to that).
Also, DataTable is a bit heavy-handed for this type of operation. All you need is a simple List<Double> :-).
Regarding the following line of code (broken into 2 lines here to prevent the need to scroll):
public static DataTable DT_Final { get; private set; }
//Need a static version so its accesible within terminate
This is a huge misunderstanding of how UDAs and UDTs work. Please do NOT use static variables here. Static variables are shared across Sessions, hence your current approach is not thread-safe. So you would either get errors about it already being declared or various Sessions would alter the value unbeknownst to other Sessions, as they would all share the single instance of
DT_Final. And the errors and/or odd behavior (i.e. erroneous results that you can't debug) might happen in a single session if a parallel plan is used.
UDTs and UDAs get serialized to a binary value stored in memory, and then are deserialized which keeps their state intact. This is the reason for the Read and Write methods, and why you needed to get those working.
Again, you don't need (or want) DataTables here as they are over-complicating the operation and take up more memory than is ideal. Please see the article I linked above on UDAs and UDTs to see how the Median operation (and UDAs in general) should work.
Given the following:
public class SomePoco {
public int IntValue { get; }
}
and
CREATE TABLE SomePocoStorage (IntValue INT NOT NULL)
and
INSERT SomePocoStorage VALUES (1), (274)
If I call
connection.Query<SomePoco>("SELECT * FROM SomePocoStorage")
does Dapper handle populating the IntValue field on the returned SomePoco instances?
Good question! It isn't a scenario I've targeted, but I'd be more than happy to take a look at what would be involved. Since we already do a lot of nasty reflection, this could still be viable. Probably better as a github issue, but I'll have a look.
Update - it does now (at the current time, via repo only - not deployed):
[Fact] // passes
public void GetOnlyProperties()
{
var obj = connection.QuerySingle<HazGetOnly>(
"select 42 as [Id], 'def' as [Name];");
obj.Id.IsEqualTo(42);
obj.Name.IsEqualTo("def");
}
class HazGetOnly
{
public int Id { get; }
public string Name { get; } = "abc";
}
No because there's no way for Dapper to set the value of the property if that property only has a getter.
If I had a User model that looks like this:
public class User
{
public Guid Id { get; set; }
public DateTime CreatedAtUtc { get; set; }
public string Username { get; set; }
public string Country { get; set; }
}
...and I'd perform a query which starts at a given row and fetches a limited amount of further rows (basically for paginating over the results) that looks like this:
var spaniards = connection.Query<User>(
"select * from Users where Country=#Country OFFSET 0 ROWS FETCH NEXT 10 ROWS ONLY",
new { Country = "Spain" }).ToList();
.. using Dapper(.Net), would it be possible to get that particular, limited result set AND the total count of rows in one single query and if so.. how?
One way to do solve this is using the splitOn functionality, together with count(1) over()
Let's assume the following sql string:
var sql = "select *, overall_count = COUNT(1) OVER() from Users ORDER BY Id Asc OFFSET 5 ROWS;"
together with the following dapper.net call:
HashSet<int> hashSet = new HashSet<int>();
Func<User, int, User> map = (result, count) =>
{
hashSet.Add(count);
return result;
};
await connection.QueryAsync(sql, map, "overall_count").ConfigureAwait(false);
This will split the result of the dapper call into two parts (just like with joining different tables) and it will call the map functor each time, in this case we simply store the count in a hashset (you could then check if the hashSet.Count is 1)
Hope this helps
PS: I'm not certain that your OFFSET works without any ORDER BY