i am creating a custom CustomerID by getting the first double characters of first name and last name taken from textboxes as following:
string CustomID = (FirstNameTxtbox.Value.Substring(0, 2) + LastNametxtbox.Value.Substring(0, 2));
in order to avoid a Duplicated CustomerID
i am trying to:
get the 5th value which is the int since the first 4 values are characters. and then increase it by 1.
my attempt:
string s = cmd.CommandText= " Select MAX(CustomerID) from Customers";
string CustomID = (FirstNameTxtbox.Value.Substring(0, 2) + LastNametxtbox.Value.Substring(0, 2) + (Convert.ToInt32(s)+1)) ;
example:
First name= Mak
last name = shima
CustomerID = MASH1
next value will be e.g KAAR2
Your practice is not recommended, you should design a primary key with Auto Increment small value like integer, remember that the table is physically sorted on the clustered index (your primary key), and your non-clustered indexes are keyed based on your primary key.
You can add your combined value in another field if you still need it, and if your queries are based on it, create a non clustered index for it.
Make the primary key an auto increment integer and the CustomerID is a string that is computed based on the ID, this way it will never duplicate.
You can set the CustomerID by:
Computed Field based on the auto increment ID
Trigger that fires on insert and set the CustomerID
Custom Code in your back-end, add the customer object, retrieve the primary key and set the CustomerID
Your CustomerID can be something like 0000001.
ex: First customer will have a primary key of 1, the CustomerID will be 000001, next customer will be 000002 and so on.
Yes Primary key (id) is necessary but Customer ID can be Custom String like 'anything'.
I you want to generate Customer ID like this
*First name= Mak
last name = shima
CustomerID = MASH1
next value will be e.g KAAR2*
Use front end to generate KA(first)AR(last)+Sequence in Sql Server /Oracle.
CREATE SEQUENCE [dbo].[CustomerId_Generation]
AS [bigint]
START WITH 1
INCREMENT BY 1
MINVALUE -9223372036854775808
MAXVALUE 9223372036854775807
CACHE
GO
Get value of sequence and Add to database afterward.
There was a case that Same CutomerID for returning customer than i mean repetitive same CutomerID can also be handled by your code if you follow this approach.
Related
I have a database table:
Correlation
------------
JobId (PK),
SomeId (PK),
Order (PK),
MapGuid
I am trying to add a new Correlation (1, 1, 1) into existing Correlation table. The order of existing entities will be updated after Add. The existing entity at (1, 1, 1) will be updated (1,1,2).
However, on add, I get an error:
The instance of entity type 'Correlation' cannot be tracked because another instance with the same key value for {'JobId', 'SomeId', 'Order'} is already being tracked. When attaching existing entities, ensure that only one entity instance with a given key value is attached. Consider using 'DbContextOptionsBuilder.EnableSensitiveDataLogging' to see the conflicting key values.'
relevant piece of Code:
if (actionType == ActionType.Create)
{
_unitOfWork.Repository<Correlation>().Insert(
new Correlation
{
JobId = jobId,
SomeId = someId,
Order = order,
MapGuid = newGuid,
});
}
if (actionType == ActionType.Update)
{
// update the title, description, imagefile.
var coors = _unitOfWork.Repository<Correlation>()
.Get()
.Where(a => a.JobId == jobId && a.SomeId == someId && a.Order = order)
.FirstOrDefault();
// set updated values
if (coors != null)
{
coors.Order = newOrder;
}
}
}
How can I add a new entity and then update existing entities at the same time.
You've pretty much answered your own question.
"The order of existing entities will be updated after Add. The existing entity at (1, 1, 1) will be updated (1,1,2)."
EF cannot insert a 1,1,1 when one already exists, and in your case the context knows about it. Even if the context didn't know about it (detached) you'd still get potential errors about inserting duplicate rows.
The main issue you have here is in your design. You potentially have an existing record (1,1,1) that you want to insert a new (1,1,1) and make that old record (1,1,2). Of course that needs to cascade because any existing (1,1,2) would become (1,1,3) and so-forth. The design issue is that you've made the PK of your table a composite key, and a meaningful key at that, as opposed as a dumb, simple, meaningless key. Keys are an identifier to a unique record. By design they should be immutable. (Cannot change)
To solve your issue the easiest way, change your table design to:
Correlation
------------
CorrelationId (PK) Identity 1,1
JobId (FK),
SomeId (FK),
Order,
MapGuid
And create an index on JobId + SomeId + Order since that will be significant for whatever this table will probably be used for. You can even enforce no duplicates, but I'd be wary of that because there may be no guarantee that EF will update the existing rows before inserting the new one.
Now, when you go to do an insert:
var existingCorrelations = context.Correlations.Where(x => x.JobId == jobId && x.SomeId == someId).ToList();
foreach(var correlation in existingCorrelations)
{
correlation.Order += 1;
}
context.Correlations.Add( new Correlation { JobId = jobId, SomeId = someId, Order = 1, MapGuid = mapGuid } );
context.SaveChanges();
In this case the Correlation has a PK called correlation ID. It is a meaningless key in the sense that none of your business logic will ever depend on it except as an identifier for this specific record. Now you can change the Order of an existing record.
Agreeing with Panagoitis about the Generic repository. I don't see it adding anything of value in your case, it helps to keep things simple. The above example should be easy enough to revert back to the repository pattern if that is what you're stuck with.
If you cannot change the data schema, then you're pretty much painted into an ugly little corner of the bathroom. It can still be worked around, but that's a lot more fiddley work loading existing records and moving the non-key data along the chain all the way to a new row on the end, and replacing the first row with the new data. (don't go there, really. :)
I have a table Estimation which has an column EstimationNo,i am trying to get the max EstimationNo like this-
var result = cont.SalesEstimateCont.Where(x => x.Org_ID == CurrentOrgId);
var estimationMaxNo = result.Any() ? result.Max(x => x.EstimateNo) + 1 : 1;
var DigitalEstimate = new SalesEstimate()
{
EstimateNo=estimationMaxNo;
};
cont.Estimate.Add(DigitalEstimate );
cont.Savechanges();
but the problem is, if same table is saving by different users at same time its saving the same EstimationNo for both users. like- 10,10
Now, how to handle this issue..please give some solution.
Best strategy is to let db engine (I assume that it is SQL Server) handle incrementing of EstimateNo field. This can be done with identity specification which can be added to normal not primary key field also.
ALTER TABLE SalesEstimateCont drop column EstimateNo
go
ALTER TABLE SalesEstimateContadd Add EstimateNo int NOT NULL IDENTITY (1,1)
Please note: if you have existing data or some data should be modified, you may need some extra effort to achieve this (i.e with temp tables and by setting IDENTITY INSERT ON)
I got a simple answer.I just had to use transacationScope class.
and lock the resource table. like this-
using (TransactionScope scope = new TransactionScope())
{
cont.Database.ExecuteSqlCommand("SELECT TOP 1 * FROM Sales__Estimate WITH (TABLOCKX, HOLDLOCK)");
var result = cont.SalesEstimateCont.Where(x => x.Org_ID == CurrentOrgId);
var estimationMaxNo = result.Any() ? result.Max(x => x.EstimateNo) + 1 : 1;
var DigitalEstimate = new SalesEstimate()
{
EstimateNo=estimationMaxNo;
};
cont.Estimate.Add(DigitalEstimate );
cont.Savechanges();
}
If you can make EstimateNo an Identity column, that is the easiest/best way to fix this. If you can change this to a Guid, that would be another easy way to fix this as PK would be unique regardless of the user.
If you can't do either of these and you must take Max() manually, you might want to consider creating another table that stores the next available number there. Then you can create a new SqlCommnand with a Serializable transaction to lock the table, update the # by 1 and select it back. If two update commands hit at the same time, only one update will run and won't let go until that connection with Serializable transaction gets closed. This allows you to select the newly updated number before the other update runs and get the now "unique" next number.
You can OrderByDescending and then Take the the first record
var estimationMaxNo = result.OrderByDescending(x => x.EstimateNo).Take(1);
It can be done in a single command. You need to set the IDENTITY property for primary id
ALTER TABLE SalesEstimateCont ADD Org_ID int NOT NULL IDENTITY (1,1) PRIMARY KEY
How can I update my column values in a table, with the help of primary key ?
Or can I override somehow the primary key?
Primary key is unique so you cannot change it. If you want to update the values using primary key then it is possible. But you cannot update the primary key.
this question sounds like youre trying to do another insert rather than an update.
If you're doing
insert into table (col1, col2, col3) values ('primaryKeyValue', 'col2val', 'col3val')
and then you try and do the same this will fail because of the primary key constraint.
you should be doing
update table set col1 = 'newValue', col2 = 'newValue2' where 'primaryKeyValue' = 'primaryKeyValue'
Use update query :
update tablename set column1 = 'new1', column2 = 'new2', 'primaryKey' = 'newPKValue' where 'primaryKey' = 'PKValue';
just one thing to remember 'newPKValue' should not be duplicated.
I am not talking about the id of the element in the list, but the id of it from a table if the value is in another table. E.g I have a FullName and an ID in one table and I have the same ID and some other stuff in another table(one to zero or one relationship). I have bound the FullName to a dropdown list control,but when saving,I need to refer to it's ID from the table, not the string value.
If you place something in a drop down list, you can place the ID in the value field and something else as a text, for example :
ddlCategorie.DataTextField = "Texte";
ddlCategorie.DataValueField = "ID_GLOBAL";
ddlCategorie.DataSource = db.GLOBAL.Where(t => t.DATE_FIN > dt).OrderByDescending(t => t.ID_GLOBAL).ToList();
ddlCategorie.DataBind();
As you see, I already placed the reel database value "ID" of the object inside the value field of the drop down list. So I can immediately retrieve is ID by doing :
int i = Convert.ToInt32(ddlCategorie.SelectedValue);
i'm have collection in my client C# app which consist of the 5 columns. In the DB I have created a User defined table type (UDTT) of six columns. the first column is an autom increment which I will be using to fetch each row stored in it. table structure is pSelCRSInfo
while(#cntr <= #pProgDuration)
begin
select
#CRSID=CRSID,
#YearNo=YearNo,
#IsCompulsory=IsCompulsory,
#EntDT=EntDT,
#EmpID=EmpID
from #pSelCRSInfo where CntrNo=#cntr
insert into DefaultCourses
(PRGID,CRSID,YearNo,IsCompulsory,EntDT,EmpID)
values (#newPRGID,#CRSID,#YearNo,#IsCompulsory,#EntDT,#EmpID)
set #cntr = #cntr + 1
end
in it's definition I have added
create type dbo.SelectedCourses
as table
(
CntrNo int not null IDENTITY(1, 1),
CRSID int,
YearNo int,
IsCompulsory varchar(20),
EntDT datetime,
EmpID int
);
go
At the moment i'm struggling with how to pass 5 column collection to a collection that requires 6 column?
Typically do people create UDTT structures with auto increment columns to accept collections? because I could generate the numbers generated by the auto increment column from the client then send that with the collection
thanks
I would say it depends entirely on you. If auto-incremented values suit your purpose then definitely use them. I'd do it.
so assume I have created a UDTT with a auto increment column, then still I should pass 6 column collection like the one below and leave the first column empty
//leave the first column empty
acd.dtCourses.Rows[CollCounter][0] = string.Empty
acd.dtCourses.Rows[CollCounter][1] = dt.Rows[i][2];
acd.dtCourses.Rows[CollCounter][2] = dt.Rows[i][3];
acd.dtCourses.Rows[CollCounter][3] = dt.Rows[i][4];
acd.dtCourses.Rows[CollCounter][4] = DateTime.Today;
acd.dtCourses.Rows[CollCounter][5] = myAcedemics.EmpID;
dtCourses is a datatable to accept the collection and my acd is an object of the business class Academics