EntityFramework using navigation property to populate computed property - entity-framework

Basically what I want to do is return a read only string (user name) which is dependent on the navigation property User and just concatenates the first and last name together.
Ideally I'd just assign a value to OwnerUserId and then use the navigation property to retrieve the data if this is possible? I want to keep the model class as clean as possible.
Any ideas?
POCO Model
namespace Model
{
public class TicketReply
{
public int TicketReplyId { get; set; }
public string Title { get; set; }
/* ....more.... */
/* User ID FK */
public int OwnerUserId { get; set; }
/* User Navigation property */
[ForeignKey("OwnerUserId")]
[IgnoreDataMember]
public User User { get; set; }
/* Here is where I am stuck.... */
public string UserName
{
get { return User.FirstName + " " + User.LastName; }
set { }
}
}
}
*DB initialiser* - just to give some context
private List<TicketReply> AddReplies(DbContext context)
{
var replies = new List<TicketReply>
{
new TicketReply { TicketReplyId = 1, TicketId = 1, CreatedAt = DateTime.Now, OwnerUserId = 1, Text = "My initial query"},
new TicketReply { TicketReplyId = 2, TicketId = 1, CreatedAt = DateTime.Now, OwnerUserId = 2, Text = "Test reply."},
new TicketReply { TicketReplyId = 3, TicketId = 2, CreatedAt = DateTime.Now, OwnerUserId = 1, Text = "Test query"}
};
replies.ForEach(status => context.TicketReplies.Add(status));
context.SaveChanges();
return replies;
}
Thanks
Arthur

Roughly (and typing from memory)
Make it part of the User, e.g...
public class User
{
[NotMapped] // or from code "modelBuilder.Entity<User>().Ignore(x => x.UserName);"
public string UserName
// your user name implementation
}
You cannot just assign OwnerUserId and expect it to work - User will
be loaded when you load Ticket. Also you don't use 'OwnerUserId' in
your fk-entity - to search for for User.
You can use that to 'set' the User 'it points to' - it's saved when you first do the 'SaveChanges'.
e.g...
var user = new User {First = ... Second=...};
db.Tickets.Add(new Ticket{ User = user, ...});
// or...
db.Tickets.Add(new Ticket{ OwnerUserId = ownerid, ...}); // to point to exisitng one you know exists
db.SaveChanges();
...sometime later on...
var ticket = new Ticket { OwnerUserId = ownerid };
// ticket.User is null - won't work
var ticket = // tickets query // db.Tickets.Where(x => x.TicketId == ticketid).FirstOrDefault();
var username = ticket.User.UserName; // now it works, you have your User loaded
Or explicitely...
var user = db.Users.Where(x => x.UserId == ownerid).FirstOrDefault();
var username = user.UserName;

Related

Get query data to view

I'm new in ASP.NET Core 3.1 and I'm trying to pass query data (entity framework) to view.
This is my query
public void OnGet()
{
var query = (from panier in _context.panier
join product in _context.product on panier.id_product equals product.Id_Product
where panier.username == HttpContext.Session.GetString("username")
select new
{
product.nom_product,
product.Image,
panier.Qte,
panier.prix,
panier.Prix_total
});
query.ToList();
}
But I don't know how to call the result in VIEW
You should define a list in your PageModel. Below is a simple example:
IndexModel:
public List<User> Users { get; set; }
public void OnGet()
{
Users = new List<User>
{
new User{ Id = 1, Name = "A"},
new User{ Id = 2, Name = "B"},
new User{ Id = 3, Name = "C"},
};
}
And in the view:
#page
#model IndexModel
#{
ViewData["Title"] = "Home page";
}
#foreach (var u in Model.Users)
{
<label>#u.Name</label><br />
}

Cannot insert explicit value for identity column while updating entity

I am using ASP.NET Boilerplate template.
I want to update Details table, which contains more than one item. If an item exists, it must update, otherwise a new one must be added and all other entries relating to Master primary key in Details table must be deleted. But it is showing an error:
Cannot insert explicit value for identity column in table
'SemesterDetails' when IDENTITY_INSERT is set to OFF
This is the Master table:
public class StudentDegreeCore : Entity<int>
{
[StringLength(150)]
[Required(ErrorMessage = "Enter Degree College ")]
public string DegreeCollege { get; set; }
[Required()]
public string CollegeID { get; set; }
[StringLength(7, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 7)]
[Required(ErrorMessage = "Enter 10th Pass Year")]
public string CommencementYear { get; set; }
public List<StudentSemesterCore> SemesterDetails { get; set; }
}
This is the Details table, represented by the StudentSemesterCore class:
public class StudentSemesterCore
{
[DatabaseGeneratedAttribute(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[StringLength(150)]
[Required(ErrorMessage = "Enter Year/Semester")]
public string YearOrSemester { get; set; }
[Required()]
public virtual int StudentDegreeID { get; set; }
[ForeignKey("StudentDegreeID")]
public virtual StudentDegreeCore StudentDegreeCore { get; set; }
[StringLength(4, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 4)]
[Required(ErrorMessage = "Enter Semester Status")]
public string Status { get; set; }
[DisplayName("% of Marks")]
[RegularExpression(#"\d+(\.\d{1,2})?", ErrorMessage = "Numbers With Two decimal Place Allowed")]
public decimal MarkPercentage { get; set; }
}
This is the Update code:
_studentdegreeRepository.Update(st);
CurrentUnitOfWork.SaveChanges();
It shows an error when SaveChanges is called. Actually, I want to update the details if the same value exists, otherwise add new one and all other data relating to the same StudentDegreeID must be removed.
StudentSemesterCore is not derived from Entity.
You don't need to put Id property in StudentSemesterCore. Remove it.
Add StudentDegreeCoreId to StudentSemesterCore as foreign key reference.
I tried this
public override async Task<StudentDegreeDto> Create(StudentDegreeCreateDto input)
{
//CheckCreatePermission();
StudentDegreeCore st = new StudentDegreeCore();
try
{
StudentDegreeCore core = new StudentDegreeCore()
{
Id = input.Id,
Address1 = input.Address1,
Address2 = input.Address2,
City = input.City,
CollegeID = input.CollegeID,
CommencementYear = input.CommencementYear,
CompletionYear = input.CompletionYear,
CurrentYear = input.CurrentYear,
DegreeCollege = input.DegreeCollege,
DegreeId = input.DegreeId,
OverallPercent = input.OverallPercent,
PinCode = input.PinCode,
PostBox = input.PostBox,
State = input.State,
StreamId = input.StreamId,
UserId = input.UserId
};
core.SemesterDetails = new List<StudentSemesterCore>();
foreach (var items in input.SemesterDetails)
{
core.SemesterDetails.Add(new StudentSemesterCore()
{
GPA = items.GPA,
MarkPercentage = items.MarkPercentage,
Status = items.Status,
UserId = items.UserId ,
Id = items.Id,
StudentDegreeID = items.StudentDegreeID ,
YearOrSemester = items.YearOrSemester,
LastModificationTime = DateTime.Now,
CreationTime = DateTime.Now
});
}
var student = core; //ObjectMapper.Map<StudentDegreeCore>(input);
long uid = (AbpSession.UserId == null) ? 0 : Convert.ToInt64(AbpSession.UserId);
st = _studentRepository.Get(student.Id);
if (st != null && st.Id > 0)
{
st.DegreeCollege = student.DegreeCollege;
st.CollegeID = student.CollegeID;
st.CommencementYear = student.CommencementYear;
st.CompletionYear = student.CompletionYear;
st.LastModificationId = Convert.ToInt32(AbpSession.UserId);
st.LastModificationTime = DateTime.Now;
st.StreamId = student.StreamId;
st.DegreeId = student.DegreeId;
st.CurrentYear = student.CurrentYear;
st.OverallPercent = student.OverallPercent;
st.PinCode = student.PinCode;
st.PostBox = student.PostBox;
st.State = student.State;
st.Address1 = student.Address1;
st.Address2 = student.Address2;
st.City = student.City;
st.SemesterDetails = new List<StudentSemesterCore>();
//st.SemesterDetails = student.SemesterDetails;
_studentRepository.Update(st);
foreach (var items in student.SemesterDetails)
{
_studentSemesterRepository.InsertOrUpdate(items);
}
//_studentRepository.Update(st);
CurrentUnitOfWork.SaveChanges();
}
else
{
student.UserId = Convert.ToInt32(AbpSession.UserId);
student.CreationId = Convert.ToInt32(AbpSession.UserId);
_studentRepository.Insert(student);
CurrentUnitOfWork.SaveChanges();
}
}
catch (Exception ex)
{
}
StudentDegreeDto studentDegreeDto = new StudentDegreeDto()
{
Id = input.Id,
Address1 = input.Address1,
Address2 = input.Address2,
City = input.City,
CollegeID = input.CollegeID,
CommencementYear = input.CommencementYear,
CompletionYear = input.CompletionYear,
CurrentYear = input.CurrentYear,
DegreeCollege = input.DegreeCollege,
DegreeId = input.DegreeId,
OverallPercent = input.OverallPercent,
PinCode = input.PinCode,
PostBox = input.PostBox,
State = input.State,
StreamId = input.StreamId,
UserId = input.UserId
};
studentDegreeDto.SemesterDetails = new List<StudentSemesterDto>();
foreach (var items in input.SemesterDetails)
{
studentDegreeDto.SemesterDetails.Add(new StudentSemesterDto()
{
GPA = items.GPA,
MarkPercentage = items.MarkPercentage,
Status = items.Status,
YearOrSemester = items.YearOrSemester,
LastModificationTime = DateTime.Now,
CreationTime = DateTime.Now
});
}
return studentDegreeDto;
}

How to update complex type field (json) using ormLite from servicestack

I am trying to update only one column with jsonb type. Insert works perfectly without any surprises but I can't find out how can I do update only one field with attribute [ComplextType('json')]
db.UpdateOnly(() => new QuestionPoco() {Answers = requestDto.answers},
where: q => q.Id.ToString() == question.id.ToString());
This should now be supported from this commit which is now available on MyGet.
We've also added new typed PgSqlTypes Attributes which you can use instead of [CustomField("json")], e.g:
public class Question
{
public int Id { get; set; }
public string Text { get; set; }
//equivalent to: [CustomField("json")]
[PgSqlJson]
public List<Answer> Answers { get; set; }
}
public class Answer
{
public int Id { get; set; }
public string Text { get; set; }
}
Which you can Insert/Update as normal, e.g:
db.DropAndCreateTable<Question>();
var createTableSql = db.GetLastSql();
Assert.That(createTableSql, Does.Contain("\"answers\" json NULL"));
db.Insert(new Question
{
Id = 1,
Answers = new List<Answer>
{
new Answer { Id = 1, Text = "Q1 Answer1" }
}
});
var question = db.SingleById<Question>(1);
Assert.That(question.Answers.Count, Is.EqualTo(1));
Assert.That(question.Answers[0].Text, Is.EqualTo("Q1 Answer1"));
db.UpdateOnly(() => new Question {
Answers = new List<Answer> { new Answer { Id = 1, Text = "Q1 Answer1 Updated" } }
},
#where: q => q.Id == 1);
question = db.SingleById<Question>(1);
Assert.That(question.Answers[0].Text, Is.EqualTo("Q1 Answer1 Updated"));

TryGetObjectByKey() doesn't return entity with Added state (EF 6)

1. Q #1
I have POCO
public class Product
{
public string Id { get; set; }
public string Name { get; set; }
public ICollection<Version> Versions { get; set; }
}
In my DbContext I have func
public void AttachUpdated<T>( T objectDetached) where T : class
{
var objContext = ((IObjectContextAdapter)this).ObjectContext;
var objSet = objContext.CreateObjectSet<T>();
var entityKey = objContext.CreateEntityKey(objSet.EntitySet.Name, objectDetached);
object original;
if (objContext.TryGetObjectByKey(entityKey, out original))
objContext.ApplyCurrentValues(entityKey.EntitySetName, objectDetached);
else
objContext.AddObject(entityKey.EntitySetName, objectDetached);}
So i want to add some Products to context
var p1 = new Product(){Id = "1", Name = "Product 1";}
var p2 = new Product(){Id = "1", Name = "Product 1";}
ctx.AttachUpdated(p1);
And when i try to add identical Product (with same Id as first product) TryGetObjectByKey() doesn't find already added product.
ctx.AttachUpdated(p2);
Therefore I need to use ctx.SaveChanges() or AccseptAllChanges() and then
ctx.AttachUpdated(p2) work as expected.
I can't understand where i have problem in my code.
Q #2
var p1 = new Product() { Id = "1", Name = "Product 1" };
var v1 = new Version() { Number = "1.0", Type = "Release", ReleaseDate = "01/01/13" };
p1.Versions = new List<Version>();
p1.Versions.Add(v1);
ctx.AttachUpdated(p1);
And then i see that v1 was addet to DbSet(). But why? And how i could prevent such bihavior. I need to add only Product and not related Versions.
public void AttachOrUpdate<T>(T entity) where T : class
{
var objContext = ((IObjectContextAdapter)context).ObjectContext;
var objSet = objContext.CreateObjectSet<T>();
var entityKey = objContext.CreateEntityKey(objSet.EntitySet.Name, entity);
var original = this.context.Set<T>().Find(entityKey.EntityKeyValues[0].Value);
if (original != null)
{
this.context.Entry<T>(original).CurrentValues.SetValues(entity);
}
else
objContext.AddObject(entityKey.EntitySetName, entity);
}

Optgroup drop-down support in MVC - Problems with Model Binding

I wonder if anyone can shed some light on this problem..
I've got an option group drop-down for selecting a person's ethnicity – however it’s not storing the value in the model.
ViewModel
[UIHint("EthnicOriginEditorTemplate")]
[DisplayName("Question 6: Ethnic Origin")]
public int EthnicOrigin { get; set; }
Helper : GroupDropList.Cs
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.Routing;
namespace Public.Helpers
{
public static class GroupDropListExtensions
{
public static string GroupDropList(this HtmlHelper helper, string name, IEnumerable<GroupDropListItem> data, int SelectedValue, object htmlAttributes)
{
if (data == null && helper.ViewData != null)
data = helper.ViewData.Eval(name) as IEnumerable<GroupDropListItem>;
if (data == null) return string.Empty;
var select = new TagBuilder("select");
if (htmlAttributes != null)
select.MergeAttributes(new RouteValueDictionary(htmlAttributes));
select.GenerateId(name);
var optgroupHtml = new StringBuilder();
var groups = data.ToList();
foreach (var group in data)
{
var groupTag = new TagBuilder("optgroup");
groupTag.Attributes.Add("label", helper.Encode(group.Name));
var optHtml = new StringBuilder();
foreach (var item in group.Items)
{
var option = new TagBuilder("option");
option.Attributes.Add("value", helper.Encode(item.Value));
if (SelectedValue != 0 && item.Value == SelectedValue)
option.Attributes.Add("selected", "selected");
option.InnerHtml = helper.Encode(item.Text);
optHtml.AppendLine(option.ToString(TagRenderMode.Normal));
}
groupTag.InnerHtml = optHtml.ToString();
optgroupHtml.AppendLine(groupTag.ToString(TagRenderMode.Normal));
}
select.InnerHtml = optgroupHtml.ToString();
return select.ToString(TagRenderMode.Normal);
}
}
public class GroupDropListItem
{
public string Name { get; set; }
public List<OptionItem> Items { get; set; }
}
public class OptionItem
{
public string Text { get; set; }
public int Value { get; set; }
}
}
This is my EditorTemplate
<%# Import Namespace="Public.Helpers"%>
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<int>"%>
<%=Html.GroupDropList("EthnicOrigin",
new[]
{
new GroupDropListItem
{
Name = "Ethnicity",
Items = new List<OptionItem>
{
new OptionItem {Value = 0, Text = "Please Select"}
}
},
new GroupDropListItem
{
Name = "a) White",
Items = new List<OptionItem>
{
new OptionItem {Value = 1, Text = "British"},
new OptionItem {Value = 2, Text = "Irish"},
new OptionItem {Value = 3, Text = "Other White (Please specify below)"}
}
},
--snip
}, Model, null)%>
And in the view I'm referencing it as:
<%=Html.EditorFor(x => x.EthnicOrigin, "EthnicOriginEditorTemplate")%>
However it's not passing through the selected Value into the model... has anyone experienced similar problems... many thanks in advance for some pointers.
Your select doesn't have a name attribute and so when you submit the form the selected value is not sent to the server. You need to add a name:
select.GenerateId(name);
select.MergeAttribute("name", name);
Just changed the helper class to get it work for MVC 3 and with nullable int.
Thanks a lot for the class, saves me plenty of time.
public static class GroupDropListExtensions
{
public static MvcHtmlString GroupDropList(this HtmlHelper helper, string name, IEnumerable<GroupDropListItem> data, int? SelectedValue, object htmlAttributes)
{
if (data == null && helper.ViewData != null)
data = helper.ViewData.Eval(name) as IEnumerable<GroupDropListItem>;
if (data == null) return new MvcHtmlString(string.Empty);
var select = new TagBuilder("select");
if (htmlAttributes != null)
select.MergeAttributes(new RouteValueDictionary(htmlAttributes));
select.GenerateId(name);
select.MergeAttribute("name", name);
var optgroupHtml = new StringBuilder();
var groups = data.ToList();
foreach (var group in data)
{
var groupTag = new TagBuilder("optgroup");
groupTag.Attributes.Add("label", helper.Encode(group.Name));
var optHtml = new StringBuilder();
foreach (var item in group.Items)
{
var option = new TagBuilder("option");
option.Attributes.Add("value", helper.Encode(item.Value));
if (SelectedValue != 0 && item.Value == SelectedValue)
option.Attributes.Add("selected", "selected");
option.InnerHtml = helper.Encode(item.Text);
optHtml.AppendLine(option.ToString(TagRenderMode.Normal));
}
groupTag.InnerHtml = optHtml.ToString();
optgroupHtml.AppendLine(groupTag.ToString(TagRenderMode.Normal));
}
select.InnerHtml = optgroupHtml.ToString();
return new MvcHtmlString(select.ToString(TagRenderMode.Normal));
}
}
public class GroupDropListItem
{
public string Name { get; set; }
public List<OptionItem> Items { get; set; }
}
public class OptionItem
{
public string Text { get; set; }
public int Value { get; set; }
}
This is supported natively using SelectListGroup as of ASP.NET MVC 5.2:
var items = new List<SelectListItem>();
var group1 = new SelectListGroup() { Name = "Group 1" };
items.Add(new SelectListItem() { Text = "Item1", Group = group1 });
Then in MVC, do
#Html.DropDownList("select", items)