I'm trying to add several entities to the db but I get this error:
"Saving or accepting changes failed because more than one entity of type 'Schema.Domain.DataModels.ActivitySummery' have the same primary key value. Ensure that explicitly set primary key values are unique. Ensure that database-generated primary keys are configured correctly in the database and in the Entity Framework model. Use the Entity Designer for Database First/Model First configuration. Use the 'HasDatabaseGeneratedOption" fluent API or 'DatabaseGeneratedAttribute' for Code First configuration."
public void CreateNewGeneratedSchema(List<WeekDayViewModel> weekDays, int userId)
{
List<ActivitySummery> savedActivities = _schemaRepository.GetAllActivitySummeries(userId).ToList();
foreach(ActivitySummery activitySummery in savedActivities)
{
_schemaRepository.DeleteActivitySummery(activitySummery.ActivitySummeryId);
}
foreach (WeekDayViewModel weekDay in weekDays)
{
foreach (ActivitySummeryViewModel activitySummeryViewModel in weekDay.ActivitiySummeries)
{
try
{
ActivitySummery activitySummery = new ActivitySummery()
{
ActivityId = activitySummeryViewModel.ActivityId,
WeekDayId = activitySummeryViewModel.WeekDayId,
//Change userId
UserId = 1,
StartTime = activitySummeryViewModel.StartTime,
EndTime = activitySummeryViewModel.EndTime,
ActivityDescription = activitySummeryViewModel.Description,
Activity = _schemaRepository.GetSpecificActivity(activitySummeryViewModel.ActivityId),
WeekDay = _schemaRepository.GetSpecificWeekDay(activitySummeryViewModel.WeekDayId)
};
_schemaRepository.CreateActivitySummery(activitySummery);
}
catch (Exception)
{
throw new Exception("Something went wrong when trying to save the activity summery");
}
}
}
_schemaRepository.Save();
_schemaRepository.Dispose();
}
I know one solution that might work and it's to save and dispose the _schemaRepository after every time i added one model to the db and create a new instace of _schemaRepository. But im not sure if that is the right way. I know that every model that im trying to save has the pk of 0 and i think that might be the problem. However it still works and the db accepts the new entities but I still get the exception.
If your model (which you didn't show) has the primary key as ActivityId (which you also didn't indicate) and it is setup to auto-generate the primary key:
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ActivityId { get; set; }
Then you must not include the primary key in the CreateActivitySummery method (also which you haven't provided).
// Remove this line...
// ActivityId = activitySummary.ActivityId;
Do note that a (reasonable) alternative to having the database generate the primary key automatically is to use Guid/UniqueIdentifier without using DatabaseGenerated. Then your application code can use Guid.NewGuid to generate new (presumably unique) primary keys that it can insert with no problem and track related entities easily.
Related
I posted the question earlier, but didn't receive any correct responses, hence posting again with some edits. I have a function that accepts two parameters, IDs and Dates. When I had put breakpoints, I was able to see the Ids and the Dates selected on the page as parameter values. However, after hitting the process button, nothing happens, meaning this data isn't getting saved to the DB.
Model Classes:
public class Hello{
public string ID{ get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
public DateTime? Date{ get; set; }
}
Controller Class:
[HttpGet]
public ActionResult Selection(string ids, string dates)
{
model = new Hello();
ExtensionDB db = new ExtensionDB();
string[] IDS = ids.Split(',');
string[] DATES = dates.Split(',');
List<Hello> list = new List<Hello>();
for (int i = 0; i < IDS.Length; i++)
{
if (IDS[i] != null && IDS[i] != "")
{
Hello item = new Hello { ID = IDS[i], Date = DateTime.Parse(DATES[i]) };
list.Add(item);
}
}
if (ModelState.IsValid)
{
foreach (var row in db.Table1)
{
foreach (var row2 in db.Table2)
{
if (row.UID== row2.CID) // UID and CID are Foreign keys that join these two tables
{
foreach (var item in list)
{
if (row.UID == Convert.ToInt32(item.ID))
{
row2.ReportedDate = item.Date;
}
db.SaveChanges();
}
}
}
}
ViewBag.Message = "Success";
return View(model);
}
else
{
ViewBag.Message = "Failed";
return View(model);
}
}
I will add the view class if needed, however the problem is here.. You can also refer to it here: Saving changes to the DB MVC
Your code does not attempt to update anything. Start with confirming what the data you are passing to this POST call contains, and what you want to do with it. It looks like what you are trying to do is update the dates for a number of records. Looking at your previous post (no need to re-post another question with the same code) there are a few things..
First: Structure the data you want to pass to the POST call into a collection of simple objects containing an id and a date. For example:
{
id = rid,
date = date
}
and add those to the collection named something like "updateData" rather than two separate arrays of IDs and dates. Then in the server-side code, declare a simple view model class:
public class UpdateDateViewModel
{
public int Id { get; set; }
public DateTime Date { get; set; }
}
In the ajax call instead of:
data: { ids: ids, dates: dates },
you'll want something like:
data: { updates: updateData },
where updateData is your collection of id + date pairs.
and use that view model in your method:
public ActionResult Process(IList updates)
Provided that request data is sent as Json, ASP.Net should translate that data automatically for you, though you may need to configure ASP.Net to translate the camelCase vs PascalCase. Worst case, to test, you can use camelCase property names ("id" and "date")
Now when it comes to updating the data: Server side, please get in the habit of using meaningful variable names, not "c", "i", etc. It makes code a lot easier to understand.
public ActionResult Process(IList<UpdateDateViewModel> updates)
{
using (db = new DB())
{
//rp = new RequestProcess(); - Assuming RequestProcess is an Entity?
//var c = rp.getStuff(); - No idea what this getStuff() method does...
foreach(var update in updates)
{
var request = db.RequestProcesses.Find(update.Id);
if (request != null)
request.RequestDate = update.Date; // If we find a matching request, update it's date.
else
{ // Doesn't exist, create it and add it to the DbSet.(table)
var request = new RequestProcess { Id = update.Id, RequestDate = update.Date };
db.RequestProcesses.Add(request);
}
db.SaveChanges();
}
}
}
Now this is a very bare bones guess at what you may be trying to do. Ideally though, updates should be completely separate from adds in the sense that an update should only deal with existing records. If it comes across an ID that it cannot find it should throw an error, ignore, and/or return a status to the user that something wasn't right. Creating new entries should be a separate call and ensure that records are properly initialized with their required fields.
Your original code looked to be taking a list of IDs, but then creating a new entity and calling that "getStuff" method that didn't have the DbContext, or any of the values from the POST call, but then attempting to copy values from that entity into the string parameters that you passed (which would overwrite the Json string) None of that would have updated an entity which would never have updated your data.
Take it slow and follow the examples before attempting to adapt them to your ideas. It will be a lot more constructive and less frustrating then writing a bunch of code that doesn't really make much sense, then wondering why it doesn't work. Your original code has probably a dozen or more problems and inefficiencies. Simply pasting it up on Stack will get a lot of confusing comments based on these problems which don't really help with the first issue you want to solve. Strip it back to the minimum, start with getting the data you need to the server in a meaningful way, then from that, attempt to use that data to update your entities.
I want to save CreatedBy and LastModifiedBy field on every table. Is there base resulution on serenity?
I am getting error below when i set fld field:
Severity Code Description Project File Line Suppression State
Error CS0029 Cannot implicitly convert type 'int' to 'Serenity.Data.Int32Field'
private static MyRow.RowFields fld { get { return MyRow.Fields; } }
protected override void SetInternalFields()
{
int userId = ((UserDefinition)Authorization.UserDefinition).UserId;
fld.LastModifiedBy = userId;
fld is a reference to your entity fields (metadata), not the entity instance itself.
In SaveHandler, this.Row references to created/updated entity with new values, while this.Old references entity with old values for update (kinda similar to a SQL trigger).
So you should write Row.LastModifiedBy = userId;
FYI, instead of doing it this way in every repository, implement IUpdateLogRow (and/or InsertLogRow) interfaces in your entity and default save behaviors will fill Insert/Update UserId/Date fields automatically.
Define a base row like LoggingRow sample in Serene to avoid having to implement this interface in every entity.
I am writing an ASP.NET MVC (C#, SQL Server) web site and would like to write a function in my controller in which I pass a table name, a column name, and a value to check against the passed in column name in the passed in table.
If the record is found, I want to return the primary key of the record.
If the value is not found in the table, I want to create it and then return the primary key of that record.
private int FindRecord(string tableName, string FieldToCheck, string LookupValue)
{
/* 1) How do I set the string 'tableName' to a table in my context */
/* How do I set the string FieldToCheck to a field in the 'tableName' */
/* How do I return the primary key if LookupValue is in 'FieldToCheck' */
/* How do I add a record and return primary key if it is now */
return PrimaryKeyOfFoundOrNewRecord;
}
I almost cannot imagine that this question is not a duplicate but I cannot find any examples - perhaps I'm not using the proper terms to search. Thank you in advance.
OK, based on the comment clarification of what is required, then what you might want is something like this:
void CreateLocation(string city,string state, string country)
{
var newLoc = new Location();
newLoc.City = GetCity(city);
if (newLoc.City == null)
{
newLoc.City = new City() { Name = city });
}
//Repeaat this for state and country.
_context.Locations.Add(newLoc);
_context.SaveChanges();
}
City GetCity(string city)
{
var city = from c in _context.Cities
where c.name.Equals(city)
select c;
return city.FirstOrDefault();
}
Apologies if the syntax is a little off, nearly quitting time here.
Sorry, some of this syntax might be entity framework as opposed to linq-to-sql - just noticed your tag. Syntax is different (marginally) but idea is the same.
I am developing a Single Page App using Hot Towel Template, using breeze...and I have come across a peculiar problem, and I am unable to figure out the internal working which causes it...
I have a Programmes table, and the Programmes table has a foreign key to Responses, so the structure of Programmes is:
Id, ResponseID, Name and Date
and the Page has Name and Date, the foreign comes from RouteData.
and for one ResponseId in Programmes table, I want to save only on Programme.
So, when user comes to this page, it check the Programmes table that if it has an Entry for that particular Response Id, if yes, it goes in Edit case and if not it goes to Add a new entry case.
To achieve this, what I am doing is below:
var objTempProgramme = ko.observable();
var objProgramme = ko.observable();
function activate(routeData) {
responseId = parseInt(routeData.responseId);
// Create a Programme Entity
objProgramme(datacontext.createProgramme());
// Fill in a Temporary Observable with Programmes data
datacontext.getEntitiesDetailsByRelativeId('responseID', responseId , 'Programmes', objTempProgramme, true).then(function(){
// Check if Programmes Exists
if (objTempProgramme() != null && objTempProgramme() != undefined) {
// What I am doing here is filling my Programmes Entity with data coming from database (if it is there)
objProgramme(objTempProgramme());
} else {
// The Else Part assigns the Foreign Key (ResponseId) to my Entity Created above
objProgramme().responseID(responseId);
}
});
}
In datacontext.js:
var createProgramme = function () {
return manager.createEntity(entityNames.programme);
}
var getEntitiesDetailsByRelativeId = function (relativeIdName, relativeId, lookupEntity, observable, forceRefresh) {
var query = entityQuery.from(lookupEntity).where(relativeIdName, "==", relativeId);
return executeGetQuery(query, observable, forceRefresh);
};
Now when I call manager.saveChanes on my page, I would Expect objProgramme to be saved, in any case, be it edit or be it save,
but what breeze is doing here is that though it is filling objTempProgramme in objProgramme, but it is also leaving the entity objProgramme unreferenced with its manager, so that when I call save, it tries to save that entity too (2 entities in total, objProramme and one unreferenced one), but that entity does not have foreign key set and it fails..but my question is why?
Assigning one entity to another does not mean all its properties get assigned to another? And why is that unreferenced entity present?
Is there an equivalent of Rails ActiveRecord::Callbacks in ASP MVC?
http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html
I'm in a situation where we are not using identities for our primary key. We do this for reasons specific to our DB sharding design. Because of this we have a lookup table to find the next ID for a specific table. I'd like to automatically get this value and set it in an abstract class whenever a model is created/updated and before it is saved. I also need to update the lookup table with an incremented 'nextID' after the save is successful.
I'm open to other solutions on how to do this without callbacks as well.
So you need the callback just to increment ID in the lookup table? AFAIK there is no equivalent in ASP.NET, may be you could try with Async Controllers (http://msdn.microsoft.com/en-us/library/ee728598%28v=vs.100%29.aspx) and wait for a state change from the successful save, but I would prefer use a service specifically for this like Snowflake (https://github.com/twitter/snowflake/).
I found a solution using overrides as opposed to callbacks. It's my hope that ASP mvc adds support for callbacks as the framework continues to mature because callbacks allow for cleaner code by allowing the OnSave event to exist in the model[s] that the event is concerned with rather than the centralized DbContext class (separation of concerns).
Solution:
The SaveChanges method can be overridden in the Context Class (Entity Framework Power Tools creates the Context class is the 'Models' directory).
public override int SaveChanges()
{
// create a cache for id values in case their are multiple added entries in the dbcontext for the same entitytype
Dictionary<string, UniqueID> idCache = new Dictionary<string, UniqueID>();
IEnumerable<DbEntityEntry> changes = this.ChangeTracker.Entries();
foreach (var entry in changes)
{
//check if this is a new row (do nothing if its only a row update because there is no id change)
if (entry.State == System.Data.EntityState.Added)
{
//determine the table name and ID field (by convention)
string tableName = entry.Entity.GetType().Name;
string idField = entry.Entity.GetType().Name + "ID";
UniqueID id = null;
//if we've already looked this up, then use the cache
if (idCache.ContainsKey(tableName))
{
id = idCache[tableName];
}
//if we havn't looked this up before get it and add it to the cache
else
{
id = this.UniqueIDs.Find(tableName, idField);
//if it doesn't already exist in the lookup table create a new row
if (id == null)
{
id = new UniqueID(tableName, idField, 1);
// since this is a new entry add it
this.UniqueIDs.Add(id);
}
else
{
// set the state to modified
this.Entry(id).State = System.Data.EntityState.Modified;
}
}
entry.CurrentValues[tableName + "ID"] = id.NextID;
id.NextID = id.NextID + 1;
}
}
return base.SaveChanges();
}