I´m new to the OO-Stuff. What I want is follwing:
A class, that contains some properties and additional a list of classes. Here a pseudo code:
[User]
[Firsname]
[Lastname]
["List of Photos"]
[Photo-1]
[Photo-2]
[Photo-n]
I have a class "User" defined (wihtout my "list of photos"):
public class user
{
string Firstname { get; set; }
string Lastname { get; set; }
}
And I have a class "Photos":
public class photos
{
string filename { get; set; }
datetime timestamp { get; set; }
}
How can I put "photos" into user, so that I can add and retrieve n photos from a user?
Use List:
public class user
{
string Firstname { get; set; }
string Lastname { get; set; }
List<photos> photos{get;set;}
}
Note: photos is not good for naming please rename photos to photo
for use:
var u=new user();
u.photos=new List<photos>();
u.photos.Add(//add photo)
Also you can use:
public class user
{
string Firstname { get; set; }
string Lastname { get; set; }
List<photos> photos{get;set;};
public User()
{
photos= new List<photos>();
}
}
and for use:
var u=new user();
u.photos.Add(//add photo)
Also for naming you can use this.
Collection properties should almost universally be read-only. As such, your classes should look like this:
public class Photo
{
public string FileName { get; set; }
public DateTime TimeStamp { get; set; }
}
public class User
{
private readonly List<Photo> _photos = new List<Photo>();
public string GivenName { get; set; }
public string FamilyName { get; set; }
public List<Photo> Photos
{
get
{
return this._photos;
}
}
}
If you want lazy-loading then the User class should look like this:
public class User
{
private List<Photo> _photos;
public string GivenName { get; set; }
public string FamilyName { get; set; }
public List<Photo> Photos
{
get
{
return this._photos ?? (this._photos = new List<Photo>());
}
}
}
You could also do away with the field and use a private setter:
public class User
{
public string GivenName { get; set; }
public string FamilyName { get; set; }
public List<Photo> Photos { get; private set; }
public User()
{
this.Photos = new List<Photo>();
}
}
In each case, code outside the User class cannot assign directly to the Photos property, meaning that the existing collection cannot be replaced. You can only get the collection from the property and Add, Remove or whatever. That's how collection properties work throughout the Framework, e.g. DataSet.Tables, DataTable.Columns, DataTable.Rows, Control.Controls, ComboBox.Items, ListView.Items, etc, etc, and the list goes on.
Related
I'm about to create an ASP.Net Web Application with MVC and EF for the following scenario:
Every month there are new people entering a department in our company. Until now we're using an Excel Spreadsheet to handle common "Workitems" such as granting filesystem permissions etc.
Now I want to handle those requests using a Webapplication but I'm stuck at creating the Model.
There are two different Requesttypes, "JoinRequest" and "ChangeRequest" for people joining the company and employees changing the department. For each request type there are different workitems defined. Those workitems should be displayed in the webapp when the corresponding request is selected. After the data is loaded the user has to enter the data for the workitems.
E.g.
[JoinRequest]
Add new Filesystem Access permission:
[ UNC Path ] [x] Read [ ] Read & Write
Here the user has to enter the UNC Path and the type of Accesslevel (R or RW).
For now I've got the following Model:
public abstract class DbItem {
[Key]
public int Id { get; set; }
public DateTime CreationDate { get; set; }
public DateTime? DeletionDate { get; set; }
public string CreatedBy { get; set; }
public string DeletedBy { get; set; }
public bool IsDeleted => DeletionDate != null;
}
public abstract class Request : DbItem {
public Department NewDepartment { get; set; }
public DateTime BeginDate { get; set; }
public DateTime? EndDate { get; set; }
public ContractType ContractType { get; set; }
public EmployeeType EmployeeType { get; set; }
public string Location { get; set; }
public string ContactUserId { get; set; }
[NotMapped]
public User Contact {
get {
if (!string.IsNullOrWhiteSpace(ContactUserId)) {
var userSearcher = new ActiveDirectoryUserSearcher();
return userSearcher.FindByUserID(ContactUserId);
}
return null;
}
}
public string Phonenumber { get; set; }
//public virtual List<RequestWorkItem> RequestWorkItems { get; set; }
public bool IsFinished { get; set; }
public abstract string GetRequestType();
}
public abstract class WorkItem : DbItem {
[Key]
[ForeignKey("Language")]
public int LanguageId { get; set; }
[NotMapped]
public virtual Language Language { get; set; }
public string WorkItemText { get; set; }
public virtual Category Category { get; set; }
}
public class JoinRequest : Request {
public Person Person { get; set; }
public override string GetRequestType() {
return "New";
}
}
public class FileSystemAccessWorkItem : AccessWorkItem {
public FileSystemAccessRight FileSystemAccessRight { get; set; }
public string FileSystemPath { get; set; }
}
So when a user visits the website a new request has to be created but for every request type a set of workitems has to be loaded from the database. Additionally for every workitem the data the user entered should be saved somehow.
I hope you understand what I'm looking for - If anything is unclear I'll do my best to explain it more precisely
Edit1:
When looking at my Models I think the only think I achived is defining my Requests (so e.g. a JoinRequest contains some specific WorkItems) but how can I achive that a user is able to use this "RequestTemplate" to create a new Request and fill in the data to the corresponding workitems. Do I need additional Models for that? Maybe an example?
My POCO objects;
public class Chest
{
public Chest()
{
this.KeyIds = new List<string>();
this.Keys = new List<Page>();
}
public string Id { get; set; }
public string Name { get; set; }
[JsonIgnore]
public IList<Key> Keys { get; set; }
public IList<string> KeyIds { get; set; }
}
public class UserKey
{
public string UserName { get; set; }
public string Id { get; set; }
public UserKey()
{
this.KeyIds = new List<string>();
}
public IList<string> KeyIds { get; set; }
}
I want the chests which my user keys has.
For example: User has KeyIds=1,2,3
And Has Chests like:
Chest1: Name:Hollywood, KeyIds=1
Chest2:Name:Mollywood, KeyIds=2
Chest3:Name:Barcelona, KeyIds=1,2
Chest4:Name:Madrid, KeyIds=1,2,3
Chest5:Name:Dortmund, KeyIds=4
Chest6:Name:Milano, KeyIds=4,5
I will give the username as parameter to query and result should return me
Chest1, Chest2, Chest3, Chest4 objects.
Thanks. Regards
Load the user instance, and then get the keys that it has.
Then do an In query on the keys.
I'm trying to query SalesForce using the Force.com Toolkit for .NET. After playing around with the example code I noticed you can map the result of a query to an object as so:
private class Account
{
public const String SObjectTypeName = "Account";
public String Id { get; set; }
public String Name { get; set; }
}
Where you can assign the query result as such:
var results = await client.QueryAsync<Account>(qry);
What I'm wondering about is how I map a Contacs object within the account object if I have something like this:
private class Account
{
public const String SObjectTypeName = "Account";
public String Id { get; set; }
public String Name { get; set; }
public List<Contact> contacs{get;set;}
}
private class Account
{
public const String SObjectTypeName = "Contacs";
public String Id { get; set; }
public String Name { get; set; }
}
And my query looks like this:
Select id,Name, (Select, id, name From Contacs) from Account
What I'm expecting is a list of contacts related to the Account object.
This should do the trick....
private class Account
{
public const String SObjectTypeName = "Account";
public String Id { get; set; }
public String Name { get; set; }
public ContactsResult Contacts { get; set; }
}
private class ContactsResult
{
public Contacts[] Records { get; set; }
}
private class Contacts
{
public String Id { get; set; }
public String Name { get; set; }
}
Additionally you can always cast the results to an object which will return the json result and you can use a tool such as http://json2csharp.com/ to create the model for you.
UPDATED: I understood I should not use a DbSet so I changed the implementation to an ICollection as suggested by Erenga
Please consider the following classes:
[Table("Tenant")]
public class Tenant : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
[Key]
public string Guid { get; set; }
public virtual ICollection<User> Users { get; set; }
}
[Table("User")]
public class User : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
}
The first test creates a new Tenant and a new User and stores them in the appropriate tables.
[Test]
public void CreateNewUserForNewTenant()
{
var user = _applicationContext.Users.Create();
user.Name = "barney";
user.EmailAddress = "barney#flinstone.com";
var tenant = _applicationContext.Tenants.Create();
tenant.Name = "localhost";
tenant.Guid = Guid.NewGuid().ToString();
tenant.Users.Add(user); // NullReferenceException, I expected the EF would LazyLoad the reference to Users?!
_tenantRepository.Add(tenant);
_applicationContext.SaveChanges();
}
This test will fail on a NullReferenceException since the property Users is not initialized.
How should I change my code that I can rely on LazyLoading provided with EF?
There are 2 problems I see here.
As #SimonWhitehead mentioned, reference types are initialized as null by default. Lazy loading works only on entities created by EF. These are actually sub classes of your class that contain addtional logic to lazy load.
DbSet is not a collection type that is supported on entities. You need to change the type to ICollection, ISet, or IList.
Here's a working example
[Table("Tenant")]
public class Tenant : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
[Key]
public string Guid { get; set; }
public virtual ICollection<User> Users { get; set; }
}
[Table("User")]
public class User : IEntity
{
public int Id { get; set; }
public string Name { get; set; }
public string EmailAddress { get; set; }
public string Password { get; set; }
}
[Test]
public void CreateNewUserForNewTenant()
{
var user = _applicationContext.Users.Create();
user.Name = "barney";
user.EmailAddress = "barney#flinstone.com";
var tenant = _applicationContext.Tenents.Create();
tenant.Name = "localhost";
tenant.Guid = Guid.NewGuid().ToString();
tenant.Users = new List<User> { user };
_tenantRepository.Add(tenant);
_applicationContext.SaveChanges();
}
var tenant = new Tenant
{
Name = "localhost",
Guid = Guid.NewGuid().ToString(),
Users = new List<User> { user }
};
I think you were expecting something like this (not threadsafe):
[Table("Tenant")]
public class Tenant : IEntity
{
private DbSet<User> _users;
public int Id { get; set; }
public string Name { get; set; }
[Key]
public string Guid { get; set; }
public virtual ICollection<User> Users
{
get
{
if (_users == null)
_users = new List<Users>();
return _users;
}
set { _users = value; }
}
}
I'm sure the Lazy<T> class could be utilised somehow too but I am not familiar with that class.
I have a Model like this
public class Challenge
{
public int ID { get; set; }
public string Name { get; set; }
public string Blurb { get; set; }
public int Points { get; set; }
public string Category { get; set; }
public string Flag { get; set; }
public List<string> SolvedBy { get; set; }
}
public class ChallengeDBContext : DbContext
{
public DbSet<Challenge> Challenges { get; set; }
}
and then Controller like this. But I cannot update the List "SolvedBy", the next time I step through with the debugger, the list is still empty.
[HttpPost]
public string Index(string flag = "", int id=0)
{
Challenge challenge = db.Challenges.Find(id);
if (flag == challenge.Flag)
{
var chall = db.Challenges.Find(id);
if (chall.SolvedBy == null)
{
chall.SolvedBy = new List<string>();
}
chall.SolvedBy.Add(User.Identity.Name);
db.Entry(chall).State = EntityState.Modified;
db.SaveChanges();
//congrats, you solved the puzzle
return "got it";
}
else
{
return "fail";
}
}
is there any way around it to make a list of strings kept in the database?
EF don't know how to store an array in database table so it just ignore it. You can create another table/entity or use XML/JSON to store the list. You can serialize the list before saving and deserialize it after loading from database
A List<T> in a model would normally map to a second table, but in your DbContext you only have a single table. Try adding a second table.
public class ChallengeDBContext : DbContext
{
public DbSet<Challenge> Challenges { get; set; }
public DbSet<Solution> Solutions {get; set;}
}
public class Challenge
{
public int ID { get; set; }
public string Name { get; set; }
public string Blurb { get; set; }
public int Points { get; set; }
public string Category { get; set; }
public string Flag { get; set; }
public List<Solution> SolvedBy { get; set; }
}
public class Solution
{
public int ID { get; set; }
public string Name { get; set; }
}
Then your controller can use code along the lines of...
var chall = db.Challenges.Find(id);
if (chall.SolvedBy == null)
{
chall.SolvedBy = new List<Solution>();
}
chall.SolvedBy.Add(new Solution {Name=User.Identity.Name});
None of the above has been tested and I may have made some mistakes there, but the general principle I want to illustrate is the fact that you need another table. The List<T> represents a JOIN in SQL.