Entity Framework Insert object with related object - c#-4.0

I am a newbie with Entity Framework and I need to insert an object Comment that has a related FK object User into the database.
public Class Comment
{
public int CommentID { get; set; }
public string CommentContent { get; set; }
public virtual User User { get; set; }
public virtual DateTime CommentCreationTime { get; set; }
}
public class User
{
public int UserID { get; set; }
public string UserName { get; set; }
public string UserPassword { get; set; }
public string UserImageUrl{get; set;}
public DateTime UserCreationDate { get; set; }
public virtual List<Comment> Comments { get; set; }
}
public void AddComment()
{
User user = new User() { UserID = 1 };
Comment comment = new Comment() { CommentContent = "This is a comment", CommentCreationTime = DateTime.Now, User = user };
var ctx = new WallContext();
comments = new CommentsRepository(ctx);
comments.AddComment(comment);
ctx.SaveChanges();
}
Ideally, with T-SQL, if I know the PRIMARY KEY of my User object, I could just insert my Comment object and specify the PK of my 'User' in the insert statement.
I have tried to do the same with Entity Framework and it doesn't seem to work. It would be overkill to have to first fetch the User object from the database just to insert a new 'Comment'.
Please, how can I achieve this ?

You need to attach the user object to the context so that the context knows its an existing entity
public void AddComment()
{
var ctx = new WallContext();
User user = new User() { UserID = 1 };
ctx.Users.Attach(user);
Comment comment = new Comment() { CommentContent = "This is a comment", CommentCreationTime = DateTime.Now, User = user };
comments = new CommentsRepository(ctx);
comments.AddComment(comment);
ctx.SaveChanges();
}

Related

How To Update Properties In Domain Aggregate Root Object

In a clean architecture project the domain layer contains: DTO interfaces, Events, Factories, Models, Exceptions, etc...
Every domain object contains a constructor with arguments through which data is passed.
I am using factories which accept a DTO interface from which domain objects are created.
The data models in the infrastructure layer implement the DTO interfaces in the domain layer.
DTO:
namespace Acme.Core.Domain.Identity.DTO
{
public interface IBuyerDto : IPersonDto
{
IAddressDto BillingAddress { get; set; }
IAddressDto ShippingAddress { get; set; }
}
}
Domain Models:
namespace Acme.Core.Domain.Identity.Models.BuyerAggregate
{
public sealed class Buyer : Aggregate<Buyer, BuyerId>, IPerson
{
public Buyer(BuyerId id, PersonName name, DateOfBirth dateOfBirth, Gender gender, string pictureUrl, Address billingAddress, Address shippingAddress, Account account) : base(id)
{
Name = name;
DateOfBirth = dateOfBirth;
Gender = gender;
BillingAddress = billingAddress;
ShippingAddress = shippingAddress;
Account = Guard.Against.Null(account, nameof(account));
PictureUrl = pictureUrl;
}
public Account Account { get; private set; }
public PersonName Name { get; private set; }
public DateOfBirth DateOfBirth { get; private set; }
public string PictureUrl { get; private set; }
public Gender Gender { get; private set; }
public Address BillingAddress { get; private set; }
public Address ShippingAddress { get; private set; }
public void UpdateName(PersonName personName)
{
Name = personName;
}
public void UpdateBillingAddress(Address billingAddress)
{
BillingAddress = billingAddress;
}
public void UpdateShippingAddress(Address shippingAddress)
{
ShippingAddress = shippingAddress;
}
}
}
namespace Acme.Core.Domain.Identity.Models
{
public class Account : Entity<Account, AccountId>
{
public Account(AccountId id, string userName, string normalizedUserName, string passwordHash, string concurrencyStamp, string securityStamp, string email, string normalizedEmail, bool emailConfirmed, string phoneNumber, bool phoneNumberConfirmed, bool twoFactorEnabled, DateTimeOffset? lockoutEnd, bool lockoutEnabled, int accessFailedCount, AccountStatus status, List<RoleId> roles, List<AccountClaim> accountClaims, List<AccountLogin> accountLogins, List<AccountToken> accountTokens) : base(id)
{
UserName = Guard.Against.NullOrWhiteSpace(userName, nameof(userName));
NormalizedUserName = Guard.Against.NullOrWhiteSpace(normalizedUserName, nameof(normalizedUserName));
PasswordHash = Guard.Against.NullOrWhiteSpace(passwordHash, nameof(passwordHash));
ConcurrencyStamp = concurrencyStamp;
SecurityStamp = securityStamp;
Email = Guard.Against.NullOrWhiteSpace(email, nameof(email));
NormalizedEmail = Guard.Against.NullOrWhiteSpace(normalizedEmail, nameof(normalizedEmail));
EmailConfirmed = emailConfirmed;
PhoneNumber = phoneNumber;
PhoneNumberConfirmed = phoneNumberConfirmed;
TwoFactorEnabled = twoFactorEnabled;
LockoutEnd = lockoutEnd;
LockoutEnabled = lockoutEnabled;
AccessFailedCount = accessFailedCount;
Status = Guard.Against.Null(status, nameof(status));
_roles = Guard.Against.Null(roles, nameof(roles));
_accountClaims = accountClaims;
_accountLogins = accountLogins;
_accountTokens = accountTokens;
}
public string UserName { get; private set; }
public string NormalizedUserName { get; private set; }
public string PasswordHash { get; private set; }
public string ConcurrencyStamp { get; private set; }
public string SecurityStamp { get; private set; }
public string Email { get; private set; }
public string NormalizedEmail { get; private set; }
public bool EmailConfirmed { get; private set; }
public string PhoneNumber { get; private set; }
public bool PhoneNumberConfirmed { get; private set; }
public bool TwoFactorEnabled { get; private set; }
public DateTimeOffset? LockoutEnd { get; private set; }
public bool LockoutEnabled { get; private set; }
public int AccessFailedCount { get; private set; }
public AccountStatus Status { get; private set; }
private List<RoleId> _roles;
public IReadOnlyCollection<RoleId> Roles
{
get
{
return _roles;
}
}
private List<AccountClaim> _accountClaims;
public IReadOnlyCollection<AccountClaim> AccountClaims
{
get
{
return _accountClaims;
}
}
private List<AccountLogin> _accountLogins;
public IReadOnlyCollection<AccountLogin> AccountLogins
{
get
{
return _accountLogins;
}
}
private List<AccountToken> _accountTokens;
public IReadOnlyCollection<AccountToken> AccountTokens
{
get
{
return _accountTokens;
}
}
public void AddRole(long roleId)
{
var role = _roles.Where(x => x.GetValue().Equals(roleId)).FirstOrDefault();
if (role == null)
{
_roles.Add(new RoleId(roleId));
}
}
public void RemoveRole(long roleId)
{
var role = _roles.Where(x => x.GetValue().Equals(roleId)).FirstOrDefault();
if (role == null)
{
_roles.Remove(role);
}
}
public void ActivateAccount()
{
Status = AccountStatus.Active;
}
public void BanAccount()
{
Status = AccountStatus.Banned;
}
public void CloseAccount()
{
Status = AccountStatus.Closed;
}
public void LockAccount()
{
Status = AccountStatus.Locked;
}
public void NewAccount()
{
Status = AccountStatus.New;
}
}
}
Factories:
namespace Acme.Core.Domain.Identity.Factories
{
public class BuyerAggregateFatory : IBuyerAggregateFactory
{
private readonly IPersonNameFactory _personNameFactory;
private readonly IDateOfBirthFactory _dateOfBirthFactory;
private readonly IGenderFactory _genderFactory;
private readonly IAccountFactory _accountFactory;
private readonly IAddressFactory _addressFactory;
public BuyerAggregateFatory(IPersonNameFactory personNameFactory,
IDateOfBirthFactory dateOfBirthFactory,
IGenderFactory genderFactory,
IAccountFactory accountFactory,
IAddressFactory addressFactory)
{
_personNameFactory = Guard.Against.Null(personNameFactory);
_dateOfBirthFactory = Guard.Against.Null(dateOfBirthFactory);
_genderFactory = Guard.Against.Null(genderFactory);
_accountFactory = Guard.Against.Null(accountFactory);
_addressFactory = Guard.Against.Null(addressFactory);
}
public Buyer Create(IBuyerDto dto)
{
BuyerId aggregateId = new BuyerId(dto.Id);
PersonName name = _personNameFactory.Create(dto.Name);
DateOfBirth dob = _dateOfBirthFactory.Create(dto.DateOfBirth);
Gender gender = _genderFactory.Create(dto.GenderId);
Address billingAddress = _addressFactory.Create(dto.BillingAddress);
Address shippingAddress = _addressFactory.Create(dto.ShippingAddress);
Account account = _accountFactory.Create(dto.Account);
return new Buyer(aggregateId, name, dob, gender, dto.PictureUrl, billingAddress, shippingAddress, account);
}
}
}
From the application layer a service class does the orchestration for the use case, using the repository interface and factory interface.
Use case 1: During an update operation I fetch existing data of the aggregate, from the database using a repository. I need to update one or two properties of a domain aggregate root object. Example: I need to update billing address or shipping address.
Use case 2: During an update operation, I fetch existing data of the aggregate, from the database using a repository. I need to update the account status. I am calling the status update method from domain aggregate root object. Example: buyerAggregate.Account.ActivateAccount()
Am i updating the domain aggregate root object and its properties in right way?
In use case 2, your aggregate would be the Account, not the Buyer. There's no need for the Buyer to be involved in the transaction.
So, for this case, you would retrieve Account from the repository and then call ActivateAccount() directly.
Any aggregate that you have designed for a use case should provide the full interface for making changes to the aggregate. In other words, your application layer will only work with properties and methods on the aggregate root. If a child entity needs changing that method should be implemented on your aggregate root. You should not directly interact with child properties of an aggregate. It is the aggregate's responsibility to avoid any invariants within its scope. If you change a child object directly, you may put the whole aggregate in an invalid state because the aggregate was not able to enforce controls.

MVC inserting values into a ViewModel after Model to ViewModel mapping

I am working with two different databases. I am passing the Model collection (SQL Server) to a ViewModel collection. The ViewModel has extra properties which I access out of a Visual Fox Pro database. I am able to map the existing properties, but the ViewModel does not save the data after passing the values to it.
The WoCust and the Lname fields return null, but the rest of the properties which come from the original Model pass to the properties in the ViewModel fine.
When I debug at the rdr for the OleDbCommand, it shows that the ViewModel is receiving a value for both rdr[WoCust] and rdr[Lname].
How do I make it so the ViewModel saves the new values?
WOSchedule.cs...
public partial class WOSchedule
{
public long Id { get; set; }
public string WoNo { get; set; }
public Nullable<long> QuoteTypeId { get; set; }
public Nullable<int> PriorityNo { get; set; }
public bool Active { get; set; }
public Nullable<System.DateTime> WoDate { get; set; }
public Nullable<long> QuoteID { get; set; }
public Nullable<System.DateTime> WoDone { get; set; }
public Nullable<long> WOScheduleListId { get; set; }
public string StorageLocation { get; set; }
public virtual QuoteType QuoteType { get; set; }
public virtual Quote Quote { get; set; }
public virtual WOScheduleList WOScheduleList { get; set; }
}
WoWcheduleVM.cs...
public partial class WoScheduleVM
{
public long Id { get; set; }
public string WoNo { get; set; }
public Nullable<long> QuoteTypeId { get; set; }
public Nullable<int> PriorityNo { get; set; }
public bool Active { get; set; }
public DateTime? WoDate { get; set; }
public Nullable<long> QuoteID { get; set; }
public DateTime? WoDone { get; set; }
public Nullable<long> WOScheduleListId { get; set; }
public string StorageLocation { get; set; }
public string WoCust { get; set; } // extra property
public string Lname { get; set; } // extra property
public virtual QuoteType QuoteType { get; set; }
public virtual Quote Quote { get; set; }
public virtual WOScheduleList WOScheduleList { get; set; }
}
WOSchedulesController.cs
string cs = ConfigurationManager.ConnectionStrings["foxproTables"].ConnectionString;
OleDbConnection cn = new OleDbConnection(cs);
var wOSchedules = db.WOSchedules.Where(w => w.WoDone == null).Include(w => w.QuoteType);
var wOSchedulesVM = wOSchedules.Select(s => new ViewModels.WoScheduleVM()
{
Id = s.Id,
WoNo = s.WoNo,
QuoteTypeId = s.QuoteTypeId,
PriorityNo = s.PriorityNo,
Active = s.Active,
WoDate = s.WoDate,
QuoteID = s.QuoteID,
WoDone = s.WoDone,
WOScheduleListId = s.WOScheduleListId,
StorageLocation = s.StorageLocation
});
cn.Open();
foreach (var sch in wOSchedulesVM)
{
string conn = #"SELECT wo_cust, lname FROM womast INNER JOIN custmast ON womast.wo_cust = custmast.cust_id WHERE wo_no = '" + sch.WoNo + "'";
OleDbCommand cmdWO = new OleDbCommand(conn, cn);
OleDbDataReader rdr = cmdWO.ExecuteReader();
while (rdr.Read())
{
sch.WoCust = ((string)rdr["wo_cust"]).Trim();
sch.Lname = ((string)rdr["lname"]).Trim();
}
}
cn.Close();
return View(wOSchedulesVM.OrderByDescending(d => d.WoDate).ToList());
The problem is you're using foreach loop for iterating wOSchedulesVM collection, which renders the source collection immutable during iteration. The older documentation version explicitly explains that behavior:
The foreach statement is used to iterate through the collection to get
the information that you want, but can not be used to add or remove
items from the source collection to avoid unpredictable side effects. If you need to add or remove items from the source collection, use a for loop.
Therefore, you should use for loop to be able to modify property values inside that collection, as shown in example below:
using (var OleDbConnection cn = new OleDbConnection(cs))
{
cn.Open();
string cmd = #"SELECT wo_cust, lname FROM womast INNER JOIN custmast ON womast.wo_cust = custmast.cust_id WHERE wo_no = #WoNo";
// not sure if it's 'Count' property or 'Count()' method, depending on collection type
for (int i = 0; i < wOSchedulesVM.Count; i++)
{
var sch = wOSchedulesVM[i];
using (OleDbCommand cmdWO = new OleDbCommand(conn, cn))
{
cmd.Parameters.AddWithValue("#WoNo", sch.WoNo)
OleDbDataReader rdr = cmdWO.ExecuteReader();
if (rdr.HasRows)
{
while (rdr.Read())
{
sch.WoCust = (!rdr.IsDbNull(0)) ? rdr.GetString(0).Trim() : string.Empty;
sch.Lname = (!rdr.IsDbNull(1)) ? rdr.GetString(1).Trim() : string.Empty;
}
}
}
}
}
Note: This example includes 3 additional aspects, i.e. parameterized query, checking row existence with HasRows property and checking against DBNull.Value with IsDbNull().
Related issue: What is the best way to modify a list in a 'foreach' loop?

Azure, DocumentDB cannot save POCO object as a document

I have serious problem with MS Azure DocumentDB.
I Know it's in pre-realese only, but as I know it is said that it is possible to save Dynamic and POCO objects as a documents. When I save dynamic object it works just fine (object is added to a collection):
dynamic dynamicObject = new
{
testId = "12",
text = "aaa bbb ccc"
};
await client.CreateDocumentAsync(collectionSelfLink, dynamicObject);
However, when I try to add a POCO obejct, it doesnt works (nothing is added to a collection):
public class House
{
[JsonProperty(PropertyName = "id")]
public string HouseId { get; set; }
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "address")]
public string Address { get; set; }
}
------------------
House house = new House() {HouseId = "1", Name="TestHouse", Address="asdasdasd"};
await client.CreateDocumentAsync(collectionSelfLink, house);
Everything else is the same.
EDIT: temp solution is to inherit your POCO class from Microsoft.Azure.Documents.Document class.
as per my comment, can't repro this.
here is my test project:
static void Main(string[] args)
{
House house = new House() { HouseId = "1", Name = "TestHouse", Address = "asdasdasd" };
Document doc = client.CreateDocumentAsync(col.DocumentsLink, house).Result;
Console.Write(doc.SelfLink);
Console.ReadLine();
}
public class House
{
[JsonProperty(PropertyName = "id")]
public string HouseId { get; set; }
[JsonProperty(PropertyName = "name")]
public string Name { get; set; }
[JsonProperty(PropertyName = "address")]
public string Address { get; set; }
}
results in 1 document created and doc.selfLink being printed to the Console.

Save changes in database controller class asp.net mvc5 identity

Visual Studio 2013, ASP.NEt MVC 5 Identity
Please someone help me in how to save the information in the database through controller. Let me explain...I want the following to happen:
The user when logged is able update his Education information. After making changes in the fields, he will press the save button and the information is saved in the database. This works fine when i am doing so with the default aspnet users class, i take the usermanager. update method and the info is saved, but i am unable to do so with for any other table. I would be really thankful if someone helps me.
Here's the controller class edit method
public async Task<ActionResult> Edit(string id, Education education)
{
if (!User.Identity.IsAuthenticated)
{
Response.Redirect("~/Account/Login");
}
var db = new ApplicationDbContext();
var educationdb = db.Edu.First(u => u.EducationID == education.EducationID);
educationdb.Qualification = education.Qualification;
educationdb.School = education.School;
educationdb.SchFrom = education.SchFrom;
educationdb.SchTo = education.SchTo;
educationdb.College = education.College;
educationdb.ClgFrom = education.ClgFrom;
educationdb.ClgTo = education.ClgTo;
educationdb.University = education.University;
educationdb.UniFrom = education.UniTo;
educationdb.Description = education.Description;
db.Entry(educationdb).State = System.Data.Entity.EntityState.Modified;
await db.SaveChangesAsync();
//return RedirectToAction("Index");
return View();
}
this is the model class:
namespace theme1.Models
{
public class Education
{
public string EducationID { get; set; }
public string UserID { get; set; }
public ApplicationUser User { get; set; }
public string Qualification { get; set; }
public string School { get; set; }
[DataType(DataType.Date)]
public DateTime SchFrom { get; set; }
[DataType(DataType.Date)]
public DateTime SchTo { get; set; }
public string College { get; set; }
[DataType(DataType.Date)]
public DateTime ClgFrom { get; set; }
[DataType(DataType.Date)]
public DateTime ClgTo { get; set; }
public string University { get; set; }
[DataType(DataType.Date)]
public DateTime UniFrom { get; set; }
[DataType(DataType.Date)]
public DateTime UniTo { get; set; }
public string Description { get; set; }
}
}
var educationdb = db.Edu.First(u => u.EducationID == education.EducationID); this line gives me an error Exception Details: System.InvalidOperationException: Sequence contains no elements
Source Error:
Line 109: var educationdb = db.Edu.First(u => u.EducationID == education.EducationID);
Instead of :
var educationdb = db.Edu.First(u => u.EducationID == education.EducationID);
Try this :
var educationdb = db.Edu.Where(u => u.EducationID == education.EducationID).FirstOrDefault();
OR
Instead of :
var educationdb = db.Edu.First(u => u.EducationID == education.EducationID);
Try this :
var educationdb = db.Edu.FirstOrDefault(u => u.EducationID == education.EducationID);
I suspect that your missing the DBSet for Education model in ApplicationContext.
If this doesn't fix it, could you please provide the code for your ApplicationContext class?
I cant comment on your discussion with Kartikeya since I am new to stackoverflow and lack reputation.
But after you changed your lambda:
var educationdb = db.Edu.Where(u => u.EducationID ==
education.EducationID).FirstOrDefault();
From your discussion with Kartikeya It sounds like you are thinking of Creating rather than editing. You cant edit something that doesn´t exist.
// GET: Model
public ActionResult Create()
{
return View();
}
in MVC 5 it is very easy to scaffold the view by right clicking inside that Action and chosing "add view" and insert the information you need, and when you have created the view, create your post method for Create:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include ="EducationID, UserId....")] Education education)
{
if (ModelState.IsValid)
{
//any extra logic you might want
db.Edu.Add(education);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(education);
}

SubSonic Simple Repository One-To-Many

I made a class like:
public class Video
{
public Guid VideoID { get; set; }
public VideoCategory VideoCategory { get; set; }
public int SortIndex { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public string Author { get; set; }
public string Filename { get; set; }
public new void Add()
{
this.VideoID = Guid.NewGuid();
DB.Repository.Add(this);
}
}
And another like
public class VideoCategory
{
public Guid VideoCategoryID { get; set; }
public string Title { get; set; }
public new void Add()
{
this.VideoCategoryID = Guid.NewGuid();
DB.Repository.Add(this);
}
}
I then have code like:
VideoCategory VideoCategory = new VideoCategory();
VideoCategory.Title = "TestTitle";
VideoCategory.Add();
Video Video = new Video();
Video.VideoCategory = VideoCategory;
Video.SortIndex = 1;
Video.Title = "TestTitle";
Video.Body = "TestBody";
Video.Author = "TestAuthor";
Video.Filename = "TestFile.flv";
Video.Add();
It doesn't save the VideoCategory into my database, so obviously i'm missing something. What else is needed to done to save a one-to-many relationship?
You could probably just do the following, you'll probably want to tidy it up but it will ensure your foreign key value gets populated:
public class Video
{
protected VideoCategory videoCategory;
public Guid ID { get; set; }
public VideoCategory VideoCategory
{
get { return videoCategory; }
set
{
videoCategory = value;
VideoCategoryId = value.ID;
}
}
public Guid VideoCategoryId { get; set; }
public int SortIndex { get; set; }
public string Title { get; set; }
public string Body { get; set; }
public string Author { get; set; }
public string Filename { get; set; }
}
public class VideoCategory
{
public Guid ID { get; set; }
public string Title { get; set; }
}
SimpleRepository repo = new SimpleRepository(SimpleRepositoryOptions.RunMigrations);
VideoCategory videoCategory = new VideoCategory();
videoCategory.ID = Guid.NewGuid();
videoCategory.Title = "TestTitle";
repo.Add<VideoCategory>(videoCategory);
Video video = new Video();
video.ID = Guid.NewGuid();
video.VideoCategory = videoCategory;
video.SortIndex = 1;
video.Title = "TestTitle";
video.Body = "TestBody";
video.Author = "TestAuthor";
video.Filename = "TestFile.flv";
repo.Add<Video>(video);
You're not missing anything. Simplerepository doesn't support one to many out of the box.
Heres a useful link that shows how to mangage foreign keys yourself in SimpleRepository -
subsonic-3-simplerepository
Have not tried it myself, but looks like it would actually work.
Fluent Nhibernate will do this foriegn key management for you automatically, but it's a LOT more complex.
PS If this was helpful, please vote it up.

Resources