I'm trying to save a checkbox to db. It works right at debug mode but switching to release or custom release without code optimizations as explained here doesn't. Db is Sqlite. Looking with DB Browser for SQLite SimpleCheck field is just NULL or 0 at release and NULL, 0 or 1 at debug. (Edited)
private void SetSingleCheck(CheckBox box)
{
var processId = (int)box.Tag;
using (var db = new AgendaContext())
{
var check = db.Checks.SingleOrDefault(c => (c.ProcessId == processId) && (c.Date.Date == SelectedDate.Date.Date));
if (null == check)
{
check = new Check()
{
ProcessId = processId,
Date = SelectedDate.Date
};
db.Checks.Add(check);
}
if (box.IsChecked.HasValue)
{
check.SimpleCheck = box.IsChecked.Value;
}
db.SaveChanges();
}
}
Relevant parts of the model, just in case:
public class Check //Check if this process is ok or not, TODAY
{
public int CheckId { get; set; }
public DateTimeOffset Date { get; set; }
public Process Process { get; set; }
public int ProcessId { get; set; }
public bool? SimpleCheck { get; set; } //USed for checked situations. example: clean this thing: Ok, i have cleaned it.
public bool? DoubleCheck { get; set; } //Used for ok/no ok after doing something.
public ObservableCollection<Temperature>Temperatures { get; set; }
}
public DbSet<Check>Checks { get; set; }
What is wrong or missing? Thank you.
Error source was in the model
public bool? SimpleCheck { get; set; }
As there are no booleans in sqlite it seems that the ef core converter fails somewhere if its nullable, it assigns null if false and 0 if true, while expected results are 0 and 1. Reproduced in a test solution. Just making it non-nullable and adjusting null-checks at code did the trick.
Related
I search for a way to combine two or more IQueryables from different Object types in order to use it as a datasource for my treelist.
For the treelist I use the DevExpress WinForms component "TreeList".
It provides me the properties "KeyFieldName" which is usually mapped to the "ID" and the ParentFieldName which is mapped to the parent id in order to build a hierarchy.
I use entity framework 6 as or mapper.
I have the two following classes I would need to combine:
XObject:
[Table("tbl_objects")]
public class XObject
{
[Column("id")]
public int Id { get; set; }
[Column("display_name")]
public String DisplayName { get; set; }
[Column("description")]
public String Description { get; set; }
[Column("usage_reason")]
public String UsageReason { get; set; }
[Column("is_network_compatible")]
public bool IsNetworkCompatible { get; set; }
[Column("ip_address")]
public String IpAddress { get; set; }
[Column("network_name")]
public String NetworkName { get; set; }
[Column("serial_number")]
public String SerialNumber { get; set; }
[Column("manufacturer_identification_code")]
public String ManufacturerIdentificationCode { get; set; }
[Column("web_link")]
public String WebLink { get; set; }
[Column("warranty")]
public int WarrantyInDays { get; set; }
[Column("ref_manufacturer")]
public virtual XManufacturer Manufacturer { get; set; }
[Column("ref_order")]
public virtual XOrder Order { get; set; }
[Column("ref_owner")]
public virtual XOwner Owner { get; set; }
[Column("ref_room")]
public virtual XRoom Room { get; set; }
[Column("ref_object_folder")]
public virtual XObjectFolder ObjectFolder { get; set; }
public virtual ICollection<XAdditionalObjectData> AdditionalObjectData { get; set; }
}
XObjectFolder:
[Table("tbl_object_folders")]
public class XObjectFolder
{
[Column("id")]
public int Id { get; set; }
[Column("display_name")]
public String DisplayName { get; set; }
[Column("short_name")]
public String ShortName { get; set; }
[Column("ref_parent_folder")]
public virtual XObjectFolder ParentFolder { get; set; }
public virtual ICollection<XObjectFolder> ChildFolders { get; set; }
public virtual ICollection<XObject> Objects { get; set; }
[NotMapped]
public int ParentFolderId { get { return ParentFolder == null ? -1 : ParentFolder.Id; } }
}
As you've probably already seen, an object folder can contain subfolders but also objects.
My goal is to see this as one "datasource" in my treelist.
For example like this:
Object Folder A
Object Sub-Folder A
Object 1
Object 1
In other questions here I've found the possibilities to concat or union queryables, but that only works with them being the same type:
using (var db = new XDbContext(_conString))
{
// Queryables
var ofs = from of in db.ObjectFolders orderby of.DisplayName ascending select of; // <- All ObjectFolders
var obs = from obj in db.Objects orderby obj.DisplayName ascending select obj; // <- All Objects
// Concat them
var comb = ofs.Concat(obs); // <- not the same type
// As DataSource for my TreeList
TreeListObjects.DataSource = comb.ToList();
}
Which is why I am searching for a good way to make this possible.
I could also imagine me using a pretty bad approach to reach my goal. So I am open to suggestions. This is a personal project which I do to improve myself at stuff.
Thanks in advance!
EDIT
So I managed to get a step further by using an interface both classes share:
public interface ITreeListCombinable
{
int Id { get; set; }
int ParentId { get; }
String DisplayName { get; set; }
}
But... who would've thought... there occures another problem:
Have a look at the db structure:
Db_Struture
Since both objects are stored in different tables, the id's will certainly not be unique when combining them.
Which is necessary when setting the datasource.
Solution:
So I've taken my own approach to my problem and it worked out.
Full disclosure -> I consider myself a beginner, so this solution is probably not the best. Still, if anyone is in a similar situation, here's how it could work:
First I created an interface, which both the folder and objects share:
ITreeListCombinable
public interface ITreeListCombinable
{
int Id { get; set; }
int ParentId { get; }
int ListId { get; set; }
int ParentListId { get; set; }
String DisplayName { get; set; }
ObjectTreeListElementTypes TreeListElementType { get; }
}
I then made sure, both my XObject and XObjectFolder classes held the ObjectTreeListElementTypes value they're corresponding to:
ObjectTreeListElementTypes Enum:
public enum ObjectTreeListElementTypes
{
Folder,
Object
}
Classes:
[NotMapped]
public ObjectTreeListElementTypes TreeListElementType => ObjectTreeListElementTypes.Folder; // or *.Object for that matter
So afterwards I've wrote my own "controller" which handles my specific scenario.
ObjectTreeListElementController:
public class ObjectTreeListElementController
{
private List<ITreeListCombinable> _list;
public ObjectTreeListElementController()
{
_list = new List<ITreeListCombinable>();
}
public void AddRange(List<ITreeListCombinable> list)
{
// add incoming items to private _list
_list.AddRange(list);
}
public List<ITreeListCombinable> GetDataSourceList()
{
// create auto increment list id
var listId = 0;
foreach (var item in _list)
{
item.ListId = listId;
listId++;
}
// set new parent list id according to incremental list id
foreach (var item in _list)
{
var parents = _list.Where(x => x.Id == item.ParentId && x.TreeListElementType == ObjectTreeListElementTypes.Folder);
if (parents.Count() > 0)
item.ParentListId = parents.First().ListId;
else
item.ParentListId = -1;
}
return _list;
}
}
Essentially, when calling the GetDataSourceList() method, it firstly distributes incremental, temporary list-ids.
In a second loop I then search for the original parent id and match the tree list element type. If none is found, this folder is a root folder in my treelist, if one is found, the given list-id becomes the parent list id:
using (var db = new XDbContext(_conString))
{
// Queryables
IQueryable<ITreeListCombinable> ofs = from of in db.ObjectFolders orderby of.DisplayName ascending select of;
IQueryable<ITreeListCombinable> objs = from obj in db.Objects orderby obj.DisplayName ascending select obj;
var lofs = ofs.ToList();
var lobjs = objs.ToList();
var ctrl = new ObjectTreeListElementController();
ctrl.AddRange(lofs);
ctrl.AddRange(lobjs);
var sourceList = ctrl.GetDataSourceList();
// As DataSource for my TreeList
TreeListObjects.DataSource = sourceList;
}
And this brought me the exact output I've wanted:
Hope this helps another beginner :)
I have been working on a shop site project, using asp.net core spa templates provided with the latest VS2017, and have come across an issue that I haven't had before, possibly because until now my apps were quite simple!
I know what the problem is and where, I just can't fix it. I have a product model which has a collection of "Attributes" and a collection of "Variations" (different colour size, etc) and those variations also have attributes, so if the same Attribute shows up in the Variation (VAttributes), as is already in the main "Attributes" I get the error
InvalidOperationException: The instance of entity type
'ProductAttribute' cannot be tracked because another instance with the
key value 'Id:2' is already being tracked. When attaching existing
entities, ensure that only one entity instance with a given key value
is attached.
The best answer I found was here : https://stackoverflow.com/a/19695833/6749293
Unfortunately, even with the above check I got the error, I even tried making a list of attached attributes, and if the vattribute matched one of the items in the list, I didn't attach it. In fact I found that even if I don't attach (_context.attach()) any of the vAttributes, it still throws the error!.
Here's the code in question:
public async Task<Product> Create(Product product)
{
try
{
foreach (var variation in product.Variations)
{
foreach (var vAttr in variation.VAttributes)
{
bool isDetached = _context.Entry(vAttr).State == EntityState.Detached;
if (isDetached)
_context.Attach(vAttr);
}
}
foreach (var attribute in product.Attributes)
{
bool isDetached = _context.Entry(attribute).State == EntityState.Detached;
if (isDetached)
_context.Attach(attribute);
}
foreach (var category in product.Categories)
{
_context.Attach(category);
_context.Attach(category).Collection(x => x.Children);
}
_context.Products.Add(product);
await Save();
return product;
}
catch (Exception)
{
throw;
}
}
The models for the 3 objects are as follows:
public class Product
{
[Key, DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Description { get; set; }
public string StockRef { get; set; }
public DateTime? LastModified { get; set; }
//image needed
public ICollection<ProductCategory> Categories { get; set; }
public ICollection<ProductAttribute> Attributes { get; set; }
public ICollection<ProductVariation> Variations { get; set; }
public Product()
{
Attributes = new List<ProductAttribute>();
Variations = new List<ProductVariation>();
Categories = new List<ProductCategory>();
}
}
Variation:
public class ProductVariation
{
[Key, DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public DateTime? LastModified { get; set; }
public virtual ICollection<ProductAttribute> VAttributes { get; set; }
//needs images
public decimal VPrice { get; set; }
public string VStockRef { get; set; }
}
Finally the Attribute:
public class ProductAttribute
{
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string Name { get; set; }
[ForeignKey("AttributeCategory")]
public int AttributeCategoryId { get; set; }
public virtual AttributeCategory AttributeCategory { get; set; }
}
Most help I found when searching was more related to having repo's injected as singletons, or HttpPut methods where the code had check for existence omitting the .AsNoTracking() or it was a mistake that they had the second instance in some way, where I am aware of the second instance, I just don't know how to prevent it from being tracked!
EDIT: I found that adding a foreign key on the ProductVariation model to the Product that was being created failed as it was only a temp key!? anyway removed it from the variation model, so have updated my code. Also thought I'd add one of my earler failed attempts, that led to all of the foreach loops.
_context.AttachRange(product.Attributes);
_context.AttachRange(product.Categories);
_context.AttachRange(product.Variations);
_context.Add(product);
I believe you can allow EF to handle the tracking.
public virtual bool Create(T item)
{
try
{
_context.Add(item);
_context.SaveChanges();
return true;
}
catch (Exception e)
{
return false;
}
}
This allows for you to save the entire object structure without worring about attaching items.
var newProduct = new Product();
newProduct.Categories.Add(cat);
newProduct.Attributes.Add(att);
newProduct.Variations.Add(vari);
Create(newProduct);
I have some simple code I am using to update a profile picture that is saved in my database as a byte[]. For some odd reason it runs through my code with no errors at all but the database is not updated with the new picture that the user has chosen.
Client client = db.Clients.FirstOrDefault(c => c.Email == User.Identity.Name);
if (client != null)
{
client.ProfileImage = bytes;
db.Clients.Attach(client);
var entry = db.Entry(client);
entry.Property(e => e.ProfileImage).IsModified = true;
db.SaveChanges();
}
Here is the code for the Client entity class:
public partial class Client
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Client()
{
Customers = new HashSet<Customer>();
}
public int ID { get; set; }
public int? StripeID { get; set; }
[Required]
[StringLength(200)]
public string Name { get; set; }
[Required]
[StringLength(150)]
public string Email { get; set; }
public Guid UserID { get; set; }
public bool IsActive { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
public byte[] ProfileImage { get; set; }
public virtual StripeInfo StripeInfo { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<Customer> Customers { get; set; }
}
Also after debugging the code I can see that the profile image is being set to the correct byte[] and the client.ProfileImage then contains the correct byte[], but as soon as db.SaveChanges() is called, client.ProfileImage is immediately set back to what it originally was. No errors occur or anything so I really can't figure out why this would be happening.
7/4/2017
Here are some additional things I have tried after looking on the internet some more:
1) Create a new database context instance to make the update.
2) Dispose of the original database context and create a new one to make the update.
Neither of the above attempts have worked. The ProfileImage property is still just set back to the original value when db.SaveChanges() is called.
The issue is that the ProfileImage property of your entity has an attribute applied to it:
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
Which, from the documentation, is telling Entity Framework:
Computed: The database generates a value when a row is inserted or updated.
You just need to remove that attribute completely.
Can you try with below code ? :
Client client = db.Clients.FirstOrDefault(c => c.Email == User.Identity.Name);
if (client != null)
{
client.ProfileImage = bytes;
var entry = db.Entry(client);
entry.Property(e => e.ProfileImage).IsModified = true;
db.SaveChanges();
}
If client has been already loaded from context then no need to attach it !
I have 3 tables:
Duty
Task :Duty
ProcessTask : Task
I'm using TPT hierarchy and everything seems fine when creating the database. But when I read the values from the database, I'm not getting any values from the Duty table. This is my query:
var tasks = _dbContext.Duties.OfType<Task>()
.OfType<ProcessTask>()
.Where(c => c.ParentID == id);
Only values from Task and ProcessTask is returned, although the SQL query returned all the correct values (I checked with SQL Profiler).
Why would it not map the first abstract class' fields? Any help is much appreciated.
My source code:
public class Duty
{
public int ID { get; set; }
}
[Table("Task")]
public class Task : Duty
{
public int ID { get; set; }
public int? Duration { get; set; }
public int? NoOfRepeats { get; set; }
}
[Table("ProcessTask")]
public class ProcessTask : DigiBob.Model.Governance.Duties.Task
{
public int ProcessTaskTypeID { get; set; }
public virtual ProcessTaskType ProcessTaskType { get; set; } //ProcessTasks
public int? ParentID { get; set; }
[ForeignKey("ParentID")]
public virtual ProcessTask Parent { get; set; } //Children
[Required(ErrorMessage = "A short description is required")]
[MaxLength(150, ErrorMessage = "The maximum length for the short description is 150")]
public string ShortDescription { get; set; }
}
public class MyContext : DbContext
{
public DbSet<Duty> Duties { get; set; }
}
Your query is wrong... - otherwise your code should work fine
What you're doing effectively - is just returning the ProcessTask-s.
(OfType selects only Tasks - but then you reapply
OfType to it - and further narrow that down.
// this returns just ProcessTask-s
IEnumerable<Duty> tasks = db.Duties
.OfType<Task>()
.OfType<ProcessTask>()
.ToList();
// this returns all Task-s (including processes)
tasks = db.Duties
.OfType<Task>()
// .OfType<ProcessTask>()
.ToList();
// this returns all Duties (including tasks, processes)
tasks = db.Duties
.OfType<Duty>()
.ToList();
I was referencing the ID field incorrectly. This is what I had:
public static ProcessTaskView ConvertToProcessTaskView(this ProcessTask processTask)
{
ProcessTaskView processTaskView = new ProcessTaskView();
processTaskView.ShortDescription = processTask.ShortDescription;
processTaskView.ProcessTaskTypeID = processTask.ProcessTaskTypeID;
processTaskView.ID = processTask.ID;
return processTaskView;
}
This is the correct code.
public static ProcessTaskView ConvertToProcessTaskView(this Duty processTask)
{
ProcessTaskView processTaskView = new ProcessTaskView();
processTaskView.ShortDescription = (processTask as ProcessTask).ShortDescription;
processTaskView.ProcessTaskTypeID = (processTask as ProcessTask).ProcessTaskTypeID;
processTaskView.ID = processTask.ID;
return processTaskView;
}
Note that I'm now passing in a Duty object and am having to specify what type of Duty it is to access properties of the ProcessTask. Now the ID field has a value, not just 0.
Thanks for the help
(Please read before marking as duplicate as my particular scenario is unique)
I have the following code:
// Get each treasure hunt
var treasureHunts = dbContext.TreasureHunts.Where(i => i.UserName == User.Identity.Name).ToList();
// Populate each treasure hunt with the list of leaderboard entries
for (int i = 0; i <= treasureHunts.Count; i++)
{
treasureHunts[i].Leaderboard = dbContext.Leaderboard.Where(
leaderboard => leaderboard.TreasureHuntId == treasureHunts[i].TreasureHuntId).ToList();
}
On running the program, I get the following error from the second database query (dbContext.Leaderboard.Where...):
LINQ to Entities does not recognize the method
'QrCodeTreasureHunter.Models.TreasureHuntDetails get_Item(Int32)'
method, and this method cannot be translated into a store expression.
In the first query, I'm getting each of the treasure hunts associated with a particular user.
In the second part, I'm attempting to iterate through each of the treasure hunts, and populate the treasure hunt's Leaderboard List property with the associated leaderboard entries from my Leaderboard table.
From what I understand from reading around is that this query isn't possible in its current form with Entity Framework.
What workarounds or solutions would you be able to recommend to solve this problem? The ideal solution would involve no changes to the data models.
If it's relevant, here is the TreasureHunt model:
public class TreasureHuntDetails
{
public TreasureHuntDetails()
{
Clues = new List<Clue>();
Leaderboard = new List<Leaderboard>();
var xml = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
var dcs = new DataContractSerializer(typeof (TreasureHuntDetails), null, int.MaxValue,
false, true, null);
xml.SetSerializer<TreasureHuntDetails>(dcs);
}
[Key]
public int TreasureHuntId { get; set; }
[Required]
public virtual string UserName { get; set; }
[Required]
public String Name { get; set; }
public String Description { get; set; }
[Required]
public String Password { get; set; }
public String CompletionMessage { get; set; }
public String State { get; set; }
public List<Clue> Clues { get; set; }
public List<Leaderboard> Leaderboard { get; set; }
}
And here is the Leaderboard model:
public class Leaderboard
{
[Key]
public int Id { get; set; }
public int TreasureHuntId { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int Completion { get; set; }
public DateTime? StartTime { get; set; }
public DateTime? EndTime { get; set; }
public Int64 TimeTaken { get; set; }
public TreasureHuntDetails TreasureHuntDetails { get; set; }
}
Good luck!
I'm not able to test it right now but it could be the indexer, try this:
foreach (var treauserHunt in treasureHunts)
{
treasureHunt.Leaderboard = dbContext.Leaderboard.Where(leaderboard =>
leaderboard.TreasureHuntId == treasureHunt.TreasureHuntId).ToList();
}
I'm not sure this is the problem, but I remember having some issues with indexing in arrays in LINQ queries, just can't remember if it was with the LINQ method syntax (the one you are using) or the other (the SQL-like);
Thanks for the answer; it solved my problem. I still wanted to use a for loop so I did something like this:
for (int i = 0; i <= treasureHunts.Count; i++)
{
var thisTreasureHunt = treasureHunts[i];
treasureHunts[i].Leaderboard = dbContext.Leaderboard.Where(
leaderboard => leaderboard.TreasureHuntId == thisTreasureHunt.TreasureHuntId).ToList);
}