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

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.

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.

EF Core with Lazy Loading tracks unreachable Objects?

I am currently having troubles with entity framework core.
The application I am developing is supposed to help users plan their next business year by increasing/decreasing the quantity of a service they want to provide in the next year.
Based on their input the "worth" of a service is distributed pro rata to other "mini-services" that are contained in the changed service.
To do so I load the affected entries of the main service and the "mini-services" from Database via a repository which then uses Entity Framework.
public IEnumerable<OpsDistributionEntry> FilteredOpsDistributionEntries(int catalogId, IEnumerable<OpsDistributionEntry> filterEntries)
{
return _context.OpsDistributionEntries.FromSqlRaw(
$"SELECT * FROM OpsDistributionEntries WHERE (Id IN (SELECT OpsEntriesId FROM DistributionCatalogOpsDistributionEntry WHERE CatalogsId = {catalogId}) " +
$"AND EntityId IN ({string.Join(",", filterEntries.Select(x => x.EntityId))}))").ToList();
}
I then map those database objects to my domain objects via constructor.
var opsDistributionEntries = new OpsDistributionEntriesFromDatabaseObjects(
_repository.FilteredOpsDistributionEntries(_distCatalogId, _filterEntries));
public class OpsDistributionEntriesFromDatabaseObjects : IOpsDistributionEntries
{
private readonly IOpsDistributionEntries _distribution;
public OpsDistributionEntriesFromDatabaseObjects(IEnumerable<DatabaseObjects.OpsDistributionEntry> distribution)
{
_distribution = new OpsDistributionEntries(distribution.Select(x => new OpsDistributionEntryFromDatabaseObject(x)));
}
public IEnumerator<IOpsDistributionEntry> GetEnumerator()
{
return _distribution.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
public class OpsDistributionEntryFromDatabaseObject : IOpsDistributionEntry
{
public OpsDistributionEntryFromDatabaseObject(DatabaseObjects.OpsDistributionEntry opsDistributionEntry)
: this(opsDistributionEntry.Id, opsDistributionEntry.TotalCases, opsDistributionEntry.TotalEffectiveWeight, opsDistributionEntry.Provide,
opsDistributionEntry.Freeze,
new OpsFromDatabaseObject(opsDistributionEntry.Entity),
new DrgDistributionsFromDatabaseObjects(opsDistributionEntry.DrgDistribution))
{
}
private OpsDistributionEntryFromDatabaseObject(int id, int totalCases, double totalEffectiveWeight, bool provide, bool freeze, IOps ops,
IDrgDistributions drgDistribution)
{
Id = id;
TotalCases = totalCases;
TotalEffectiveWeight = totalEffectiveWeight;
Provide = provide;
Freeze = freeze;
Ops = ops;
DrgDistribution = drgDistribution;
}
public int Id { get; }
public int TotalCases { get; }
public double TotalEffectiveWeight { get; }
public bool Provide { get; }
public bool Freeze { get; }
public IOps Ops { get; }
public IDrgDistributions DrgDistribution { get; }
}
public sealed class OpsFromDatabaseObject : IOps
{
public OpsFromDatabaseObject(DatabaseObjects.Ops ops) : this(ops.Id, ops.Code, ops.Description, ops.Year)
{
}
private OpsFromDatabaseObject(int id, string code, string description, int year)
{
Id = id;
Code = code;
Description = description;
Year = year;
}
public int Id { get; }
public string Code { get; }
public string Description { get; }
public int Year { get; }
}
I pass the database objects on to different levels, but finally every value is assigned and every possible navigation property is mapped to an domain object.
With those mapped domain objects I recalculate the new "worth" of the service and the correlated "mini-services".
After calculation I again map my Domain Objects to DatabaseObjects.
DatabaseObjects.OpsDistributionEntry ToDatabaseObject() => new DatabaseObjects.OpsDistributionEntry
{
Id = Id,
EntityId = Ops.Id,
Freeze = Freeze,
Provide = Provide,
TotalCases = TotalCases,
TotalEffectiveWeight = TotalEffectiveWeight,
DrgDistribution = DrgDistribution.Select(x => x.ToDatabaseObject()).ToImmutableList(),
};
When I want to add those "updated" Objects to the context via repository
public void UpdateDistributionEntries(IEnumerable<OpsDistributionEntry> opsDistributionEntries)
{
if (opsDistributionEntries == null) throw new ArgumentNullException(nameof(opsDistributionEntries));
_context.OpsDistributionEntries.UpdateRange(opsDistributionEntries);
}
I am getting an Error that the Entities I want to updated are already being tracked by Entity Framework.
After some debugging I think that EF is still tracking the database objects I loaded for mapping the domain objects. I just use the database objects to map values to the domain objects and do not store any reference for them (as far as I understand).
Can any of you maybe tell me why they are still being tracked even if they are "unreachable". Or am I thinking wrong? Might this be because of Lazy Loading?
I've been debugging for almost 14 hours now :D Please someone give me a hint :D
Many thanks in advance

Mapping SQL View in EF Core 5 - SaveChanges

I'm trying to add a view as a Navigation Property of an entity.
public class Schedule
{
public int Id { get; set; }
public decimal ScheduledQuantity { get; set; }
public ScheduleDetails ScheduleDetails { get; set; }
}
public class ScheduleDetails
{
public int ScheduleId { get; set; }
public decimal BadQuantity { get; set; }
public Schedule Schedule { get; set; }
}
with mappings:
public class ScheduleDetailMap : IEntityTypeConfiguration<ScheduleDetails>
{
public void Configure(EntityTypeBuilder<ScheduleDetails> builder)
{
builder.ToView("vwScheduleDetails", "ShopOrders");
builder.HasKey(t => t.ScheduleId);
builder.HasOne(p => p.Schedule).WithOne(s => s.ScheduleDetails);
}
}
public class ScheduleMap : IEntityTypeConfiguration<Schedule>
{
public void Configure(EntityTypeBuilder<Schedule> builder)
{
builder.ToTable("Schedules");
builder.HasKey(t => t.Id);
builder.Property(t => t.Id).UseIdentityColumn();
}
}
when I query it works fine. However if I add a new Schedule record.
var schedule = new Schedule
{
ScheduledQuantity = 100,
ScheduleDetails = new ScheduleDetails()
};
context.Schedules.Add(schedule);
context.SaveChanges();
I get an exception saying " The entity type 'ScheduleDetails' is not mapped to a table, therefore the entities cannot be persisted to the database. Use 'ToTable' in 'OnModelCreating' to map it."
Is there anyway to get EF to ignore saving this 'entity'?
This is kind of an old question, but for anyone having similar issues - in my case the problem lied in navigation properties in my view. I had some leftover properties in view's class, because its code was copied from other entity. By removing those properties, the error was gone.
This doesn't really help if you want to use navigation properties in your code, but it may help someone to continue their search.

Auto Mapper Constructor initialization Mapping Issue

I have the following Mapping configurations:-
Initialized Data:-
private static IEnumerable<Source> InitializeData()
{
var source= new[]
{
new Source("John", "Doe", "1111111111"),
new Source("Jack", "Handsome", "2222222222"),
new Source("Joe", "Mackenze", "3333333333")
};
return source;
}
Source Model:
public class Source
{
private string First { get; set; }
private string Last { get; set; }
private string Phone { get; set; }
public Source(string first, string last, string phone)
{
First = first;
Last = last;
Phone = phone;
}
}
Destination Model
public class Destination
{
public string First { get; set; }
public string Last { get; set; }
public string Phone { get; set; }
}
Main
static void Main(string[] args)
{
var config = new MapperConfiguration(cfg =>
{
cfg.AllowNullCollections = true;
cfg.CreateMap<Source, Destination>().ReverseMap();
});
var mapper = new Mapper(config);
var source= InitializeData();
var people = mapper.Map<IEnumerable<Destination>>(source);
foreach (var p in people)
{
Console.WriteLine("Name: {0}-{1} Phone: {2}", p.First, p.Last, p.Phone);
}
Console.ReadLine();
}
Problem descriptions:
I have been struggled to understand the AutoMapper mapping between source and destination models.
My source model has a constructor to initialize or accept data from outside. It works fine when I removed the source constructor from the model that's mean flat mapping works fine but constructor initialization has the issue. When I debug in VS2019, it shows the number of records but all fields are empty/null.
What is wrong with the above mapping. I have gone through the AutoMapper reference docs but do not get a hold on this issue.
I highly appreciate your help!
Try calling AssertConfigurationIsValid. Check http://docs.automapper.org/en/latest/Configuration-validation.html.
Your Source properties are private. I assume you meant public.

How do you set some properties and leave others as defaults using AutoFixture and AutoMoqCustomization?

I am new to AutoFixture so I hope you can help. How do you set some properties in an object but leave others as the AutoFixture default - while using XUnit's [Theory] attribute and an AutoDataAttribute.
For example, in the contrived Airport example below based on Jason Robert's Pluralsight course, when setting the property (or the Airport object) e.g.
f.Customize<Mock<IAirport>>(c => c.Do(m => m.SetupGet(i => i.code).Returns("NOO")));
the other properties are often null, or I have to manually set them rather than letting AutoFixture do it. I would prefer to have cleaner code where the fixtureFactory sets all the properties for the Airport so that the V2 unit test only passed in a single Airport parameter.
So, within the fixtureFactory
How do you set MULTIPLE properties?
How does one use the default AutoFixture values rather than leaving the uninitialized values as
null?
Thanks!
using AutoFixture;
using AutoFixture.AutoMoq;
using AutoFixture.Xunit2;
using Moq;
using System;
using Xunit;
namespace AirportTesterWithAutoFixture
{
public interface IAirport
{
string city { get; set; }
string code { get; set; }
string country { get; set; }
string name { get; set; }
void CallAirTrafficControl();
}
public class Airport : IAirport
{
public string name { get; set; }
public string city { get; set; }
public string code { get; set; }
public string country { get; set; }
public Airport()
{
}
public Airport(string name, string code, string country, string city)
{
this.name = name;
this.code = code;
this.country = country;
this.city = city;
}
public void CallAirTrafficControl()
{
if (this.country.Equals("Canada") && this.code.StartsWith("Y"))
{
// Send "Bonjour!"();
}
else
{
throw new Exception("Invalid code for Canada");
}
}
}
public class UnitTest1
{
[Fact]
public void V1_Validate_ExceptionThrown_ForInvalidCanadianAirportCode()
{
var fixture = new Fixture();
var sut = fixture.Create<Airport>();
// Overwrite code and country with invalid setting for Canada.
sut.country = "Canada";
sut.code = "NOT";
Assert.ThrowsAny<Exception>(() => sut.CallAirTrafficControl());
}
[Theory]
[AutoMoqInvalidAirportDataAttribute]
public void V2_Validate_ExceptionThrown_ForInvalidCanadianAirportCode(IAirport sut, string name, string city)
{
Airport airport = new Airport(name, sut.code, sut.country, city);
Assert.ThrowsAny<Exception>(() => airport.CallAirTrafficControl());
}
}
// https://stackoverflow.com/questions/58998834/how-to-use-ifixture-buildt-with-automoqcustomization-when-t-is-an-interface
public class AutoMoqInvalidAirportDataAttribute : AutoDataAttribute
{
public static Func<IFixture> fixtureFactory = () =>
{
IFixture f = new Fixture().Customize(new AutoMoqCustomization());
f.RepeatCount = 5;
// How do you set MULTIPLE properties?
// How does one use the default AutoFixture values rather than leaving the uninitialized values as null?
// Can one pass a custom property used earlier in the Fixture creation process to another custom property used later?
f.Customize<Mock<IAirport>>(c => c.Do(m => m.SetupGet(i => i.code).Returns("NOT")));
return f;
};
public AutoMoqInvalidAirportDataAttribute() : base(fixtureFactory)
{
}
}
}
AutoFixture does not populate mock properties by default, but it can be done. These blog posts describe how to do it:
https://blog.ploeh.dk/2013/04/05/how-to-configure-automoq-to-set-up-all-properties/
https://blog.ploeh.dk/2013/04/08/how-to-automatically-populate-properties-with-automoq/
Author of AutoFixture does not recommend this approach, however, as he considers declaration of properties in interfaces a design smell.
I could not find the original discussion about this topic unfortunately, but it is hidden somewhere on StackOverflow in the comments. Maybe you will be able to find it if you go through Mark Seemann's profile.

Resources