I have a User model which I want to add a friends property to. Friends, are supposed to be other Users.
I created the UserFriendsPivot:
final class UserFriendsPivot: MySQLPivot, ModifiablePivot {
var id: Int?
var userID: User.ID
var friendID: User.ID
typealias Left = User
typealias Right = User
static var leftIDKey: WritableKeyPath<UserFriendsPivot, Int> {
return \.userID
}
static var rightIDKey: WritableKeyPath<UserFriendsPivot, Int> {
return \.friendID
}
init(_ user: User, _ friend: User) throws {
self.userID = try user .requireID()
self.friendID = try friend.requireID()
}
}
extension UserFriendsPivot: Migration {
public static var entity: String {
return "user_friends"
}
}
I added the friends property to User:
var friends: Siblings<User, User, UserFriendsPivot> {
return siblings()
}
Now, I'm seeing the following error on the line with return siblings():
Ambiguous use of 'siblings(related:through:)'
I tried to replace it with:
return siblings(related: User.self, through: UserFriendsPivot.self)
...without any luck.
I know that the two code snippets should work, because I straight-up copied them from another siblings relationship I built between Event and User that is working just fine.
The only difference I'm seeing is that I'm trying to build a relationship between the same models.
What can I do?
Try replacing your friends definition with something like:
var friends: Siblings<User,UserFriendsPivot.Right, UserFriendsPivot> {
return User.siblings()
}
EDIT:
It ought to work with Left and Right as the same table, but seems to fail because the aliases resolve to the base values. I.e. autocomplete in Xcode shows all the candidates for siblings all end up being of type:
Siblings<User, User, UserFriendsPivot> siblings(...)
Instead of:
Siblings<User, UserFriendsPivot.Right, UserFriendsPivot> siblings(...)
and similar.
I'd suggest raising a bug on GitHub. In the meantime, how about creating a copy of User with a different name and setting:
static let entity = "User"
to use the same physical table. Not pretty, but it might get you working.
The issue here is that in a same-Model (User-User) siblings relation, Fluent cannot infer which sibling you are referring to – the sides need to be specified.
extension User {
// friends of this user
var friends: Siblings<User, User, UserFriendsPivot> {
siblings(UserFriendsPivot.leftIDKey, UserFriendsPivot.rightIDKey)
}
// users who are friends with this user
var friendOf: Siblings<User, User, UserFriendsPivot> {
siblings(UserFriendsPivot.rightIDKey, UserFriendsPivot.leftIDKey)
}
}
The other same-Model consequence is that you will not be able to use the attach convenience method to add to the pivot table, and need to manually create instead:
let pivot = try UserFriendsPivot(user, friend)
pivot.save(on: req)
(There are other approaches to work around this, I just find these straightforward ways above the easiest to use. Specifying the sides and reversing the key positions to obtain the inverse relation are the important concepts.)
as answered by grundoon
Related
What is the most sensible way to save an entry from a public to private database using CloudKit
There is a similar question answered: here, but I didn't quite understand the solution in those words. I don't necessarily need code answers (though always appreciated), but the linked question confused me here:
That recordType would then have a field of type CKReference in which you save your user ID
Does it mean public entry has an array of references to all the users who saved it?
Currently I'm just creating a brand-new entry in the users private database based on the public one they chose to save. When I fetch public items I first query the saved items and cross reference so I can e.g. set the 'star' button to filled or unfilled. It feels very verbose and wrong, I would like to know how people manage this.
Example:
An app with two tabs/views - 'public' and 'private'. The public view is a list of strings (from the public database) and a star icon showing whether the user saved it or not, and the private view shows entries that are saved.
Thanks
Yes, that's their solution. If you want to keep your own, just create a copy of the record in your private db. In my code I create models for each record type with;
let publicDB: CKDatabase
let privateDB: CKDatabase
var saveToDB : CKDatabase
then;
... other code retrieved record R from public DB
now;
saveToDB = privateDB
saveToDB.save(R, completionHandler:
{
(record, error) -> Void in
if (error != nil)
{
// ... error handling...
completion(error)
}
else
{
completion(nil)
}
})
I have following entities:
abstract class User
{
string Id
string Name
}
class UserA: User
{
string PropA
}
class UserB : User
{
string PropB
}
It is a good solution to have a unique create (post) with a dynamic parameter and instantiate the subclasses according to a property?
[HttpPost]
public IActionResult Create([FromBody]dynamic data)
{
if (data.PROP == null)
{
_context.Users.Add(new UserA(data.PropA));
}
else
{
_context.Users.Add(new UserB(data.PropB));
}
...
Don't use dynamic. I'm actually kind of surprised that works at all. Though there's no indication that you've actually tested this code yet, so perhaps it doesn't. The modelbinder needs to know a concrete type to bind to, so that it can determine how to map the values onto the destination instance. Without strong types, it can't do anything but make everything a string, since that is how it comes in the request body.
Anyways, for something like this, the correct approach is to use a view model. Your view model should contain all the properties for all the various possible derived types. Again, the modelbinder needs these to determine how to map the data from the request body over, so if a property doesn't exist, it will simply discard the associated data.
This is also why you cannot simply use the base class. If this were a normal method, you could do something like:
public IActionResult Create([FromBody]User data)
Then, inside, you could use pattern matching or similar to cast to the correct derived type. This works because ultimately, the object in memory would actually be an instance of something like UserA, and you're simply up-casting it to User. As a result, you can always cast it back to UserA. However, actions are different. What's coming in from the request is not an object instance. The modelbinder serves to create an object instance out of it, by inspecting the parameter it needs to bind to. If that parameter is of type User, then it will fill the properties on User, and discard everything else. As a result, the object in memory is just User, and there's no way to cast to something like UserA - at least in terms of having all the values that were actually posted for an instance of UserA being on the object.
Which brings us back to the view model:
public class UserViewModel
{
public string Id { get; set; }
public string Name { get; set; }
public string PropA { get; set; }
public string PropB { get; set; }
}
Then, have your action accept that as a param:
public IActionResult Create([FromBody]UserViewModel data)
Then, inside:
if (!string.IsNullOrWhiteSpace(data.PropA))
{
// UserA was posted, map data to an instance of UserA
}
Similarly for UserB. If you like, you could also post an explicit "type" along with the data and switch on that to instantiate the right type. It's up to you. To reduce code duplication, you can instantiate the right type, but store it in an variable of type User. Then, if you need to get back at the correct type, you can use pattern matching:
User user;
switch (data.Type)
{
case "UserA":
user = new UserA
{
Id = data.Id,
Name = data.Name,
PropA = data.PropA
};
break;
// etc.
default:
user = new User
{
Id = data.Id,
Name = data.Name
};
break;
}
Then later:
switch (user)
{
case UserA userA:
// do something specific with `userA`
// etc.
}
Or:
if (user is UserA userA)
{
// do something with `userA`
}
How can I include a related entity, but only select the top 1?
public EntityFramework.Member Get(string userName)
{
var query = from member in context.Members
.Include(member => member.Renewals)
where member.UserName == userName
select member;
return query.SingleOrDefault();
}
According to MSDN:
"Note that it is not currently possible to filter which related entities are loaded. Include will always bring in all related entities."
http://msdn.microsoft.com/en-us/data/jj574232
There is also a uservoice item for this functionality:
http://data.uservoice.com/forums/72025-entity-framework-feature-suggestions/suggestions/1015345-allow-filtering-for-include-extension-method
The approach to use an anonymous object works, even though it's not clean as you wish it would be:
public Member GetMember(string username)
{
var result = (from m in db.Members
where m.Username == username
select new
{
Member = m,
FirstRenewal = m.Renewals.FirstOrDefault()
}).AsEnumerable().Select(r => r.Member).FirstOrDefault();
return result;
}
The FirstRenewal property is used just to make EF6 load the first renewal into the Member object. As a result the Member returned from the GetMember() method contains only the first renewal.
This code generates a single Query to the DB, so maybe it's good enough for You.
I have two domain classes. One is :
class User {
String login
String password
String firstName
String lastName
String address
String email
static constraints = {
login blank:false, size:5..15,matches:/[\S]+/, unique:true
password blank:false, size:5..15,matches:/[\S]+/
firstName blank:false
lastName blank:false
email email: true
}
}
And other is
class AddWebsite {
String website
User user
static constraints = {
website blank:false
website(unique: ['user'])
}
}
I am working with MongoDB at the backend. I need that for a particular login value, all siteURL values should be unique. Ex: login = abc#gmail.com. Then this user can have all unique url only in the database. But same urls can exist for different users. How do I do that using the unique constraint or any other approach?
Use embedded sub-documents to store SiteURL instances right inside the User. Then you define the collection to be a Set, which makes sure, all it's entries are unique. If you want to use the default mongo collection types or want to persist the order, define an interceptor like:
def beforeSave = {
urls = urls.unique()
}
UPDATE:
If your urls are plain strings, use the default primitive collection (no hasMany):
class User {
String login
//...
Set urls = new HashSet()
}
In this case you should be able to place unique constraint on the AddWebsite domain class such as this:
class AddWebsite {
String website
User user
static constraints = {
website(blank:false, unique: ['user'])
}
}
This will ensure that each website is unique in the database per user. Notice that multiple constraints are applied to the property website.
edited to match updated question.
It finally worked. I was getting the user cannot be null error while entering the website though it was not being validated in the AddWebsite domain class. I made the following changes and got it to work:
class AddWebsite{
String website
User user
static belongsTo = [user: User]
static constraints = {
website( url:true, unique: ['user'])
}
}
And in my controller also, I set the value of the user object to the session variable:
def addWebsites() {
if(request.method == 'POST') {
def w = new AddWebsite()
w.properties[
'website'
] = params
w.user = session["user"] //modified to make it work
if(w.save()) {
render view:'addWebsites', model:[message: "Successfully saved"]
}
else {
return [addWebsite:w]
}
}
Hope it helps someone :)
I have the following class hierarchy:
public class RealPeople { }
public class Users : RealPeople { }
public class People : RealPeople { }
In my dbContext, I defined a dbSet for RealPeople and on the OnModelCreating procedure, I specified separated tables for People and Users:
modelBuilder.Entity<Users>().ToTable("Users");
modelBuilder.Entity<People>().ToTable("People");
This creates the corresponding full hierarchy in my DB, with the 3 corresponding tables.
The problem comes when I want to retrieve the list of Users in my DB.
This:
List = (from Reg in PersistentMgr.RealPeople select (Users)Reg)
.ToList();
or this:
List = (from Reg in PersistentMgr.RealPeople select (Users)((RealPeople)Reg))
.ToList();
Throws an exception:
LINQ only being able to cast primitive model types.
So the thing is, I can't cast RealPeople to the corresponding subclass Users.
Any ideas on this one?
The way to get a collection of subclasses is using OfType:
var users = (from p in PersistentMgr.RealPeople select p).OfType<User>();
Try this instead:
var list = PersistentMgr.RealPeople.Select(reg => reg as Users).ToList();
better:
var list = PersistentMgr.RealPeople.Select(reg => (reg is Users) ? reg as Users : null).ToList();
You will get the same error if you try this:
var realperson = new RealPeople();
var user = (Users) realperson;
The reason is because the compiler doesn't know how to convert complex types into their subtypes by simple casting - so you need to use the as keyword instead. This will either return null, or the supertype casted into the subtype.
var realperson = new RealPeople();
var user = realperson as Users; // user is realperson converted into a Users object
var aString = "this is a string";
var otheruser = aString as Users; // otheruser is null, because aString was not a valid supertype for Users