ServiceStack OrmLite raising MissingMethodException when selecting objects with references - c#

I'm attempting what I thought was going to be a simple select across two database tables. I'm selecting from an association table called PlayerEquipment that looks like this:
PlayerId | ItemId | Quantity | IsEquipped
----------+--------+----------+------------
1 1 1 1
1 3 1 0
This makes up a player's inventory in our system, mapping to specific IDs in our Player and Item tables.
The associated PlayerEquipment POCO looks like this:
public class PlayerEquipment
{
[Ignore]
public string Id
{
get { return PlayerId + "/" + ItemId; }
}
public int PlayerId { get; set; }
[References(typeof(ItemData))]
public int ItemId { get; set; }
public int Quantity { get; set; }
public bool IsEquipped { get; set; }
[Reference]
public ItemData ItemData { get; set; }
}
For now, ignore the fact that PlayerId isn't associated with a relevant Player object (it will be, eventually).
I'm trying to get a list of equipment for a player, given his ID:
List<PlayerEquipment> equipment = this.Db.LoadSelect<PlayerEquipment>(q => q.PlayerId == playerId);
When I do that call, I get this exception:
Method not found: 'Boolean ServiceStack.EnumerableExtensions.IsEmpty(System.__Canon[])'.
...with this stack trace:
at ServiceStack.OrmLite.OrmLiteReadCommandExtensions.LoadListWithReferences[Into,From](IDbCommand dbCmd, SqlExpression`1 expr, String[] include)
at ServiceStack.OrmLite.ReadExpressionCommandExtensions.LoadSelect[T](IDbCommand dbCmd, Expression`1 predicate, String[] include)
at ServiceStack.OrmLite.OrmLiteReadExpressionsApi.<>c__DisplayClass34`1.<LoadSelect>b__33(IDbCommand dbCmd)
at ServiceStack.OrmLite.OrmLiteExecFilter.Exec[T](IDbConnection dbConn, Func`2 filter)
at ServiceStack.OrmLite.OrmLiteReadExpressionsApi.Exec[T](IDbConnection dbConn, Func`2 filter)
at ServiceStack.OrmLite.OrmLiteReadExpressionsApi.LoadSelect[T](IDbConnection dbConn, Expression`1 predicate, String[] include)
[snip]
Looking online, I haven't found any help for this exception, but I can't imagine how I could make my query any simpler. Am I going about this in the wrong way? Should I instead query a Player or Item object that references a List<PlayerEquipment> property instead?

I've just tried this example in all supported Databases and it's working as expected. The only change was removing [Ignore] which is not allowed on Primary Keys as they need to exist on the underlying RDBMS table.
Types Used
public class PlayerEquipment
{
public string Id
{
get { return PlayerId + "/" + ItemId; }
}
public int PlayerId { get; set; }
[References(typeof(ItemData))]
public int ItemId { get; set; }
public int Quantity { get; set; }
public bool IsEquipped { get; set; }
[Reference]
public ItemData ItemData { get; set; }
}
public class ItemData
{
[AutoIncrement]
public int Id { get; set; }
public string Data { get; set; }
}
Create Tables, Populate Data, Select and Dump Results
db.DropTable<PlayerEquipment>();
db.DropTable<ItemData>();
db.CreateTable<ItemData>();
db.CreateTable<PlayerEquipment>();
var item1 = new ItemData { Data = "ITEM1" };
db.Save(item1);
db.Save(new PlayerEquipment
{
PlayerId = 1,
ItemId = item1.Id,
Quantity = 1,
IsEquipped = true,
});
var item2 = new ItemData { Data = "ITEM2" };
db.Save(item2);
db.Save(new PlayerEquipment
{
PlayerId = 1,
ItemId = item2.Id,
Quantity = 1,
IsEquipped = false,
});
var playerId = 1;
var results = db.LoadSelect<PlayerEquipment>(q => q.PlayerId == playerId);
results.PrintDump();
Console Output
[
{
Id: 1/1,
PlayerId: 1,
ItemId: 1,
Quantity: 1,
IsEquipped: True,
ItemData:
{
Id: 1,
Data: ITEM1
}
},
{
Id: 1/2,
PlayerId: 1,
ItemId: 2,
Quantity: 1,
IsEquipped: False,
ItemData:
{
Id: 2,
Data: ITEM2
}
}
]

Related

C# linq / Lambda - Adding an item to a list that summarizes all the items in the list

I have al list of Purches items
I want to add new item to my list, that sum all the items in my list
this is my code:
public class Purches
{
public int Id { get; set; }
public int Items { get; set; }
public int TotalPrice { get; set; }
}
List<Purches> purchesList = new List<Purches>() {
new Purches() {
Id = 1,
Items = 3,
TotalPrice = 220
},
new Purches() {
Id = 2,
Items = 5,
TotalPrice = 300
}
};
now, I want to add the list new item that sum the Items and the TotalPrice properties
the result will be something like that:
List<Purches> purchesList = new List<Purches>() {
new Purches() {
Id = 1,
Items = 3,
TotalPrice = 220
},
new Purches()
{
Id = 2,
Items = 5,
TotalPrice = 300
},
new Purches()
{
Id = 0,
Items = 8,
TotalPrice = 550
}
};
I have to do it via linq / Lambda in c#
I would not recommend adding a summary item of the same type. That is just likely to lead to confusion. A better solution would be to to use either a separate object for the total, or use different types with a shared interface, for example:
public class PurchaceSummary{
public List<Purches> Purchases {get;}
public TotalItemCount => Items.Sum(p => p.Items);
public TotalPrice => Items.Sum(p => p.TotalPrices);
}
Or
public interface IPurchaseLineItem{
public int Items { get; }
public int TotalPrice { get; }
}
public interface Purchase : IPurchaseLineItem{
public int Id { get; set; }
public int Items { get; set; }
public int TotalPrice { get; set; }
}
public interface PurchaseSummary : IPurchaseLineItem{
public int Items { get; set; }
public int TotalPrice { get; set; }
}
// Use the LINQ methods from the previous example to create your totals for the summary
In either case it should be immediately obvious for everyone what each value represents.
Purches totalSum = new Purches
{
Id = 0,
Items = purchesList.Sum(p => p.Items),
TotalPrices = purchesList.Sum(p => p.TotalPrices)
};
// now add it to your list if desired

I'm getting a "Collection was of a fixed size error" when trying to add objects to a DbSet in an in-memory database

I'm working on writing an API using .NET 6. I've encountered a "Collection was of a fixed size error" error, which I've never seen before. Looking here on SO I haven't seen anything that addresses this situation. Here's the DbContext class:
using Microsoft.EntityFrameworkCore;
namespace ECommerce.Api.Orders.Db
{
public class OrdersDbContext : DbContext
{
public OrdersDbContext(DbContextOptions dbContextOptions) : base(dbContextOptions)
{
}
public DbSet<Order> Orders { get; set; }
public DbSet<OrderItem> OrderItems { get; set; }
}
}
Here's the Order class:
namespace ECommerce.Api.Orders.Db
{
/*
* This is the in-memory class for Orders
*/
public class Order
{
public int Id { get; set; }
public int CustomerId { get; set; }
public DateTime OrderDate { get; set; }
public decimal Total { get; set; }
public ICollection<OrderItem>? Items { get; set; } }
}
And the OrderItem class:
namespace ECommerce.Api.Orders.Db
{
/*
* This is the in-memory class for OrderItems
*/
public class OrderItem
{
public int Id { get; set; }
public int OrderId { get; set; }
public int ProductId { get; set; }
public int Quantity { get; set; }
public decimal UnitPrice { get; set; }
}
}
Now comes the OrdersProvider class. In OrdersProvider I create an orderItems array, an orderItemsTotals array and 3 arrays of type OrderItem which I use to put into new values in the instances I add to the in-memory Orders table. The error comes on the second Add() method in Step 3:
private void SeedData()
{
if (!dbContext.Orders.Any())
{
// Step 1: Start by creating a collection of 3 OrderItems
OrderItem[] orderItems = new OrderItem[]
{
new OrderItem() {Id = 1, OrderId = 1, ProductId = 1, Quantity = 5, UnitPrice = 5.5M},
new OrderItem() {Id = 2, OrderId = 1, ProductId = 2, Quantity = 7, UnitPrice = 13.75M },
new OrderItem() {Id = 3, OrderId = 2, ProductId = 3, Quantity = 10, UnitPrice = 25.99M}
};
// need to get the totals of the values created above, into its own array
decimal[] orderItemTotals = new decimal[3];
for (int i = 0; i < 3; i++)
{
orderItemTotals[i] = orderItems[i].Quantity * orderItems[i].UnitPrice;
}
// Step 2: Need three separate array types to use in the creation of Orders
OrderItem[] orderItems1 = new OrderItem[2]
{
orderItems[0],
orderItems[2]
};
OrderItem[] orderItems2 = new OrderItem[2]
{
orderItems[0],
orderItems[1]
};
OrderItem[] orderItems3 = new OrderItem[1]
{
orderItems[2]
};
// Step 3: Then create a dbContext of Orders the 3 OrderItems above
dbContext.Orders.Add(new Db.Order()
{
Id = 1,
CustomerId = 1,
OrderDate = new DateTime(2022, 1, 17),
Items = orderItems1,
Total = orderItemTotals[0] + orderItemTotals[2]
});
dbContext.Orders.Add(new Db.Order()
{
Id = 2,
CustomerId = 2,
OrderDate = new DateTime(2020, 7, 1),
Items = orderItems2,
Total = orderItemTotals[0] + orderItemTotals[1]
});
dbContext.Orders.Add(new Db.Order()
{
Id = 3,
CustomerId = 3,
OrderDate = new DateTime(2018, 10, 31),
Items = orderItems3,
Total = orderItemTotals[2]
});
dbContext.SaveChanges();
}
}
At this point I don't understand why I'm getting that error when adding the second element after Step 3 in the code immediately above. I would think that Orders is a table; there should be nothing fixed about it. Or it might be related to Items in Order class, but Items is of nullable type of ICollection<OrderItem>. That shouldn't complain about being fixed, either.
So, where am I making my mistake?

LiteDB Insert list with BsonRef

Hi and thanks in advance everyone!
I have a collection of the following objects:
public class ItemsModel
{
public List<int> IdCollection { get; set; }
public string Name { get; set; }
public int Weight { get; set; }
}
List<ItemsModel> col = ...;
I want to optimally store this with LiteDb and be able to modify the records.
Each ItemsModel has a unique Name+Weight set.
In the entire col, the elements of the IdCollection are also unique.
Body example:
List<ItemsModel>:
[{
IdCollection: [1,3,5,6,...],
Name: "first name",
Weight: 10
},
{
IdCollection: [2,4,...],
Name: "second name",
Weight: 5
}]
I want to index by Id
I want to expand into two tables for easy storage in LiteDb:
[{
_id: 1,
NameAndWeight: {&ref: "names"}
},
{
_id: 2,
NameAndWeight: {&ref: "names"}
},
{
_id: 3,
NameAndWeight: {&ref: "names"}
},
...
]
[{
Name: "first name",
Weight: 10
},
{
Name: "second name",
Weight: 5
}]
For this I have to make new storage classes:
public class ItemsModel
{
[BsonId]
public int Id { get; set; }
[BsonRef("names")]
public NamesModel NameAndWeight { get; set; }
}
public class NamesModel
{
[BsonId(true)]
public ObjectId Id { get; set; }
public string Name { get; set; }
public int Weight { get; set; }
}
But next step I'm having trouble...
Tell me, can I somehow save data using Insert array and Include in one operation?
Or should I use foreach to first write the NamesModel in "names" DB, get the generated _id, then write the ItemsModel with a link to the NamesModel already written to the database?
using (var db = new LiteDatabase(_strConnection))
{
var itemsDb = db.GetCollection<ItemsModel>("items");
var namesDb = db.GetCollection<NamesModel>("names");
itemsDb.EnsureIndex(x => x.Id, true);
foreach (var group in col)
{
var name = new NamesModel(group.Name, group.Weight);
namesDb.Insert(name);
var itemDb = group.IdCollection.Select(el => new ItemsModel(el, name));
var h = itemsDb.Insert(itemDb);
}
}
it is too long(
Now I did like this:
using (var db = new LiteDatabase(_strConnection))
{
var itemsDb = db.GetCollection<ItemsModel>("items");
var namesDb = db.GetCollection<NamesModel>("names");
itemsDb.EnsureIndex(x => x.Id, true);
namesDb.EnsureIndex(x => x.Name);
var temp = col.Select(el => (el.IdCollection, new NamesModel(el.Name, el.Weight))).ToList();
namesDb.Insert(temp.Select(el => el.Item2));
var temp2 = temp.SelectMany(gr => gr.IdCollection.Select(el => new ItemsModel(el, gr.Item2)));
eventsIdDB.Insert(temp2);
}
Performed basic operations in linq to reduce the number of hits in liteDb

convert mvc c# model class to json from datatable

I have a C# class and data table.
DataTable:
+---------+-----------------+---------------+---------+---------+--------------+
| Pers_Id | Pers_First_Name | Pers_Last_Name| OrderNu | OrderId | Pers_Update |
+---------+-----------------+---------------+---------+---------+--------------+
| 1 | ABC | Ln | 76454 | 1 | 2018-03-25 |
+---------+-----------------+---------------+---------+---------+--------------+
| 1 | ABC | Ln | 76578 | 2 | 2018-03-25 |
+---------+-----------------+---------------+---------+---------+--------------+
Class:
public class Person
{
public int Pers_Id { get; set; }
public string Pers_First_Name { get; set; }
public string Pers_Last_Name { get; set; }
public DateTime Pers_Update { get; set; }
public List<Order> Order_List { get; set; }
public class Order
{
public int OrderID { get; set; }
public string OrderNu { get; set; }
}
}
I need to bind this class from data table and need to convert it into json object for rest API response in asp .net web API.
When i am binding i am getting json duplicate but result should be like this
{
"Pers_Id": 1,
"Pers_First_Name": "ABC",
"Pers_Last_Name": "LN",
"Pers_Update": "",
"Order_List": [
{
"OrderID": "1",
"OrderNu": "76454"
},
{
"OrderID": "2",
"OrderNu": "76578"
}
]
}
When you have an object (f.eks. your Employee object in this example), you should be able to return it like this:
return Content(JsonConvert.SerializeObject(employee), "application/json");
More info here: https://stackoverflow.com/a/34091196/4034346
First;
using System.Web.Script.Serialization;
Second;
If your data table's class isn't same with your Person class, then you should create a new class of datatable version for your persons.
public class Person
{
public int Pers_Id { get; set; }
public string Pers_First_Name { get; set; }
public string Pers_Last_Name { get; set; }
public DateTime Pers_Update { get; set; }
public List<Order> Order_List { get; set; }
public class Order
{
public int OrderID { get; set; }
public int OrderNu { get; set; }
}
}
//You need a class that fits to your DataTable
public class PersonDataTable
{
public int Pers_Id { get; set; }
public string Pers_First_Name { get; set; }
public string Pers_Last_Name { get; set; }
public DateTime Pers_Update { get; set; }
public int OrderId { get; set; }
public int OrderNu { get; set; }
}
In your method;
public string ReturnGoodPeopleJsonFormat()
{
JavaScriptSerializer js = new JavaScriptSerializer();//Needed for converting an object to Json string.
List<PersonDataTable> personDataTableList = new List<PersonDataTable>();//Needed for filling your data from in program or from database
List<Person> personList = new List<Person>();//Needed 'to be converted' in to Json string
//Add items to your DataTable list manually
personDataTableList.Add(
new PersonDataTable { Pers_Id = 1, Pers_First_Name = "ABC", Pers_Last_Name = "Ln", Pers_Update = Convert.ToDateTime("2018-03-25"), OrderId = 1, OrderNu = 76454 });
personDataTableList.Add(
new PersonDataTable { Pers_Id = 1, Pers_First_Name = "ABC", Pers_Last_Name = "Ln", Pers_Update = Convert.ToDateTime("2018-03-25"), OrderId = 2, OrderNu = 76578 });
//or from database
// personDataTableList.AddRange(myDatabaseModel.DataTables.ToList());
//Now group your data by Pers_Id //We are grouping this because we don't want same person 2 or 3 time, we want one person just one time but get all orders in it. That's why we need to group them by Pers_Id
foreach (var personGroup in personDataTableList.GroupBy(x => x.Pers_Id))
{
List<Person.Order> orderList = new List<Person.Order>();
foreach (var dataTablePerson in personDataTableList.Where(x => x.Pers_Id == personGroup.Key))
{
//Get all orders of personGroup one by one in to an Order list from PersonDataTable list by using Pers_Id like a foreign key.
///This personGroup.Key is nothing but Pers_Id\\\
orderList.Add(new Person.Order { OrderID = dataTablePerson.OrderId, OrderNu = dataTablePerson.OrderNu });
}
//Add new Person object to your personList if you don't have it before (by checking Pers_Id)
if (personList.Where(x => x.Pers_Id == personGroup.Key).Count() == 0) //This personGroup.Key is nothing but Pers_Id
{
personList.Add(new Person
{
Pers_Id = personDataTableList.Where(x => x.Pers_Id == personGroup.Key).FirstOrDefault().Pers_Id,
Pers_First_Name = personDataTableList.Where(x => x.Pers_Id == personGroup.Key).FirstOrDefault().Pers_First_Name,
Pers_Last_Name = personDataTableList.Where(x => x.Pers_Id == personGroup.Key).FirstOrDefault().Pers_Last_Name,
Pers_Update = personDataTableList.Where(x => x.Pers_Id == personGroup.Key).FirstOrDefault().Pers_Update,
Order_List = orderList
});
}
}
string JsonString = js.Serialize(personList);
return JsonString;
}
The result is like this:
[{"Pers_Id":1,"Pers_First_Name":"ABC","Pers_Last_Name":"Ln","Pers_Update":"/Date(1521925200000)/","Order_List":[{"OrderID":1,"OrderNu":76454},{"OrderID":2,"OrderNu":76578}]}]

sorting Self-Referencing Relationship

Assume the following model. Note the self-referencing relationship "parent".
public class Category
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public virtual Category Parent { get; set; }
public virtual long? ParentId { get; set; }
}
My data are as follows:
id | name | parentId
1--------tag 1 ----- null
2--------tag 2 ----- 1
3--------tag 3 ----- 1
4--------tag 4 ----- 2
5--------tag 5 ----- null
6--------tag 6 ----- null
I want to write a query that data will be sorted as follows
tag 1
----->tag 2
----->----->tag 4
----->tag 3
tag 5
tag 6
This is my code
var categorys = __categories
.AsNoTracking()
.ToList();
I do not know how to sort them
Well I would describe that more as hierarchical organisation as opposed to sorting, but here is an example of how you can achieve it quite simply. Note, this is not very optimised as the search for each Parent Category requires potentially a full scan of the entire Category list, but it's a good starting point:
using System;
using System.Collections.Generic;
using System.Linq;
namespace SimpleTree
{
public class Program
{
private static void Main(string[] args)
{
var categories = new List<Category>()
{
new Category {Id = 1, Name = "tag 1"},
new Category {Id = 2, Name = "tag 2", ParentId = 1},
new Category {Id = 3, Name = "tag 3", ParentId = 1},
new Category {Id = 4, Name = "tag 4", ParentId = 2},
new Category {Id = 5, Name = "tag 5"},
new Category {Id = 6, Name = "tag 6"},
};
foreach (var category in categories)
{
category.Parent = FindParent(categories, category.ParentId);
}
//pretty printing with indentation is left as an exercise for you :)
foreach (var category in categories)
{
Console.WriteLine("ID:{0} Name:{1} ParentID:{2}", category.Id, category.Name, category.ParentId);
}
Console.ReadLine();
}
private static Category FindParent(IEnumerable<Category> categories, long? parentId)
{
if (parentId == null) return null;
return categories.FirstOrDefault(c => c.Id == parentId);
}
}
public class Category
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public virtual Category Parent { get; set; }
public virtual long? ParentId { get; set; }
}
}
Output
ID:1 Name:tag 1 ParentID:
ID:2 Name:tag 2 ParentID:1
ID:3 Name:tag 3 ParentID:1
ID:4 Name:tag 4 ParentID:2
ID:5 Name:tag 5 ParentID:
ID:6 Name:tag 6 ParentID:
Note that depending on your use case, you might find it useful to include a ChildCategories collection on the Category object, and fill this as well, so that it's easy to walk the tree in either direction.
Try this recursive function
class Program
{
static void Main(string[] args)
{
using (var db = new aaContext2())
{
Temp temp = new Temp();
var cc = db.Catagory.FirstOrDefault();
IList<Category> parentList =new List <Category>();
foreach (Category catagory in db.Catagory.Where(cat => cat.ParentId == null))
{
parentList.Add(temp.Recursive(catagory.Id, catagory.Name));
}
}
}
}
public class Temp{
public Category Recursive(long parentId, string name)
{
Category catagory = new Category();
catagory.Id = parentId; catagory.Name = name;
using (var db = new aaContext2())
{
//base condition
if (db.Catagory.Where(catagory1 => catagory1.ParentId == parentId).Count() < 1)
{
return catagory;
}
else
{
IList<Category> newCatagoryList = new List<Category>();
foreach (Category cat in db.Catagory.Where(cata => cata.ParentId == parentId))
{
newCatagoryList.Add(Recursive(cat.Id, cat.Name));
}
catagory.CatagoryList = newCatagoryList;
return catagory;
}
}
}
}
public class aaContext2 : DbContext
{
public DbSet<Category> Catagory { get; set; }
}
public class Category
{
public virtual long Id { get; set; }
public virtual string Name { get; set; }
public virtual Category Parent { get; set; }
public virtual ICollection<Category> CatagoryList { get; set; }
public virtual long? ParentId { get; set; }
}

Categories

Resources