Problem using FluentNHibernate, SQLite and Enums - sqlite

I have a Sharp Architecture based app using Fluent NHibernate with Automapping. I have the following Enum:
public enum Topics
{
AdditionSubtraction = 1,
MultiplicationDivision = 2,
DecimalsFractions = 3
}
and the following Class:
public class Strategy : BaseEntity
{
public virtual string Name { get; set; }
public virtual Topics Topic { get; set; }
public virtual IList Items { get; set; }
}
If I create an instance of the class thusly:
Strategy s = new Strategy { Name = "Test", Topic = Topics.AdditionSubtraction };
it Saves correctly (thanks to this mapping convention:
public class EnumConvention : IPropertyConvention, IPropertyConventionAcceptance
{
public void Apply(FluentNHibernate.Conventions.Instances.IPropertyInstance instance)
{
instance.CustomType(instance.Property.PropertyType);
}
public void Accept(FluentNHibernate.Conventions.AcceptanceCriteria.IAcceptanceCriteria criteria)
{
criteria.Expect(x => x.Property.PropertyType.IsEnum);
}
}
However, upon retrieval (when SQLite is my db) I get an error regarding an attempt to convert Int64 to Topics.
This works fine in SQL Server.
Any ideas for a workaround?
Thanks.

Actually, it is possible to map enums to INT, but in SQLite, INT will come back as an Int64, and, since you can't specify that your enum is castable to long, you will get this error. I am using NHibernate, so my workaround was to create a custom AliasToBean class that handles converting the enum fields to Int32:
public class AliasToBeanWithEnums<T> : IResultTransformer where T : new()
{
#region IResultTransformer Members
public IList TransformList(IList collection)
{
return collection;
}
public object TransformTuple(object[] tuple, string[] aliases)
{
var t = new T();
Type type = typeof (T);
for (int i = 0; i < aliases.Length; i++)
{
string alias = aliases[i];
PropertyInfo prop = type.GetProperty(alias);
if (prop.PropertyType.IsEnum && tuple[i] is Int64)
{
prop.SetValue(t, Convert.ToInt32(tuple[i]), null);
continue;
}
prop.SetValue(t, tuple[i], null);
}
return t;
}
#endregion
}
Usage:
public IList<ItemDto> GetItemSummaries()
{
ISession session = NHibernateSession.Current;
IQuery query = session.GetNamedQuery("GetItemSummaries")
.SetResultTransformer(new AliasToBeanWithEnums<ItemDto>());
return query.List<ItemDto>();
}

By default, emums are automapped to strings for SQLite (don't know what happens with SQL Server).
Less efficient storage wise, obviously, but that might be a non-issue unless you have really huge data sets.

Related

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

Aspnetboilerplate application gives runtime error while applying sort on a query for ISoftDelete entity

I want to write CRUD services for an entity named Location . Location has a composite primary key, so I couldn't use AsyncCrudAppService and copied the methods that I wanted from AsyncCrudAppService.
When I use GetAll Service without ApplySorting method, it works fine. But when I add sorting, I get this runtime error:
[35] ERROR BookingSystem.middlewares.HttpGlobalExceptionFilter [(null)] - The LINQ expression 'DbSet
.Where(l => __ef_filter__p_0 || !(((ISoftDelete)l).IsDeleted))
.OrderByDescending(l => l.Id)' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync(). S
ee https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
public class Location : IEntity<int>, IPassivable, IFullAudited<User> {
public int Id { get; set; }
public int IdLocation { get; set; } //primary key 0
public LocationType TypeId { get; set; } //primary key 1
public string Name { get; set; }
}
public interface ILocationService : IApplicationService
{
Task<PagedResultDto<LocationDto>> GetAllAsync(PagedLocationResultRequestDto input);
}
public class LocationService : AbpServiceBase, ILocationService, IPerWebRequestDependency
{
private readonly IRepository<Location, int> _repository;
private IAsyncQueryableExecuter AsyncQueryableExecuter { get; set; }
public LocationService(IRepository<Location> repository)
{
_repository = repository;
AsyncQueryableExecuter = NullAsyncQueryableExecuter.Instance;
}
public async Task<PagedResultDto<LocationDto>> GetAllAsync(PagedLocationResultRequestDto input)
{
if (input.MaxResultCount > _appSettings.Value.MaxResultCount)
{
throw new BookingSystemUserFriendlyException(BookingSystemExceptionCode.InputNotValid,
nameof(input.MaxResultCount));
}
var query = CreateFilteredQuery(input);
var totalCount = await AsyncQueryableExecuter.CountAsync(query);
query = ApplySorting(query, input);
query = ApplyPaging(query, input);
var entities = await AsyncQueryableExecuter.ToListAsync(query);
return new PagedResultDto<LocationDto>(
totalCount,
entities.Select(MapToEntityDto).ToList()
);
}
protected virtual IQueryable<Location> ApplySorting(
IQueryable<Location> query, PagedLocationResultRequestDto input)
{
if (input is ISortedResultRequest sortInput &&
!sortInput.Sorting.IsNullOrWhiteSpace())
{
return query.OrderBy(sortInput.Sorting);
}
return query.OrderByDescending(e => e.Id);
}
protected virtual IQueryable<Location> ApplyPaging(
IQueryable<Location> query, PagedLocationResultRequestDto input)
{
if (input is IPagedResultRequest pagedInput)
{
return query.PageBy(pagedInput);
}
return query;
}
private IQueryable<Location> CreateFilteredQuery(PagedLocationResultRequestDto input)
{
return _repository.GetAll()
.WhereIf(!string.IsNullOrWhiteSpace(input.Name),
location => location.Name.ToLower().Contains(input.Name.Trim().ToLower()));
}
private LocationDto MapToEntityDto(Location entity)
{
return ObjectMapper.Map<LocationDto>(entity);
}
}
Abp package version: 5.1.0
Base framework: .Net Core
Well, I asked the same question in the project's GitHub and got the answer.
In ApplySorting, the default sorting is based on Id, which doesn't exist in my database table.
If you are using composite PK, then you probably don't have the Id field in the database, right? Then you should not sort by Id.
https://github.com/aspnetboilerplate/aspnetboilerplate/issues/5274#issuecomment-583946065

Object value is null after deserialization (Xamarin with SQLite)

for an internship project I'm developping an app with Xamarin that will allow users to scan barcodes and create sheets of labels and purchases.
To prevent the scanned codes from being lost in case of crash etc, I've implemented SQLite to create a local backup that we could restore.
The structure is as follows : a ListOfLabelLines contains several LabelLine which each contain a Product and different other informations (such as packaging, quantity etc).
ListOfLabelLines.cs :
[Table("ListOfLabelLines")] // Indique le nom de la table qui sera générée par SQLite
public class ListOfLabelLines : BaseItem
{
private string _name { get; set; }
[TextBlob("LabelLinesBlob")]
public ObservableCollection<LabelLine> lines { get; set; }
[TextBlob("ListBlob")]
public List<String> TestList { get; set; }
public string LabelLinesBlob { get; set; } // serialized LabelLines
public string ListBlob { get; set; } // serialized TestList
public ListOfLabelLines()
{
}
public ListOfLabelLines(string name)
{
this._name = name;
lines = new ObservableCollection<LabelLine>();
TestList = new List<String>();
TestList.Add("Test1");
TestList.Add("Test2");
}
public string Name
{
get { return _name; }
set
{
_name = value;
}
}
}
}
These objects ListOfLabelLines contain an ObservableCollection<LabelLine> which I'm serializing by using the TextBlob property from SQLite-net-extensions.
However, when I retrieve the ListOfLabelLines I've stored, the ObservableCollection appears as null :
Example of null collections
Here are the methods I use to store the objects in SQlite :
public void SaveListOfLabelLines(ListOfLabelLines ShelfLabelInstance)
{
var query = from label in database.Table<ListOfLabelLines>()
where label.Name == ShelfLabelInstance.Name
select label;
var res = query.FirstOrDefault();
if (res != null)
{
database.UpdateWithChildren(ShelfLabelInstance);
Console.WriteLine("Label " + ShelfLabelInstance.Name + " updated");
}
else
{
database.InsertWithChildren(ShelfLabelInstance);
Console.WriteLine("Label " + ShelfLabelInstance.Name + " created");
}
}
and to retrieve them :
public void CheckProductsInLabelLine(string n)
{
var query = from LOLL in database.Table<ListOfLabelLines>()
where LOLL.Name == n
select LOLL;
ListOfLabelLines res = query.FirstOrDefault();
The string property linked to the TextBlob, however, contains the JSON object I need.
I thought the ObservableCollection would be obtainable when getting the object in DB since TextBlob is supposed to serialize AND deserialize.
Could anybody help ?
Thanks a lot !

iterating collections in .net

I have a class that has this code,
pCollection pub = RSSXmlDeserializer.GetPub(path, fReload);
get pub is the method that returns a collections of pub's...
How can i iterate them. I tried,
for (var n = 0; n < pub.Count; n++ ){
}
this is the getPub method
public static PCollection GetPub(string path, bool fReload)
{
HttpApplicationState session = HttpContext.Current.Application;
PCollection pub = session["PUB"] as PCollection;
if pub == null || fReload)
{
StreamReader reader = null;
try
{
XmlSerializer serializer = new XmlSerializer(typeof(PCollection));
reader = new StreamReader(path);
pub = (PCollection)serializer.Deserialize(reader);
session["PUB"] = pub;
}
catch (Exception ex)
{
//throw ex;
}
finally
{
reader.Close();
}
}
return pub;
}
}
[Serializable()]
public class Pub
{
[System.Xml.Serialization.XmlElement("title")]
public string Title { get; set; }
[System.Xml.Serialization.XmlElement("description")]
public string Description { get; set; }
[System.Xml.Serialization.XmlElement("imageUrl")]
public string ImageUrl { get; set; }
}
[Serializable()]
[System.Xml.Serialization.XmlRoot("RPublications")]
public class PCollection
{
[XmlArray("Pub")]
[XmlArrayItem("Pub", typeof(Pub))]
public Pub[] Pub { get; set; }
}
but 'Count' is not recognised. I get this message , pCollection does not have a definition for 'Count'...
How do i iterate the collection n get the collection elements? pls help.
You could use:
foreach(var p in pub.Pub)
{
// Do work on p
}
Note that your PCollection class is not following good .NET practices, as it's named "Collection" but not implementing any of the standard interfaces for collections. You may want to consider reworking this to be more "standardized".
PCollection is not really a collection. It is a class that contains a collection (more precisely an array). So to iterate you need to iterate the array:
for (Int32 i = 0; i < pub.Pub.Length; ++i) {
Pub p = pub.Pub[i];
...
}
Or if you don't care about the index and just want to go through the collection from start to finish:
foreach (Pub p in pub.Pub) {
...
}
(A more consistent naming of types and members would probably help.)

How do I test response data using nUnit?

Django has a very handy test client/dummy web browser that one can use in test cases to verify the correctness of HTTP responses (e.g., status codes, context/model data). It does not require you to have the web server running, as it deals directly with the framework to simulate the calls.
I'd really love an nUnit (or similar) equivalent that we can slip right into our test suites. We're working in MVC3 and 4, and want to check things like successful 301 redirects, that model validation is correct, and that ViewModel data is correct in the views.
What's the best solution for this?
ViewModel Data should be easy to check with the following:
public T GetViewModelFromResult<T>(ActionResult result) where T : class
{
Assert.IsInstanceOf<ViewResult>(result);
var model = ((ViewResult)result).Model;
Assert.IsInstanceOf<T>(model);
return model as T;
}
[Test]
public void TheModelHasTheOrder()
{
var controller = new MyController();
var result = controller.MyActionMethod();
var model = GetViewModelFromResult<MyModel>();
Assert.That(model, Is.SameAs(???));
}
As for the model validation, if you are using the out of the box .net property attributes like [Required] etc, you can be pretty sure they will work fine, and won't need testing.
To explicitly test the [Required] etc attributes on your object you will have extract the built in .net validation into another class. Then use that class in your controllers to validate your objects, instead of the Model.IsValid property on your controller.
The model validator class:
public class ModelValidator : IModelValidator
{
public bool IsValid(object entity)
{
return Validate(entity, new List<ValidationResult>());
}
public IEnumerable<ValidationResult> Validate(object entity)
{
var validationResults = new List<ValidationResult>();
Validate(entity, validationResults);
return validationResults;
}
private static bool Validate(object entity, ICollection<ValidationResult> validationResults)
{
if (entity != null)
{
var validationContext = new ValidationContext(entity, null, null);
return Validator.TryValidateObject(entity, validationContext, validationResults);
}
return false;
}
}
This could be verifiable in unit tests with the following:
public class MySampleEntity
{
[Required]
public string X { get; set; }
[Required]
public int Y { get; set; }
}
[TestFixture]
public class ModelValidatorTests
{
[Test]
public void GivenThePropertiesArePopulatedTheModelIsValid()
{
// arrange
var _validator = new ModelValidator();
var _entity = new MySampleEntity { X = "ABC", Y = 50 };
// act
var _result = _validator.IsValid(_entity);
// assert
Assert.That(_result, Is.True);
}
}

Resources