I have a reliable service with 3 partitions. The data is partitioned reasonably equally between the 3 - it's almost exact. I have just under 3.95 million entries of the following contract across the 3 partitions
[DataContract]
public class Item
{
[DataMember]
public string StandardisedText { get; set; }
[DataMember]
public string Vendor{ get; set; }
[DataMember]
public bool Card{ get; set; }
[DataMember]
public string Suburb { get; set; }
[DataMember]
public string State { get; set; }
[DataMember]
public string PostCode { get; set; }
[DataMember]
public float Latitude { get; set; }
[DataMember]
public float Longitude { get; set; }
[DataMember]
public string Level1 { get; set; }
[DataMember]
public string Level2 { get; set; }
[DataMember]
public string Level3 { get; set; }
[DataMember]
public string Level4 { get; set; }
}
I have an upstream aggregate service which reports back a count across all partitions with the following code (please ignore the terribleness of this code, its a quick and dirty POC to see how viable reliable collections are for reasonably large data sets).
[HttpGet("")]
public IActionResult GetAll()
{
try
{
_logger.LogError("Getting all count");
ServiceEventSource.Current.Message("Getting all count");
var settings = new FabricTransportRemotingSettings { OperationTimeout = TimeSpan.FromMinutes(30) };
var factory =
new ServiceProxyFactory(h =>
new FabricTransportServiceRemotingClientFactory(settings));
int total = 0;
foreach (var servicePartitionKey in PartitionKeys)
{
var proxy = factory.CreateServiceProxy<ITermTextService>(
new Uri("fabric:/Acme.Fabric.Refinery/Acme.Fabric.Refinery.RefineryStore"),
servicePartitionKey);
var count = proxy.Count().Result;
ServiceEventSource.Current.Message($"Searched partition {servicePartitionKey.Value} and found {count} values" );
_logger.LogInformation($"Searched partition {servicePartitionKey.Value} and found {count} values");
total += count;
}
return Ok(total);
}
catch (Exception excep)
{
_logger.LogError($"Error in get all {excep.Message} {excep.StackTrace}");
ServiceEventSource.Current.Message($"Error {excep.Message} {excep.StackTrace}");
}
return null;
}
The count code is
public async Task<int> Count()
{
int i = 0;
var termTexts = await TermTexts;
using (var tx = StateManager.CreateTransaction())
{
var enumerable = await termTexts.CreateEnumerableAsync(tx);
var enumerator = enumerable.GetAsyncEnumerator();
while (await enumerator.MoveNextAsync(CancellationToken.None))
{
i++;
}
}
return i;
}
The total time of this operation is 747165 ms (12 minutes). This is an integer on the wire - no large packets.
My question is, is this performance in the expected region or should I investigate this further? It could be that using a RC for this volume of data is a misuse, so we would need to look elsewhere. These response times imply also that there are disk reads involved, so another question is at what point would this occur and can this be configured? A read from hot storage I would imagine to be in the region below 50ms per partition.
Thanks in advance.
If u enumerate millions of records asynchronously, this performance is expected. If u use IReliableDictionary2, you can use the Count property directly. No need to enumerate all records. If u want to search by key, use CreateAsyncEnumerable function.
Related
I am to new both entity framework and dotnet core. Shortly i want to explain what i did and what kind of an error i got?
What i did?
First I created a few models below.
public class Quiz
{
public int QuizID { get; set; }
public int UserID { get; set; }
public string Text { get; set; }
public User User { get; set; }
public ICollection<Question> Questions { get; set; }
}
public class Question
{
public int QuestionID { get; set; }
public string Text { get; set; }
public int QuizID { get; set; }
public Quiz Quiz { get; set; }
public IList<Option> Options { get; set; }
}
public class User
{
public int ID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public ICollection<Quiz> Quizzes { get; set; }
}
public class Option
{
public int OptionID { get; set; }
public string Choice { get; set; }
public int QuestionID { get; set; }
public bool? IsCorrect { get; set; }
public Question Question { get; set; }
}
Second I created IActionResult method for saving informations that comes from user.
public async Task<IActionResult> CreateQuiz()
{
Quiz quiz = new Quiz();
quiz.UserID = 0;
quiz.Text = Request.Form["content"].ToString();
_context.Add(quiz);
await _context.SaveChangesAsync();
for (int i = 1; i <= 4; i++)
{
Question question = new Question();
question.QuizID = quiz.QuizID;
question.Text = Request.Form["title_" + i].ToString();
_context.Add(question);
_context.SaveChanges();
for (int j = 1; j <= 4; j++)
{
Option option = new Option();
option.QuestionID = question.QuestionID;
option.Choice = Request.Form["option_a" + i].ToString();
option.IsCorrect = j == int.Parse(Request.Form["correct_answer_" + i].ToString());
_context.Add(option);
}
}
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
Finally i got this error when i save form.
An unhandled exception occurred while processing the request.
SqliteException: SQLite Error 19: 'FOREIGN KEY constraint failed'.
Microsoft.Data.Sqlite.SqliteException.ThrowExceptionForRC(int rc,
sqlite3 db)
DbUpdateException: An error occurred while updating the entries. See
the inner exception for details.
Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection
connection, CancellationToken cancellationToken)
I need your help guys.
*Sorry for spelling rules i am also new to english.
There are a few things you can do to help avoid issues like this. It's a bit hard to pin down from your description exactly what is wrong, but you can simplify your code considerably which should take care of it.
Firstly, follow a convention for your key names and use annotations to nominate your Keys rather than relying on convention. You have a mix of naming like "QuestionId" for the PK on Question, yet "Id" for the PK on User.
Next, I would remove all FK fields and use Shadow Properties for the FK fields. The issue with FK columns is they are a second source of truth for relationships when you use Navigation Properties. Is option.QuestionId the ID of the question, or option.Question.QuestionId? How do you guarantee these are always in sync?
Lastly for the entity definitions, declare the navigation properties as virtual. This serves both lazy-loading (as a failsafe) as well as change tracking via proxies.
So updating the entity definitions to something like:
public class Quiz
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int QuizID { get; set; }
public string Text { get; set; }
public virtual User User { get; set; }
public virtual ICollection<Question> Questions { get; set; } = new List<Question>();
}
public class Question
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int QuestionID { get; set; }
public string Text { get; set; }
[ForeignKey("QuizID")] // Creates a shadow property mapped to a QuizID column in table.
public virtual Quiz Quiz { get; set; }
public virtual ICollection<Option> Options { get; set; } = new List<Option>();
}
public class User
{
[Key]
public int UserID { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public virtual ICollection<Quiz> Quizzes { get; set; } = new List<Quiz>();
}
public class Option
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int OptionID { get; set; }
public string Choice { get; set; }
public bool? IsCorrect { get; set; }
[ForeignKey("QuestionID")]
public virtual Question Question { get; set; }
}
Now, when it comes to creating your question data you can leverage EF to manage the FKs by populating a complete set of related entities and saving them together.
public async Task<IActionResult> CreateQuiz()
{
var systemUser = _context.Users.Single(x => x.UserId == 0);
Quiz quiz = new Quiz();
quiz.User = systemUser;
quiz.Text = Request.Form["content"].ToString();
for (int i = 1; i <= 4; i++)
{
Question question = new Question();
question.Text = Request.Form["title_" + i].ToString();
quiz.Questions.Add(question);
for (int j = 1; j <= 4; j++)
{
Option option = new Option();
option.Choice = Request.Form["option_a" + i].ToString();
option.IsCorrect = j == int.Parse(Request.Form["correct_answer_" + i].ToString());
question.Options.Add(option);
}
}
_context.Quizes.Add(quiz);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
This can likely be simplified even more, but I kept it close to the original so it should be easier to follow. Rather that calling SaveChanges several times and trying to set FKs, it is far better to create the objects and associate them with each other. We create our quiz, then go through and create the questions, associating them to the Quiz by adding them to the quiz.Questions collection. Then go through and do the same for the Question Options. Once we are done, we tell the context to add the Quiz to it's Quizes DbSet, and call SaveChanges once at the very end. EF will save all of the entities and their relationships automatically, populating the appropriate FKs. The benefit here is that all of the changes are committed together in one transaction rather than separate saves where a quiz might be saved, but there was an issue with one question or one option, leaving the database in an incomplete state at the point of failure.
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 one class and one view model class.
UserClass contains about 30,000 records
[Key]
public int UserId { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public string Password { get; set; }
public string ActiveCode { get; set; }
public bool IsActive { get; set; }
public string UserAvatar { get; set; }
public DateTime RegisterDate { get; set; } = DateTime.Now;
public bool IsDelete { get; set; } = false;
and my viewmodel class contain
public string UserName { get; set; }
public string Email { get; set; }
public DateTime RegisterDate { get; set; }
I want to run my query like this
var list= EF.CompileQuery((TopLearnContext db) =>
db.Users.Select(x=>new InformationUserViewModel()
{
Email = x.Email,
UserName = x.UserName,
RegisterDate = x.RegisterDate
}));
foreach (var item in list)
{
}
but got this error at run time:
foreach statement cannot operate on variables of type 'Func<TopLearnContext, IEnumerable<InformationUserViewModel>>' because 'Func<TopLearnContext, IEnumerable<InformationUserViewModel>>' does not contain a public instance definition for 'GetEnumerator'
My question is, how can I do foreach loop in EF.CompileQuery?
As I know EF.CompileQuery improves performance and because of that I want to know how can I use it in foreach.
A CompiledQuery doesn't have anything to loop over. If you want the results from the query, it has to be executed first:
var toLoop = list.Invoke(context, ...)
Which will be an IQueryable which can be looped over.
thanks for the reply I try it and it works
but it takes times as like as this method
var list = db.Users.Select(x=>new InformationUserViewModel()
{
Email = x.Email,
UserName = x.UserName,
RegisterDate = x.RegisterDate
}).toList();
shouldn`t EF.CompileQuery must have much faster compile time?
I am retrieving data from Azure Table Storage and storing them in IEnumerable, but I don't know how to convert this IEnumerable<T> into IReliableDictionary<string, Store>, so that I can save this store data into state manager of stateful service.
var storesRepo = await StateManager.GetOrAddAsync<IReliableDictionary<long, Store>>("");
So how can I insert this IEnumerable<Store> into storesRepo?
Store Class:
public class Store : TableEntity
{
public string Name { get; set; }
public double Longitude { get; set; }
public double Latitude { get; set; }
public string Address { get; set; }
public double Distance { get; set; }
public string Email { get; set; }
public string City { get; set; }
}
ReliableDictionary<string, Store>
where key is store name.
Create a dictionary, enumerate over all stores in the collection, add them one by one, inside a transaction.
Make sure the set of stores doesn't get too large to avoid a long running transactions & locks.
Make sure to think about partitioning first
Add retry support.
Apply best practices from here.
meta code below:
var storesRepo = await StateManager.GetOrAddAsync<IReliableDictionary<string, Store>>("someName");
IEnumerable<Store> stores = await GetFromTableStorage();
using (ITransaction tx = base.StateManager.CreateTransaction())
{
foreach(var store in stores)
{
await storesRepo.AddAsync(tx, store.Name, store, cancellationToken);
}
await tx.CommitAsync();
}
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