Should I Be Using Protocols for these Models? - swift-protocols

I am working with the Dribbble API, and there are three models that I am currently using:
User
Team
Shot
A Shot is something that a User or Team uploads to their respective portfolio to be shown off. This can be an image or an animation. A User can belong to multiple Teams, while a Team can have multiple Users.
The three models each have many properties, which I have provided below.
User takes the following structure:
/// Encapsulates information about a Dribbble User.
public struct User {
// MARK: - Properties
/// The identifier of the User.
public var identifier: Int?
/// The name of the User.
public var name: String?
/// The username of the User.
public var username: String?
/// The `URL` for the User's Dribbble page.
public var htmlURL: URL?
/// The `URL` for the User's avatar image.
public var avatarURL: URL?
/// The biography for the User.
public var biography: String?
/// The location for the User.
public var location: String?
/// The `URL` for the User's website.
public var websiteURL: URL?
/// The `URL` for the User's Twitter profile.
public var twitterURL: URL?
/// The number of Buckets for the User.
public var bucketsCount: Int?
/// The number of Comments the User has received.
public var commentsReceivedCount: Int?
/// The number of followers for the User.
public var followersCount: Int?
/// The number of followings for the User.
public var followingsCount: Int?
/// The number of Likes for the User.
public var likesCount: Int?
/// The number of Likes received for the User.
public var likesReceivedCount: Int?
/// The number of projects for the User.
public var projectsCount: Int?
/// The number of Rebounds received for the User.
public var reboundsReceivedCount: Int?
/// The number of Shots for the User.
public var shotsCount: Int?
/// The number of Teams that the User belongs to.
public var teamsCount: Int?
/// Whether the User is authorized to upload Shots.
public var canUploadShot: Bool?
/// The type of User.
public var type: String?
/// Whether the User has Pro status.
public var isPro: Bool?
/// The `URL` for the User's Buckets.
public var bucketsURL: URL?
/// The `URL` for the User's followers.
public var followersURL: URL?
/// The `URL` for the User's followings.
public var followingURL: URL?
/// The `URL` for the User's Likes.
public var likesURL: URL?
/// The `URL` for the User's Projects.
public var projectsURL: URL?
/// The `URL` for the User's Shots.
public var shotsURL: URL?
/// The `URL` for the Teams that the User belongs to.
public var teamsURL: URL?
/// The `Date` that the User was created.
public var createdAt: Date?
/// The `Date` that the User was last updated.
public var updatedAt: Date?
}
Team takes the following structure:
/// Encapsulates information about a Dribbble Team.
public struct Team {
// MARK: - Properties
/// The identifier of the Team.
public var identifier: Int?
/// The name of the Team.
public var name: String?
/// The username of the Team.
public var username: String?
/// the `URL` for the Team's Dribbble page.
public var htmlURL: URL?
/// The `URL` for the Team's avatar image.
public var avatarURL: URL?
/// The biography for the Team.
public var biography: String?
/// The location for the Team.
public var location: String?
/// The `URL` for the Team's website.
public var websiteURL: URL?
/// The `URL` for the Team's Twitter profile.
public var twitterURL: URL?
/// The number of Buckets for the Team.
public var bucketsCount: Int?
/// The number of Comments the Team has received.
public var commentsReceivedCount: Int?
/// The number of followers for the Team.
public var followersCount: Int?
/// The number of followings for the Team.
public var followingsCount: Int?
/// The number of Likes for the Team.
public var likesCount: Int?
/// The number of Likes received for the Team.
public var likesReceivedCount: Int?
/// The number of projects for the Team.
public var projectsCount: Int?
/// The number of Rebounds received for the Team.
public var reboundsReceivedCount: Int?
/// The number of Shots for the Team.
public var shotsCount: Int?
/// Whether the Team is authorized to upload Shots.
public var canUploadShot: Bool?
/// The type of Team.
public var type: String?
/// Whether the Team has Pro status.
public var isPro: Bool?
/// The `URL` for the Team's Buckets.
public var bucketsURL: URL?
/// The `URL` for the Team's followers.
public var followersURL: URL?
/// The `URL` for the Team's followings.
public var followingURL: URL?
/// The `URL` for the Team's Likes.
public var likesURL: URL?
/// The `URL` for the Team's Projects.
public var projectsURL: URL?
/// The `URL` for the Team's Shots.
public var shotsURL: URL?
/// The `Date` that the Team was created.
public var createdAt: Date?
/// The `Date` that the Team was last updated.
public var updatedAt: Date?
/// The number of members for the Team.
public var membersCount: Int?
/// The `URL` for the members of the Team.
public var membersURL: URL?
/// The `URL` for the Team Shots.
public var teamShotsURL: URL?
}
Shot takes the following structure:
/// Encapsulates information about a Dribbble Shot.
public struct Shot {
// MARK: - Properties
/// The identifier of the Shot.
public var identifier: Int?
/// The title of the Shot.
public var title: String?
/// The description of the Shot.
public var description: String?
/// The size of the Shot.
public var size: CGSize?
/// The `URL` for the Shot's high-definition image.
public var highDefinitionImageURL: URL?
/// The `URL` for the Shot's normal image.
public var normalImageURL: URL?
/// The `URL` for the Shot's teaser image.
public var teaserImageURL: URL?
/// The number of views for the Shot.
public var viewsCount: Int?
/// The number of likes for the Shot.
public var likesCount: Int?
/// The number of comments for the Shot.
public var commentsCount: Int?
/// The number of attachments for the Shot.
public var attachmentsCount: Int?
/// The number of Rebounds for the Shot.
public var reboundsCount: Int?
/// The number of Buckets for the Shot.
public var bucketsCount: Int?
/// The `Date` that the Shot was created.
public var createdAt: Date?
/// The `Date` that the Shot was last updated.
public var updatedAt: Date?
/// The `URL` for the Shot's Dribbble page.
public var htmlURL: URL?
/// The `URL` for the Shot's attachments.
public var attachmentsURL: URL?
/// The `URL` for the Shot's Buckets.
public var bucketsURL: URL?
/// The `URL` for the Shot's comments.
public var commentsURL: URL?
/// The `URL` for the Shot's Likes.
public var likesURL: URL?
/// The `URL` for the Shot's Projects.
public var projectsURL: URL?
/// The `URL` for the Shot's Rebounds.
public var reboundsURL: URL?
/// Whether the Shot is animated.
public var isAnimated: Bool?
/// The tags associated with the Shot.
public var tags: Array<String>?
/// The `User` that the Shot belongs to.
public var user: User?
/// The `team` that she Shot belongs to.
public var team: Team?
}
User and Team have many duplicated properties, while Shot has a few that User and Team have. If I take the protocol route for these models, there will be many protocol implementations.
In my case, I have no reason to have an Array of Identifiable types or WebsiteURL types, or any other type that conforms to a protocol for the duplicated properties. I will have an Array of Shots, which belong to either Users or Teams. I will also have Arrays for Users and Arrays for Teams.
Should I implement protocols for the duplicated properties and have User, Team, and Shot conform to them, or would this lead to protocol hell for no apparent benefit other than being so-called "Swifty"?

So what about using inheritance like this,
Common parent takes the properties Shot, User and Team have and Parent for user and team just take the properties only user and team have
Common parent
|
-----------------------------------------------
| |
Parent for user and team |
| |
|-----------| |
User Team Shot

Related

Azure Mobile Service “Cannot insert if the id member is already set.”

I am building an Azure Mobile Service and am attempting to use Entity Framework Code First. I am having my database entities inherit from the Microsoft.WindowsAzure.Mobile.Service.EntityData class which includes an Id string property as the primary key. I want to be able to pass a value for the Id on an insert, but I get the response "Cannot insert if the id member is already set." If I do not pass a value for the Id, the insert is successful and the row has a GUID Id value.
I have tried using fluentAPI to specify that the Id column on this table is not to be generated by the database in the OnModelCreating method of the DbContext:
modelBuilder.Entity<User>().Property(p => p.Id).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.None);
I have also tried the attribute decoration way, by rebuilding the DataEntity class as I needed it without any luck
public class User : ITableData
{
public string EmailAddress { get; set; }
public string UserName { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Index(IsClustered = true)]
[TableColumn(TableColumnType.CreatedAt)]
public DateTimeOffset? CreatedAt { get; set; }
[TableColumn(TableColumnType.Deleted)]
public bool Deleted { get; set; }
[Key]
[TableColumn(TableColumnType.Id)]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public string Id { get; set; }
[DatabaseGenerated(DatabaseGeneratedOption.Computed)]
[TableColumn(TableColumnType.UpdatedAt)]
public DateTimeOffset? UpdatedAt { get; set; }
[TableColumn(TableColumnType.Version)]
[Timestamp]
public byte[] Version { get; set; }
}
What am I missing here?
Testing the service directly I was able to insert Ids without a problem, so it turns out this was not an EF problem. The problem was the case sensitivity of the Azure Mobile Service Javascript client. Inserting with a specified value for the Id will only work if the JSON parameter for Id == 'id'. After dropping Id to all lowercase, everything is peachy.
If nothing else, I hope this saves someone some time someday.
#DevNoob, thanks for your investigation. I have filed an issue to track this bug: https://github.com/Azure/azure-mobile-apps-js-client/issues/96

Add/insert a new entry containing complex type in Entity Framework 7 Code first

I am trying to add a new entry for my object model which has few complex type, using EF 7 code first. But it's not working and throwing exception.
Model:
public class Student
{
public int id {get; set;}
public string Name {get; set;}
public Address Address {get; set;} -> complex type
}
public class Address
{
public int Id {get;set;}
public string Location {get;set;}
}
Code first code:
class SchoolContext {
DbSet<Student> Students {get; set;}
DbSet<Address> Addresses {get; set;}
}
var context = new SchoolContext();
context.Students.Add(new Student { Id = 1, Name = "XYZ", Address = new Address { Id = 2, Location = "US" } } -> The add method has second parameter which says include dependent objects, and by default it is true.
context.SaveChanges();
This throws exception:
{"The INSERT statement conflicted with the FOREIGN KEY constraint \"FK_Student_Address_AddressId\". The conflict occurred in database \"School\", table \"dbo.Address\", column 'Id'.\r\nThe statement has been terminated."}
This error I think means, the Address object does not exists in database, and if I add the address first and then Student, it works. But this is just too much code, as in my real application objects has many complex types.
Ideally this should work, but it does not.
Based on this bug report, you should add item before.
It is considered as-designed. At least until RC2.

Automapper with Column Order Data Annotation

I am having an issue on application start up when I setup my AutoMapper config. It is throwing an exception when creating the mapping for a business object to a data object. The issue appears to be coming from the use of data annotations. It is worth mentioning that the mapping from data object to business object works just fine.
The exception that I get is a CustomAttributeException:
'Order' property specified was not found.
AutoMapper.config Mapping:
Mapper.CreateMap<Note, NoteData>();
The database object is defined as:
public class NoteData
{
[Key]
[Column(Order = -1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public virtual Guid Id { get; set; }
[Timestamp]
[ConcurrencyCheck]
[Column(Order = 999)]
public virtual byte[] Version { get; set; }
[Required]
public virtual DateTime Date { get; set; }
[Required]
[StringLength(500)]
public virtual string Value { get; set; }
public virtual bool IsDeleted { get; set; }
[Required]
public virtual UserData CreatedBy { get; set; }
}
I have tried ignoring the fields that have Column Order data annotations on them but that did not resolve the issue.
When I comment out the Order data annotations, Automapper has no issues. So my main question is there a way to configure AutoMapper to work with the Column Order data annotations?
Order can't be negative one. And since an answer needs at least 30 characters let me add MSDN's doc:
Gets or sets the zero-based order of the column the property is mapped to.

Map Many to Many relationship without navigation property

Is is possible to map a many to many relationship without having a navigation property on one of the ends? For example I have some widgets and some users who can star particular widgets. I'd like to be able to see what widgets a user cares stars, but I don't really care about seeing all the users who have starred a particular widget
Widget.cs
public int Id { get; set; }
public string Name { get; set; }
User.cs
public int Id { get; set; }
public string Username { get; set; }
public ICollection<Widget> StarredWidgets { get; set; }
With this setup, EF will generate a one-to-many relationship from Widgets to Users. However, it needs to be a many to many. I realize I could add a public ICollection<User> Users to Widget.cs, but just seeing if there was another way around this.
You can and this case must define the many-to-many relationship with Fluent API:
modelBuilder.Entity<User>()
.HasMany(u => u.StarredWidgets)
.WithMany() // <- no parameter here because there is no navigation property
.Map(m =>
{
m.MapLeftKey("UserId");
m.MapRightKey("WidgetId");
m.ToTable("UserWidgets");
});

EF Code First using non-Primary key to create one-to-many relationship

I have the following two entities:
public class User
{
public int PKID { get; set; }
public string Login { get; set; }
public string Username { get; set; }
public string Password { get; set; }
public virtual ICollection<AppAccess> AppAccessList { get; set; }
}
public class AppAccess
{
public int PKID {get; set; }
public string Login { get; set; }
public string AppCode { get; set; }
}
The PKID field is the primary key and identity column of each table but the Login field is what links them in a one-to-many relationship where the User is the principal entity.
How can I setup the navigation property AppAccessList using the fluent API, if it is even possible, to use the Login field as the relationship key? BTW, the entities are based on existing schema that cannot be refactored at this time.
I don't believe this is possible out of the box, certainly not the ability to add a navigation property. I tried using the new power tools to reverse engineer a database and it just ignored the relationship, neither could i create it using code (annotations or fluent interface).
You can create the relationship using raw sql on the on OnModelCreating method to make the constraint, but you'd need to use join's manually to navigate between the tables.

Resources