As a way to learn NHibernate, I came up with a small project that includes a typical users & groups authentication system. It got me thinking about how this would be done. I quickly put together the following classes and mapped them to the database, which worked after a lot of trial and error. I ended up with a three-table database schema with a many to many association table between the User and Group tables.
public class User
{
public virtual string Username { get; set; }
public virtual byte[] PasswordHash { get; set; }
public virtual IList<Group> Groups { get; set; }
}
public class Group
{
public virtual string Name { get; set; }
public virtual IList<User> Users { get; set; }
}
My question is regarding the scaleability and potential performance of this sort of class design. If this was in a production system with tens of thousands of users, even with lazy-loading on a Group's Users collection, any call to the Groups property could set off a potentially HUGE data retrieval.
How would NHibernate cope with such a scenario and how might I improve upon my design?
Don't create these as properties. Add functions to these classes which will allow you to fine tune your queries (through the use of parameters) to retrieve the specific data sets you require.
I know this question is old, but just happened to stumble upon it. You state "even with lazy-loading on a Group's Users collection, any call to the Groups property could set off a potentially HUGE data retrieval." Why? Presumably the number of groups is not tens of thousands, and accessing the Groups property on User would only load the Groups collection, not the Users collection within the Groups collection (unless Users wasn't marked to lazy-load). The huge data retrieval would only occur if you accessed the Users collection in Group, in which case I would recommend not having that relationship accessible from the Group mapping.
Related
Having recently moved to C#/.Net Core from other languages, I got stuck on a problem with EF Core that I couldn't figure out from the documentation and hope you may be able to help. In a way this is related but not identical to my previous question .Net Core [FromBody] JArray to ICollection
My database holds a number of appointments that are rendered on a Syncfusion schedule. Attendees can be invited to these appointments. To facilitate that, a list of users is displayed in the editor and a JSON array of guids is transmitted with any insert or update action.
The User entity itself is not available within the scope of the application, so I'd like to persist only their Guids for each appointment. I had foreseen this structure:
Appointments (Start, End, ..., ´ICollection Attendees´)
Attendee would simply consist of AppointmentsId and Guid -
Since any one Guid can only attend each Appointment once, a composite Key made up of these two attributes appeared to be useful.
Any appointment can have none, one or many associated Guids.
In Code, I have this (abbreviated):
public class Appointment
{
public int Id { get; set; }
public DateTime StartTime { get; set; }
public ICollection<Attendee> AttendeeList { get; set; }
[NotMapped]
public List<Guid> PostedAttendeeList { get; set; } // Contains a list of Guids after an Insert/Update POST action from [FromBody]
}
Attendee would simply be made up of the Appointment Id and a Guid of a user.
public class Attendee
{
public Guid Id { get; set; }
public int AppointmentId { get; set; }
}
Attendee's configuration is this:
public class AttendeeConfiguration : IEntityTypeConfiguration<Attendee>
{
public void Configure(EntityTypeBuilder<Attendee> builder)
{
builder.HasKey(x => new { x.Id, x.AppointmentId });
}
}
After receiving a POST from the schedule, ´PostedAttendeeList´ may be empty or contain one or more Guids.
If it is an existing Appointment, ´AttendeeList´ may be empty or contain one or more Guids.
I'm wondering about a few things:
a) is there a better way to go about persisting this kind of data? I've tried to understand Owned Entity Types but failed to see if that would help me here.
b) if this is indeed an ok way to handle this, how can I make sure that ´AttendeeList´ is identical to ´PostedAttendeeList´ after processing, so that all new entries are added and those not present in ´PostedAttendeeList´ are removed through EF Core?
I'm especially confused about whom's responsibility it is to maintain ´AppointmentId´ - I wanted to keep the property visible but I understand that EF would fill that in when operating within the base property? Ie. that within the class ´Appointment´, a ´AttendeeList.Add(new Attendee() { Id = "1234-abcd-..." }´ would automagically fill in the AppointmentId upon saving?
If you read until here and are confused, please take a moment to remember when you started programming - I'm thoroughly confused and unable to come up with a better question. Even if you cannot help out with an answer, maybe you could help me make the question better. Thank you all very much! Any comment with suggestions will result in an update to my question in order to improve it.
I'm building a mobile app in xamarin that has a lot of relationships between classes. For simplicity consider it to be a "University" app that has the classes: ExamPapers, Students, ExamAnswers.
The ExamPaper class would look like this
public class ExamPapers {
[ManyToMany(typeof(Student_ExamPaper))]
public List<Student> Students { get; set; }
[OneToMany]
public List<ExamAnswers> Files { get; set; }
[OneToMany(CascadeOperations = CascadeOperation.All)]
public List<ExamSection> Sections { get; set; }
public string ExamName { get; set; }
[PrimaryKey]
public string Id { get; set; }
}
So at the moment any sqlitenetextension operation (the ones that end with "WithChildren") will interact with all the relationships when I often just want to interact with one at a time. Here are some example scenarios:
A student "Lana" is just granted access to an ExamPaper "Mathematics 101". Now to grant her access I need to load; all other students with access, all the files for the exam, all the sections. Otherwise these relationships get deleted when I call "InsertOrReplaceWithChildren".
I want to find out the first question in an ExamPaper. I call "GetWithChildren(examId)". I now have an object with a lot of information I don't want (e.g. 300 students and 300 ExamAnswers).
Sorry if I missed something relevant in the documentation, but I've read it through a couple times now. https://bitbucket.org/twincoders/sqlite-net-extensions
Thanks.
Answer on your question in title: No. You cannot.
SQLite-Net-Extensions does not provide such flexible API for manipulating related data.
But there is one helpful thing that can be used in specific cases:
You can work with junction tables as simple tables through SQLite-Net methods (methods without *WithChildren postfix) if junction tables has PrimaryKey (Id).
For example if you have any data which you want to get withoud additional (related) data, just simply call Table</*class*/>() method on specific table with Where(/*your condition*/) clause for getting only data that you really need. Then you can save modified data through Update method.
Unfortunately, this thing will not work with relations update (for example, if you want to move one ExamAnswer from ExamPaper to another ExamPaper object) because all SQLite-Net-Extensions attributes inherited from Ignore SQLite-Net attribute which is ignoring in all SQLite-Net operations.
But there is another one workaround (a little hacky), you can specify second class without any SQLite-Net-Extensions attributes and that's provide you to CRUD any field in specific table.
At first I want to give an example. Here I will use code first approach to make database tables and their relationship. Please look at the class below. (C#)
public class Blog
{
public int BlogId { get; set; }
public string Name { get; set; }
public virtual List<Post> Posts { get; set; }
}
public class Post
{
public int PostId { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public int BlogId { get; set; }
public virtual Blog Blog { get; set; }
}
You’ll notice that I'm making the two navigation properties (Blog.Posts and Post.Blog) virtual. This enables the Lazy Loading feature of Entity Framework. Lazy Loading means that the contents of these properties will be automatically loaded from the database when you try to access them.
Now Here is my question.
I want to make a Database as like below. The table names will be:
tblCompany
tblSite // Site will be create under Company (A Company will have one or more Sites).
tblLine // Line will be create under Site (A Site will have one or more Lines).
tblMachine // Machine will be create under Line (A Line will have one or more Machines).
So I will create,
Company table and it will have a Company_Id.
.
Then I will create,
Site table and this table will have Site_Id and Company_Id for making relationship between Site Table and Company Table
.
After that when I create Line Table should I user both Company_Id
and Site_Id?
I know I can use only Site_Id and by query I can get the Site which Company belongs to. But what is the best practice? Should I use every Table's Id or I just use Previous Table's Id?
And also provide the class if anyone can.
No, you shouldn't have every table in a hierarchy having every ID from every table above it, because we can use joins to link the tables together in the entire hierarchy chain.
There may be a very limited number of situations where it's specifically advantageous to have a lower level table have the ID of one much further above it, embedded within it but it's typically a developer convenience, when they think "I can't be bothered joining these 27 tables together every time I want to know which machine belongs to which company. I'll just have a companyid in the machine table and I promise I'll keep it updated by some complicated mechanism"..
.. Don't do it.. when you sell a site to another company you have to remember to transfer all the machines to them too, not just by selling the site, but visiting every machine and updating its company ID, otherwise the hierarchy gets messed up
What's the alternative, if your front end app will be querying a million times a second which machines belong to which company, and you don't want the database to have to join 27 tables together, a million times a second, to find this out? Caching; a separate system where you maintain a transient list of machines and companies. Every time you sell something or make a transfer, you invalidate the cache when you update the part of the database hierarchy. Upon next query, the cache misses and shall be rebuilt with the new info. The database only occasionally has to join 27 tables
This is starting to head into an opinion piece, and hence heading out of scope of a SO question/answer, but if you come up against specific problems as you implement your system, feel free to post them up
Ps: don't prefix your tables with tbl; it's obvious what they are. The days of having to give everything a name that included the type of thing it was have thankfully long gone
I'm really looking for advice here on best practices so I will explain the situation. We have a fairly large application built on top of POCO and EF 4 with a complicated database. While we have been happy with Entity Framework there are definite performance improvements to be made for example with the following scenario (quite simplified).
We have a table called News which has a collection of users that have added it to their favourites and a collection of ratings (1 - 5) by users for example:
public class News
{
public virtual int NewsId;
public virtual string Title;
.......etc....
public virtual ICollection<User> UserFavourites { get; set; }
public virtual ICollection<Rating> Ratings { get; set; }
}
We have written a stored procedure which returns news for a user and allows us to return whether it is a favourite and whether it has already been rated by the user we are requesting the data for and the current rating for News rather than use EF to build this data from the ICollections and we end up with an object like below.
public class NewsDataModel
{
public int NewsId;
public string Title;
.......etc....
public bool IsFavourite { get; set; }
public bool IsRated { get; set; }
public double Rating { get; set; }
}
The stored procedure is much faster and a single database hit rather than EF with Lazy Loading which could be multiple calls but the data returned by the sproc does not match the POCO class for news which is above.
We have been trying to workout the best way to move forward with this as we have a INewsRepository which can either return the entity framework related class or the custom DataModel class we are populating with a stored procedure and ADO.NET. This doesn't feel right and I would appreciate any advice or insight from others experience about the best way to handle these scenarios when you want a single object with data built from multiple tables which would be a lot faster with a sproc than an entity framework call with lazy loading enabled.
Many thanks for any help
There is nothing wrong with a new method on your repository returning instances of NewsDataModel - it is still in the scope of your INewsRepository because it is data class constructed from news information. Otherwise you will have repository for every data model you defined.
Lets assume this hierarchy.
public class A
{
public int Id { get; set; }
public IEnumerable<B> Children { get; set; }
}
public class B
{
public A Parent { get; set; }
public string Name { get; set; }
}
I've created a app in WinForms and for database communication I use Fluent NHibernate. Multiple users can use the app simultaneously, however when a user decides to work with a specific instance of A (or a specific row in the database), I want to make that row non-accesible for other users. And when the user is finished working with that specific row, I want to make it accesible again. Can this be accomplished?
The idea is that a user fetches A and then works with some or all of its Children (List of B). At that stage, A is locked so that no other user can work with the children of A. When the user is finished working A is released and accesible to other users.
Fluent NHibernate is about your mapping. There are several ways to do data access with NHibernate. You do not specify which you use, but either way, the same principles apply.
What you describe is pessimistic locking. The way to do it in NHibernate is described here. I think you'd want to use LockMode.UPGRADE_NOWAIT.
If B-s can only be accessed through A it would indeed be sufficient to lock A.