duplicate values in the index, primary key, or relationship - jet-ef-provider

Using JetEntityFrameworkProvider and an MS Access/Jet File
I am updating values of an Entity, and I am receiving the following error when I save changes.
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.
Here is my code
var pagesList = projectDB.Pages.OrderBy(x => x.PageNumber).ToList();
for(int i = 0; i < pagesList.Count(); i++)
{
pagesList[i].BookNumber = i+30;
}
foreach(var blah2 in pagesList.OrderBy(x => x.Id))
{
System.Console.WriteLine($" {{ID:{blah2.Id}, BN:{blah2.BookNumber}, PN:{blah2.PageNumber}}}");
}
projectDB.SaveChanges();
This is my code output, as you can see there is no duplicate index, or duplicate BookNumber (which is indexed and is unique)
{ID:2, BN:58, PN:5}
{ID:3, BN:59, PN:6}
{ID:6, BN:62, PN:11}
{ID:7, BN:65, PN:21}
{ID:20, BN:64, PN:13}
{ID:21, BN:66, PN:22}
{ID:25, BN:67, PN:23}
{ID:29, BN:68, PN:24}
{ID:78, BN:35, PN:1}
{ID:79, BN:45, PN:2}
{ID:108, BN:34, PN:1}
{ID:132, BN:73, PN:41}
{ID:177, BN:33, PN:1}
{ID:291, BN:74, PN:42}
{ID:318, BN:32, PN:1}
{ID:319, BN:42, PN:2}
{ID:320, BN:48, PN:3}
{ID:340, BN:31, PN:1}
{ID:341, BN:41, PN:2}
{ID:342, BN:50, PN:3}
{ID:343, BN:53, PN:4}
{ID:344, BN:55, PN:5}
{ID:345, BN:60, PN:6}
{ID:346, BN:61, PN:7}
{ID:452, BN:71, PN:32}
{ID:469, BN:63, PN:12}
{ID:510, BN:72, PN:39}
{ID:520, BN:43, PN:2}
{ID:524, BN:75, PN:43}
{ID:533, BN:70, PN:31}
{ID:539, BN:69, PN:25}
{ID:610, BN:30, PN:1}
{ID:611, BN:36, PN:1}
{ID:612, BN:46, PN:2}
{ID:613, BN:37, PN:1}
{ID:614, BN:38, PN:1}
{ID:615, BN:44, PN:2}
{ID:616, BN:51, PN:3}
{ID:617, BN:52, PN:4}
{ID:618, BN:56, PN:5}
{ID:619, BN:40, PN:1}
{ID:620, BN:39, PN:1}
{ID:621, BN:47, PN:2}
{ID:622, BN:49, PN:3}
{ID:623, BN:54, PN:4}
{ID:624, BN:57, PN:5}
ETA:
Model
[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; }
}

Without the model is hard to understand the problem but I think you have defined a unique index on BookNumber.
EF runs an UPDATE statement for each update (for each page).
At a certain point, on the DB (not in memory), there are 2 entities with the same BookNumber.
Also inside a transaction you could have the same problem so the solution is to update the books in two different steps
var pagesList = projectDB.Pages.OrderBy(x => x.PageNumber).ToList();
for (int i = 0; i < pagesList.Count; i++)
pagesList[i].BookNumber = null;
projectDB.SaveChanges();
for (int i = 0; i < pagesList.Count; i++)
pagesList[i].BookNumber = i + 30;
foreach (var blah2 in pagesList.OrderBy(x => x.Id))
Console.WriteLine("{{ID:{0}, BN:{1}, PN:{2}}}", blah2.Id, blah2.BookNumber, blah2.PageNumber);
projectDB.SaveChanges();

Related

.Net Core 6.0 Web API - How to implement postgresql database(eg: Product Table -> Description column) localization for English and French?

I am developing a Web API using Core 6.0 with localization. Localization should be supported for both static (e.g., basic strings like greeting) and dynamic content (e.g., Values of the Product Instance).
I have implemented the localization for static content using JsonStringLocalizerFactory as discussed in this article - https://christian-schou.dk/how-to-add-localization-in-asp-net-core-web-api/.
public class LocalizerController : ControllerBase
{
private readonly IStringLocalizer<LocalizerController> _stringLocalizer;
public LocalizerController(IStringLocalizer<LocalizerController> stringLocalizer)
{
_stringLocalizer = stringLocalizer;
}
[HttpGet]
public IActionResult Get()
{
var message = _stringLocalizer["hi"].ToString();
return Ok(message);
}
[HttpGet("{name}")]
public IActionResult Get(string name)
{
var message = string.Format(_stringLocalizer["welcome"], name);
return Ok(message);
}
[HttpGet("all")]
public IActionResult GetAll()
{
var message = _stringLocalizer.GetAllStrings();
return Ok(message);
}
}
Next, I would like to implement localization for dynamic content (e.g., Details of the Product which will be sent to the WEB API and stored in the postgresql database table).
A possible approach is to duplicate the postgresql database table for each language (English and French). Could there be a better approach to avoid duplicate data and additional manual work?
You can create language table for each multi-language entity.
Langugage model;
public class Language
{
public int Id { get; set; }
public string Name { get; set; }
public string IsoCode { get; set; }
}
Static language list;
public class Constant
{
public static List<Language> Languages { get; set; } = new()
{
new Language
{
Id = 1,
Name = "English(United States)",
IsoCode = "en-US"
},
new Language
{
Id = 2,
Name = "Turkish",
IsoCode = "tr-TR"
}
};
}
Entities;
public class Product
{
public int Id { get; set; }
public decimal Price { get; set; }
public virtual ICollection<ProductLang> ProductLangs { get; set; }
}
public class ProductLang
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
[ForeignKey("Products")]
public Guid ProductId { get; set; }
public virtual Product Product { get; set; }
public int LanguageId { get; set; }
}
You can change the LanguageId property name. If you want to store languages in database, you can create a Languages table and create a relationship with that table from entity language tables. This can reduce duplication.
After include the language table to the entity, you can write an extension method to easily get the requested language data.
public static string GetLang<TEntity>(this IEnumerable<TEntity> langs, Expression<Func<TEntity, string>> propertyExpression, int defaultLangId)
{
var languageIdPropName = nameof(ProductLang.LanguageId);
var requestedLangId = GetCurrentOrDefaultLanguageId(defaultLangId);
if (langs.IsNullOrEmpty())
return string.Empty;
var propName = GetPropertyName(propertyExpression);
TEntity requestedLang;
if (requestedLangId != defaultLangId)
requestedLang = langs.FirstOrDefault(lang => (int)lang.GetType()
.GetProperty(languageIdPropName)
.GetValue(lang) == requestedLangId)
?? langs.FirstOrDefault(lang => (int)lang.GetType()
.GetProperty(languageIdPropName)
.GetValue(lang) == defaultLangId);
else requestedLang = langs.FirstOrDefault(lang => (int)lang.GetType().GetProperty(languageIdPropName).GetValue(lang) == defaultLangId);
requestedLang ??= langs.FirstOrDefault();
return requestedLang.GetType().GetProperty(propName).GetValue(requestedLang, null)?.ToString();
static int GetCurrentOrDefaultLanguageId(int defaultLanguageId)
{
var culture = CultureInfo.CurrentCulture;
var currentLanguage = Constant.Languages.FirstOrDefault(i => i.IsoCode == culture.Name);
if (currentLanguage != null)
return currentLanguage.Id;
else
return defaultLanguageId;
}
static string GetPropertyName<T, TPropertyType>(Expression<Func<T, TPropertyType>> expression)
{
if (expression.Body is MemberExpression tempExpression)
{
return tempExpression.Member.Name;
}
else
{
var op = ((UnaryExpression)expression.Body).Operand;
return ((MemberExpression)op).Member.Name;
}
}
}
This extension method checks for 3 conditions;
If there is data in the requsted language, it returns this data,
If there is no data in the requsted language, it checks if there is data in the default language. If the data is available in the default language, it will return the data,
Returns the first available language data if there is no data in the default language
Usage;
var defaultLangId = 1;
Product someProduct = await _dbContext.Set<Product>().Include(i => i.ProductLangs).FirstOrDefaultAsync();
var productName = someProduct.ProductLangs.GetLang(i => i.Name, defaultLangId);
It is up to you to modify this extension method according to your own situation. I gave you an example scenario where languages are kept in a static list.

SQLite.NET PCL returning 0 in all instances of autoincrement primary key

I am totally not getting this, because I have used this library in Xamarin apps for several years.
I have this base class that contains properties common in all db items:
public class BaseItem
{
[PrimaryKey, AutoIncrement]
public int ID { get; set; } = 0; // SQLite ID
public long CreatedTimeSeconds { get; set; } = DateTime.Now.ToUnixTimeSeconds();
public long ModifiedTimeSeconds { get; set; } = DateTime.Now.ToUnixTimeSeconds();
}
Now, I derive from it:
[Table("CategoryTable")]
public class Category : BaseItem
{
public int CategoryTypeID { get; set; } = (int)CategoryType.Invalid;
public string Name { get; set; } = string.Empty;
public string Description { get; set; } = string.Empty;
}
Here's a simplified version of what I'm seeing:
public class DBWorld
{
ISQLiteService SQLite { get { return DependencyService.Get<ISQLiteService>(); } }
private readonly SQLiteConnection _conn;
public DBWorld()
{
_conn = SQLite.GetConnection("myapp.sqlite");
}
public void TestThis()
{
_conn.CreateTable<Category>();
var category = new Category();
category.Name = "This Should Work";
int recCount = connection.Insert(category);
// at this point recCount shows as 1, and category.ID shows as zero.
// I thought Insert was supposed to set the autoincrement primary key
// regardless, it should be set in the database, right? So...
var categoryList = connection.Query<Category>($"SELECT * FROM {DBConstants.CategoryTableName}");
// at this point categoryList[0] contains all the expected values, except ID = 0
}
}
I am obviously missing something, but for the life of me, I can't figure out what...
Like so many other bizarre things that happen in the Visual Studio Xamarin world, when I went back later, this worked the way all of us expect. I guess Visual Studio was just tired and needed to be restarted.

DateTime? to DateTime

I am stuck. I'm creating a database of authors and run to a problem. I need to save value Umrti (date of death) to database as null, but it always save that value as 01.01.0001. I tried few things and now my code looks like this:
public class AutorDetailModel
{
public int Id { get; set; }
[Required]
public string Jmeno { get; set; }
[Required]
public string Prijmeni { get; set; }
public string ProstredniJmeno { get; set; }
[DotvvmEnforceClientFormat]
public DateTime Narozeni { get; set; }
[DotvvmEnforceClientFormat]
public DateTime? Umrti { get; set; }
public string Bio { get; set; }
public string Narodnost { get; set; }
public byte Obrazek { get; set; }
}
And in service like this:
public async Task UpdateAutorAsync(AutorDetailModel autor)
{
using (var dbContext = CreateDbContext())
{
var entity = await dbContext.Autors.FirstOrDefaultAsync(s => s.Id == autor.Id);
entity.Jmeno = autor.Jmeno;
entity.Prijmeni = autor.Prijmeni;
entity.ProstredniJmeno = autor.ProstredniJmeno;
entity.Narozeni = autor.Narozeni;
entity.Umrti = autor.Umrti;
entity.Bio = autor.Bio;
entity.Narodnost = autor.Narodnost;
entity.Obrazek = autor.Obrazek;
await dbContext.SaveChangesAsync();
}
}
However, autor.Umrti still gives me this error:
Cannot implicitly convert type 'System.DateTime?' to 'System.DateTime'. An explicit conversion exists (are you missing a cast?)
I am really stuck and will appreciate any advice. Thanks
And sorry for my bad english :)
you must check Umrti is null or not
public async Task UpdateAutorAsync(AutorDetailModel autor)
{
using (var dbContext = CreateDbContext())
{
var entity = await dbContext.Autors.FirstOrDefaultAsync(s => s.Id == autor.Id);
entity.Jmeno = autor.Jmeno;
entity.Prijmeni = autor.Prijmeni;
entity.ProstredniJmeno = autor.ProstredniJmeno;
entity.Narozeni = autor.Narozeni;
if(autor.Umrti!=null)
entity.Umrti = autor.Umrti;
entity.Bio = autor.Bio;
entity.Narodnost = autor.Narodnost;
entity.Obrazek = autor.Obrazek;
await dbContext.SaveChangesAsync();
}
}
If you're using SQL Server (possibly other Database engines too) then you can't save a NULL value in a DateTime field.
You'll have to convert the NULL to a date, any date, to save it in the db. Typically SQL Server uses '1900-01-01' to represent a NULL date. In most cases that's fine as, unless you are working with historical data around the end of the 18th Century, it won't clash with your valid data. When reading from the db you can convert all dates with a value of '1900-01-01' to null in your code, and when writing to the db if the value in code is null convert it to '1900-01-01'.

Can I do an UPDATE on a JOIN query with OrmLite on ServiceStack?

I want to do an update for a specific field on a table based on the results from a query that includes a join. Using OrmLite with ServiceStack.
My Classes are as follows:
public class Document
{
public int Id { get; set; }
public string BCL_Code { get; set; }
public bool IsActive { get; set; }
public int DocumentTypeId { get; set; }
}
public class DocumentType
{
public int Id { get; set; }
public string TypeName { get; set; }
}
Trying to do the Update on the Join with the following code:
var q = db.From<Document>()
.Join<Document, DocumentType>(
(doc, type) => doc.DocumentTypeId == type.Id)
.Where(d => d.BCL_Code == "FR49")
.And<DocumentType>(t => t.TypeName == "Enrollment Process");
db.UpdateOnly(new Document { IsActive = false }, onlyFields: q);
I know I can update specific fields, and I know how to do joins, but when I try to include a join in the query, and then do an UpdateOnly, I get an error message on the db.UpdateOnly line:
The multi-part identifier "DocumentType.TypeName" could not be bound.
Is it possible to do an Update on a Join Query?
If so, what is the proper way to do it?
There's no Typed APIs for Update From Table in OrmLite yet, you can add a feature request for it.
In the meantime you can use Custom SQL, e.g:
db.ExecuteSql(#"UPDATE Document SET IsActive = #isActive
FROM Document d
INNER JOIN DocumentType t ON (d.DocumentTypeId = t.Id)
WHERE d.BCL_Code = 'FR49'
AND t.TypeName = 'Enrollment Process'",
new { isActive = false });

List <T> store large amounts of data , Not enough memory

public class ListKeywords
{
public int ID { set; get; }
public string Keyword { set; get; } //关键词
public string Language { set; get; } //语种
public int WordCount { set; get; } //单词数
public int WordLength { set; get; } // 字符数
public int Status { set; get; } //采集状态 0-未采集 1-采集成功 2-保存失败 3-保存成功 4-发布失败 5-发布成功
public bool Taken { set; get; }
public bool FTPStatus { set; get; }
public bool DBStatus { set; get; }
public string UrlName { set; get; }
public ListKeywords()
{
}
public ListKeywords(string keyword)
{
this.Keyword = keyword;
}
}
List<string> lines = new List<string>();
List<ListKeywords> keywordsList = new List<ListKeywords>();
using (StreamReader sr = File.OpenText(filePath))
{
string s = String.Empty;
while ((s = sr.ReadLine()) != null)
{
//lines.Add(s); //Operating normally
eywordsList.Add(new ListKeywords("some keywords")); // Operating normally
keywordsList.Add(new ListKeywords(s)); // it will be out of memeory
}
}
In text file, have 1,000,000 line data, if i use above code to load the large data to list< keywordsList >, it will raises an OutOfMemoryException, but if i load it to list< string >, it run normally. How to solved it ?
Instead of using a List maybe try using an IEnumerable w/ yield?
static IEnumerable<ListKeywords> Keywords()
{
using (StreamReader sr = File.OpenText(path))
{
string s = String.Empty;
while ((s = sr.ReadLine()) != null)
{
yield return new ListKeywords(s);
}
}
}
Note that Jon Skeet's C# in Depth offers a great explanation about this in Chapter 6. I imagine he also has some articles or posts on StackOverflow about this topic. As he points out, you want to be careful about modifying this method to pass in a StreamReader (or TextReader as is used in his example) as you would want to take ownership of the reader so it will be properly disposed of. Rather, you would want to pass in a Func<StreamReader> if you have such a need. Another interesting note he adds here - which I will point out because there are some edge cases where the reader will not actually be properly disposed of even if you don't allow the reader to be provided by the caller - it's possible for the caller to abuse the IEnumerable<ListKeywords> by doing something like Keywords().GetEnumerator() - this could result in a memory leak and could even potentially cause security issues if you have security-related code which relies on the using statement to clean up the resource.

Resources