I ask a similar question here, but this is a different situation that results in the same error message.
I am updating an NON-index, NON-unique property, PageNumber.
And I am receiving the following error
OleDbException: The changes you requested to the table were not successful because they would create duplicate values in the index, primary key, or relationship. Change the data in the field or fields that contain duplicate data, remove the index, or redefine the index to permit duplicate entries and try again.
public void DoRenumberPages(object blah)
{
var hiddenPages = projectDB.Pages.AsEnumerable().Where(x => !IsVisibleDrawing(x.DrawingType) && x.DrawingType != 3001).ToList();
for (int i = 0; i < hiddenPages.Count(); i++)
{
hiddenPages[i].PageNumber = i + 1000;
}
var TOCPages = projectDB.Pages.AsEnumerable().Where(x => x.DrawingType == 3001).OrderBy(x => x.BookNumber).ToList();
for (int i = 0; i < TOCPages.Count(); i++)
{
TOCPages[i].PageNumber = i + 1;
}
var visiblePagesNotTOC = projectDB.Pages.AsEnumerable().Where(x => IsVisibleDrawing(x.DrawingType) && x.DrawingType != 3001).OrderBy(x => x.BookNumber).ToList();
for (int i = 0; i < visiblePagesNotTOC.Count(); i++)
{
visiblePagesNotTOC[i].PageNumber = i + TOCPages.Count() + 1;
}
projectDB.SaveChanges();
RenumberPages.EnableExecute();
}
Page Model Class
[Table("Content")]
public class Page
{
//** Primary Key
[Column("Counter")]
public int Id { get; set; }
public int ProjectCounter { get; set; }
public short Version { get; set; }
public short Revision { get; set; }
public bool Locked { get; set; }
public int DrawingType { get; set; }
//** Forign Key?
public int DeviceLocationCounter { get; set; }
//** Forign Key?
public int FolderID { get; set; }
[Column("Page")]
public int PageNumber { get; set; }
//** Indexed, Unique
public int BookNumber { get; set; }
public string PageIndex { get; set; }
//** Product
//** DrawingObject is not here
public bool Update { get; set; }
public short Flag { get; set; }
}
ETA:
I have change public int BookNumber { get; set; } to public int? BookNumber { get; set; } This doesn't solve the issue.
Ok, I could start the project.
The problem is the one I wrote here duplicate values in the index, primary key, or relationship. If you enable query printing setting JetEntityFrameworkProvider.JetConnection.ShowSqlStatements = true; somewhere (I did it in your Test.Program.Main) you can see the statements that EF runs. The first update query is
update [Content]
set [Page] = #p0
where ([Counter] = #p1)
#p0 = 3
#p1 = 2
If you look at the database, the time you run the query the Page = 3 is already contained in the record with Counter = 3. You can't solve this problem also inside a transaction and also with other (next to all) DBMSs you have the same problem.
The only solution (if you need the unique index on Page) is to update in 2 different SaveChanges. For example:
1. Set Page = null then SaveChanges()
2. Set Page = number then SaveChanges()
Microsoft Access permits duplicated null values in unique indexes. If you will permit to use different databases you could have problems.
Related
I use EntityFramework SQLite and got stuck with weird bug. I searched for the similar problems, but no solution suits my case.
In my real project it does not work at all. (with the same exception) In the example I made when I execute code first time it works fine (when db is just created), when I run it again I got exception:
Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: 'Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.'
Minimum reproducible sample can be found here.
The code is rather trivial:
using (var ctx = new SqliteDbContext("data.db"))
{
ctx.Database.Migrate();
ctx.UserReferencesData.Add(new UserReferencesData()
{
ColumnId = Guid.NewGuid(),
MetadataId = -2,
RowId = 1,
ColumnType = 1,
StringValue = "asdf",
});
ctx.SaveChanges();
}
Model is also rather simple, but I suspect that it has to do with particular column or index, since other tables works fine:
[Table("UserReferencesData")]
[Index(nameof(RowId))]
public class UserReferencesData
{
[Key]
public int Id
{
get;
set;
}
public int MetadataId
{
get;
set;
}
public int RowId
{
get;
set;
}
public Guid ColumnId
{
get;
set;
}
public int ColumnType
{
get;
set;
}
//type 0
public DateTime? DateValue
{
get;
set;
}
//type 1
public string StringValue
{
get;
set;
}
//type 2
public int? IntValue
{
get;
set;
}
//type 3
public double? DoubleValue
{
get;
set;
}
//type 4
public bool? BoolValue
{
get;
set;
}
//type 5
public decimal? DecimalValue
{
get;
set;
}
}
dbcontext:
public class SqliteDbContext : DbContext
{
private string _dbPath = #"data.db";
public SqliteDbContext(string dbPath)
{
_dbPath = dbPath;
}
//ctor for migration
public SqliteDbContext(DbContextOptions<SqliteDbContext> options)
: base(options)
{
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
var path = $"Data Source=\"{_dbPath}\"";
optionsBuilder.UseSqlite(path);//
#if DEBUG
optionsBuilder.EnableSensitiveDataLogging(true);
optionsBuilder.LogTo(s=>System.Diagnostics.Debug.WriteLine(s));
#endif
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserReferencesData>()
.HasOne<UserReferencesMetadata>()
.WithMany()
.HasForeignKey(d => d.MetadataId);
}
public virtual DbSet<UserReferencesData> UserReferencesData
{
get;
set;
}
public virtual DbSet<UserReferencesMetadata> UserReferencesMetadata
{
get;
set;
}
}
When I execute insert query in SQLite db browser row is added. But second one returns 0 rows (I logged queries which are used by Entity Framework):
INSERT INTO "UserReferencesData" ("BoolValue", "ColumnId", "ColumnType", "DateValue", "DecimalValue", "DoubleValue", "IntValue", "MetadataId", "RowId", "StringValue")
VALUES (null, 'c6c653c5-3fd3-4c54-ae0d-d5565b3cb725', '1', null, null, null, null, '-2', '1', 'asdf');
SELECT "Id"
FROM "UserReferencesData"
WHERE changes() = 1 AND "rowid" = last_insert_rowid();
If I switch to different table and then execute second query again it then pops a list of ids.
The problem has to do with naming, in particular RowId.
public int RowId
{
get;
set;
}
changed it to RowKeyId and it now works fine.
I'm using EF6 with ASP.Net. I'm trying to add items to the Jobs list in the following model:
EDIT:
My goal is to save the changes I make to the Timecards.Jobs list through a PUT method in such a way that I can retrieve them through a GET method.
public class Timecard
{
[Key]
public long TimecardID { get; set; }
[Required]
public DateTime StartDate { get; set; }
[Required]
public DateTime EndDate { get; set; }
[Required]
public long EmployeesID { get; set; }
[Required]
public decimal Hours { get; set; }
[Required]
public ICollection<int> Jobs { get; set; } = new List<int>();
public List<DateTime> Days { get; set; } = new List<DateTime>();
}
And I believe i'm doing so, i'm checking the states change in my PUT method:
// PUT: api/TimecardsAPI/5
[ResponseType(typeof(void))]
public IHttpActionResult PutTimecard(int id, Job job)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
Timecard card = db.Timecards.Where(x => x.TimecardID == id).First();
var state = db.Entry(card).State;
db.Timecards.Attach(card);
state = db.Entry(card).State;
card.Jobs.Add((int)job.JobID);
db.Entry(card).State = EntityState.Modified;
state = db.Entry(card).State;
var result = db.SaveChanges();
state = db.Entry(card).State;
var change = db.Timecards.Where(x => x.TimecardID == id).First().Jobs;
}
catch (DbUpdateConcurrencyException)
{
if (!TimecardExists(id))
{
return NotFound();
}
else
{
throw;
}
}
return StatusCode(HttpStatusCode.NoContent);
}
Before returning from the put method, i have a var change to check the results of the Jobs list once i'm done working on it. Before I leave the put method, the changes to the Jobs list are accurate. However, when I do a get, I get all the correct data EXCEPT the list. It comes back as a 0 length list. Here's my get method, which also has the jobs list in a variable. This is where the list comes back as size 0:
// GET: api/TimecardsAPI
public IQueryable<Timecard> GetTimecards()
{
var change = db.Timecards.Where(x => x.TimecardID == 6).First().Jobs;
//In this example, six is the id of the timecard in question. Only hardcoded here
//for debugging.
return db.Timecards;
}
and my dbcontext:
public class ClockedWebContext : DbContext
{
public ClockedWebContext() : base("name=ClockedWebContext")
{
}
public DbSet<Job> Jobs { get; set; }
public System.Data.Entity.DbSet<ClockedWeb.Models.PayPeriod> PayPeriods { get; set; }
public System.Data.Entity.DbSet<ClockedWeb.Models.Employee> Employees { get; set; }
public System.Data.Entity.DbSet<ClockedWeb.Models.Timecard> Timecards { get; set; }
}
There are many similar questions on SO but I have not found information yet that has helped me solve my issue. I have no idea what I'm doing wrong, but I've lost days on this and I could really use some help. thank you.
Generally storing multiples values in column is an indication of poor database design. Relational databases are designed specifically to store one value per row/column combination. In order to store more than one value, you must serialize your list into a single value for storage, then deserialize it upon retrieval or you can use many-to-one relationship then you should use an extra table with a foreign key constraint. There is no other way to do so in RDMS.
If you use serialize approach, then your model look like--
public class Timecard
{
[Key]
public long TimecardID { get; set; }
[Required]
public DateTime StartDate { get; set; }
[Required]
public DateTime EndDate { get; set; }
[Required]
public long EmployeesID { get; set; }
[Required]
public decimal Hours { get; set; }
[NotMapped]
public List<int> JobList { get; set; } = new List<int>();
[Required]
public string Jobs
{
get => string.Join(",", JobList);
set
{
if (string.IsNullOrEmpty(value)) JobList = new List<int>();
else
{
JobList = !string.IsNullOrWhiteSpace(value) && value.Contains(",")
? value.Split(',').Select(s => Convert.ToInt32(s.Trim())).ToList()
: !string.IsNullOrWhiteSpace(value) && !value.Contains(",")
? new List<int>()
: new List<int>();
}
}
}
//have to change also
public List<DateTime> Days { get; set; } = new List<DateTime>();//Follow previous technique
}
Then you can do your operation as you doing. just it's insert data as a coma separated string.
I am not getting you correctly but if you not getting the update after you changed your entity then can you please add below line
db.savechanges();
I'm working at my first project in .Net Core 2.0. It is a simple blog system. I want to add search functionality based on post title and tags.
My entities:
public class Post
{
public int ID { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Body { get; set; }
public int CategoryID { get; set; }
public DateTime ReleaseDate { get; set; }
public string ImageName { get; set; }
public Category Category { get; set; }
public ICollection<PostTag> PostTags { get; } = new List<PostTag>();
}
public class PostTag
{
public int PostID { get; set; }
public int TagID { get; set; }
public Post Post { get; set; }
public Tag Tag { get; set; }
}
public class Tag
{
public int TagID { get; set; }
public string Name { get; set; }
public int Counter { get; set; }
public ICollection<PostTag> PostTags { get; } = new List<PostTag>();
public Tag()
{
Counter = 1;
}
So far I have come up with something like that. I joined the Tag table to be able to view all the tags for each post in IndexView.
public async Task<IActionResult> Index(int? page, string searchString)
{
IQueryable<Post> posts = _context.Posts
.OrderByDescending(post => post.ReleaseDate)
.Include(post => post.Category)
.Include(post => post.PostTags)
.ThenInclude(pt => pt.Tag);
//SEARCH
if (!String.IsNullOrEmpty(searchString))
{
posts = posts.Where(post => post.PostTags.Any(pt => pt.Tag.Name.Contains(searchString)) || post.Title.Contains(searchString));
//POPULARITY INCREESE
var tag = _context.Tags.SingleOrDefault(t => t.Name == searchString);
if (tag != null)
{
tag.Counter += 1;
_context.Update(tag);
_context.SaveChanges();
}
}
int pageSize = 4;
return View("Index", await PaginatedList<Post>.CreateAsync(posts.AsNoTracking(), page ?? 1, pageSize));
}
It's wroking but I would like to know if there is a simpler or better way.
And will .Where function work when i dont include related tables?
First of all the answer to your last question: As long as you didn´t send any request to the database, you don´t need Include() for filtering.
If you want to populate a list of entities, and you want to access Navigation-Properties e.g. while iterating over the list, you need to use Include().
If you want to avoid using Include(), you should select the values you need. This will avoid unexpected behaviour with NavigationProperties or something like this too. I would do something like this:
IQueryable<Post> posts = _context.Posts.OrderByDescending(post => post.ReleaseDate);
//SEARCH
if (!String.IsNullOrEmpty(searchString))
{
posts = posts.Where(post => post.PostTags.Any(pt => pt.Tag.Name.Contains(searchString)) || post.Title.Contains(searchString));
//POPULARITY INCREESE
var tag = _context.Tags.SingleOrDefault(t => t.Name == searchString);
if (tag != null)
{
tag.Counter += 1;
_context.Update(tag);
_context.SaveChanges();
}
}
int pageSize = 4;
return View("Index", await PaginatedList<Post>.CreateAsync(posts.AsNoTracking().Select(e=> new YourDisplayObject { DiplayValue = e.DbValue, DisplayNavProp = e.NavProp }, page ?? 1, pageSize));
I create a SQlite Table without primary key using Class as follow. But Each time I update, SQLite keeps generating record for each update. It should override the same record which it was first created but should not generate record for each update.
Thanks.
Not adding this to the class: [PrimaryKey, AutoIncrement]
class NetworkCredential
{
public string WebServiceName { get; set; }
public string WebServer { get; set; }
public int Port { get; set; }
public string Company { get; set; }
public int DefaultCredential { get; set; }
}
using (var db = new SQLite.SQLiteConnection(DBPath))
{
var User = db.Query("Select * From NetworkCredential").FirstOrDefault();
if (User != null)
{
User.WebServiceName = txtWebServer.Text.Trim();
User.WebServer = txtWebServer.Text.Trim();
User.Port = Convert.ToInt32(txtPort.Text);
int success = db.InsertOrReplace(User);
}
else
{
int success = db.Insert(new NavNetworkCredential()
{
WebServer = txtWebServer.Text.Trim(),
WebServiceName = txtWebServiceName.Text.Trim(),
Port = Convert.ToInt32(txtPort.Text)
});
}
this is the database table
[Table("Score")]
public class Score
{
[Key]
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int ScoreId { get; set; }
public int QuizId { get; set; }
public int PersonId { get; set; }
public string Answer { get; set; }
public virtual Quiz Quiz { get; set; }
}
i want to return a result of how many record exist on database with this code
var query = (from n in db.Scores
where n.PersonId == x && n.Quizid == y
select n).Count();
x and y have a value.
how i can make this code work with 2 conditions?
or any other way to make this work
What you've got should work. A more concise way of writing it would be:
db.Scores.Count(n => n.PersonId == x && n.QuizId == y);
The problem, in terms of compilation, is that Quizid does not exist, it's QuizId.