I inherited a C# ASP.Net Core (now .Net 5.0) project with a bunch of entities models like this:
public class Foo
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ID { get; set; }
[Column(TypeName = "varchar(36)")]
public string CreatedBy { get; set; }
public DateTime CreatedAt { get; set; }
[Column(TypeName = "varchar(36)")]
public string RDoxFindingsID { get; set; }
[Column(TypeName = "varchar(36)")]
public string LastUpdatedBy { get; set; }
public DateTime LastUpdatedAt { get; set; }
public string F001 { get; set; }
...
public class Bar
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public Guid ID { get; set; }
[Column(TypeName = "varchar(36)")]
public string CreatedBy { get; set; }
public DateTime CreatedAt { get; set; }
[Column(TypeName = "varchar(36)")]
public string RDoxFindingsID { get; set; }
[Column(TypeName = "varchar(36)")]
public string LastUpdatedBy { get; set; }
public DateTime LastUpdatedAt { get; set; }
public string Q100 { get; set; }
... <= The same "header" fields (CreatedBy, CreatedAt, etc. etc) are copied/pasted at the top of each different model
So my Create and Edit razor pages have a lot of duplicate code like this:
public class CreateModel : PageModel
{
...
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
Foo.CreatedAt = DateTime.Now;
Foo.CreatedBy = Findings.CreatedBy;
Foo.LastUpdatedAt = DateTime.Now;
Foo.LastUpdatedBy = Findings.CreatedBy;
_context.Foo.Add(Foo);
await _context.SaveChangesAsync();
Bar.CreatedAt = DateTime.Now;
Bar.CreatedBy = Findings.CreatedBy;
Bar.LastUpdatedAt = DateTime.Now;
Bar.LastUpdatedBy = Findings.CreatedBy;
_context.Bar.Add(Bar);
await _context.SaveChangesAsync();
...
Q: Is there any clever way I can refactor my C# code such that I can write ONE method I can call for ANY of these models? Without copying/pasting each of these 4-6 lines over and over for each individual model?
NOTE: I CANNOT change the model. But I can do whatever I want with the "Pages" code.
Here comes interfaces very handy, it's actually main use of them. Define common interface and use it. This should e something like this
class Foo : IEntity
{
public Guid ID { get; set; }
public string CreatedBy { get; set; }
public DateTime CreatedAt { get; set; }
public string RDoxFindingsID { get; set; }
public string LastUpdatedBy { get; set; }
public DateTime LastUpdatedAt { get; set; }
}
class Bar : IEntity
{
public Guid ID { get; set; }
public string CreatedBy { get; set; }
public DateTime CreatedAt { get; set; }
public string RDoxFindingsID { get; set; }
public string LastUpdatedBy { get; set; }
public DateTime LastUpdatedAt { get; set; }
}
//You can name whatever you want
interface IEntity
{
Guid ID { get; set; }
string CreatedBy { get; set; }
DateTime CreatedAt { get; set; }
string RDoxFindingsID { get; set; }
string LastUpdatedBy { get; set; }
DateTime LastUpdatedAt { get; set; }
}
class Program
{
public static async Task<IActionResult> OnPostAsync(IEntity entity)
{
if (!ModelState.IsValid)
{
return Page();
}
entity.CreatedAt = DateTime.Now;
entity.CreatedBy = Findings.CreatedBy;
entity.LastUpdatedAt = DateTime.Now;
entity.LastUpdatedBy = Findings.CreatedBy;
_context.Foo.Add(entity);
await _context.SaveChangesAsync();
}
static async void Main(string[] args)
{
await OnPostAsync(new Foo());
}
}
Related
I have two tables which have one-to-many relationship between them.
public class Policy : BaseEntityAudit
{
public override string Kod { get; set; }
public PolicyType PolicyType { get; set; } = PolicyType.Policy;
public long AgentId { get; set; }
public long InsuranceTypeId { get; set; }
public string PolicyNu { get; set; }
public long OwnerId { get; set; }
public string PlateNumber { get; set; }
public string Explanation { get; set; }
public Agent Agent { get; set; }
public InsuranceType InsuranceType { get; set; }
public Owner Owner { get; set; }
public virtual ICollection<SubPolicy> SubPolicies { get; set; }
}
public class SubPolicy : BaseEntityAudit
{
public override string Kod { get; set; }
public long PolicyId { get; set; }
public DateTime IssueDate { get; set; } = DateTime.Now.Date;
public DateTime StartDate { get; set; } = DateTime.Now.Date;
public DateTime EndDate { get; set; } = DateTime.Now.AddYears(1);
public decimal Premium { get; set; }
public long InsurerId { get; set; }
[StringLength(50)]
public Policy Policy { get; set; }
public Insurer Insurer { get; set; }
}
How can I insert related records of these tables to database under one side of one-to-many relationship, so under Policy entity?
Attention:I'm using EF Code-First model, not Db-First;
I have a class which has a many to many relationship with student. Please bare in mind this is a xarmain forms application talking to the client using NewtownSoft
public class Booking
{
public int Id { get; set; }
public int? DayOfWeek { get; set; }
public DateTime? BookingDate { get; set; }
public bool? IsAbsent { get; set; }
public DateTime? Time { get; set; }
public bool? HasCheckedIn { get; set; }
public ICollection<Student> Students { get; set; }
public bool? IsDeleted { get; set; }
public bool? IsActive { get; set; }
public string? CreatedBy { get; set; }
public string? LastModifiedBy { get; set; }
public DateTime? LastUpdatedDate { get; set; }
public DateTime? CreatedDate { get; set; }
}
Student Class
public class Student
{
public int Id { get; set; }
public int? Type { get; set; }
public string? FirstName { get; set; }
public string? Surname { get; set; }
public DateTime? DOB { get; set; }
public decimal? Weight { get; set; }
public decimal? Height { get; set; }
public int? Gender { get; set; }
public string? Photo { get; set; }
public int? Age { get; set; }
public ICollection<Booking> Bookings { get; set; }
public bool? IsDeleted { get; set; }
public ICollection<Notes>? Notes { get; set; }
public decimal? TB { get; set; }
public decimal? OP { get; set; }
public decimal? PU { get; set; }
public decimal? PB { get; set; }
public decimal? BP { get; set; }
public bool? IsActive { get; set; }
public string? CreatedBy { get; set; }
public string? LastModifiedBy { get; set; }
public DateTime? LastUpdatedDate { get; set; }
public DateTime? CreatedDate { get; set; }
}
I am adding that student to my api in the following way from the button click event.
private async void btnBookStudent_Clicked(object sender, EventArgs e)
{
//if we want the booking to include our student we must add it to our colleciton.
var test = Helpers.Dates.GetDateZeroTime(selectedBookingDate.Date).Add(timePicker.Time);
var student = await api.GetStudentById(StudentId);
var newBooking = new Booking
{
IsAbsent = false,
IsActive = true,
IsDeleted = false,
Time = Helpers.Dates.
GetDateZeroTime(selectedBookingDate.Date).
Add(timePicker.Time),
DayOfWeek = DayNumber
};
newBooking.Students = new List<Student>();
newBooking.Students.Add(student);
await api.AddToBooking(newBooking);
await DisplayAlert(Constants.AppName, "Booking Created For
Student", "OK");
}
However my client application is crashing out and not producing an error.
public async Task<HttpStatusCode> AddToBooking(Booking booking)
{
HttpStatusCode statusCode = new HttpStatusCode();
List<string> errors = new List<string>();
var serializerSettings = new JsonSerializerSettings {
ReferenceLoopHandling =
Newtonsoft.Json.ReferenceLoopHandling.Serialize};
string json =
JsonConvert.SerializeObject(booking,Formatting.Indented,
serializerSettings);
booking.CreatedBy = db.GetActiveUser();
var httpContent = new StringContent(json, Encoding.UTF8,
"application/json");
// AddAuthenicationHeader();
// Do the actual request and await the response
var httpResponse = await httpClient.PostAsync(Constants.BaseUrl + Constants.ApiSegmant + Constants.AddBooking, httpContent);
statusCode = httpResponse.StatusCode;
return statusCode;
}
As said before in my previous post its not giving me an error my Xamarin forms c# android application its just crashing at the JsonConvert line.
I have seen some articles suggesting turning off reference loop handling works but it doesn't in my case as I need to add the student at time of the booking.
How do I get more details error information on what is happening I tried adding.
On my booking class but it doesn't even get fired?. A try catch doesn't catch it either.
[OnError]
internal void OnError(StreamingContext context, ErrorContext errorContext)
{
var test = errorContext.Error;
}
I even tried [JsonIgnore] but i dont want that as I want the students to be with the bookings.
There is a self-referencing loop, as both models reference each other and if Json.NET was to serialise the object, it'd be stuck between Booking and Student.
Try ignoring the bookings from being serialised in every student using [JsonIgnore].
public class Student
{
public int Id { get; set; }
public int? Type { get; set; }
public string? FirstName { get; set; }
public string? Surname { get; set; }
public DateTime? DOB { get; set; }
public decimal? Weight { get; set; }
public decimal? Height { get; set; }
public int? Gender { get; set; }
public string? Photo { get; set; }
public int? Age { get; set; }
[JsonIgnore]
public ICollection<Booking> Bookings { get; set; }
public bool? IsDeleted { get; set; }
public ICollection<Notes>? Notes { get; set; }
public decimal? TB { get; set; }
public decimal? OP { get; set; }
public decimal? PU { get; set; }
public decimal? PB { get; set; }
public decimal? BP { get; set; }
public bool? IsActive { get; set; }
public string? CreatedBy { get; set; }
public string? LastModifiedBy { get; set; }
public DateTime? LastUpdatedDate { get; set; }
public DateTime? CreatedDate { get; set; }
}
I have a NestedSetBuilder class. It has a MakeRootAsync method:
public async Task<TEntity> MakeRootAsync<TEntity>(TEntity ownerNode) where TEntity: NestedSetEntity
{
_operation = OperationMakeRoot;
ownerNode.Lft = 1;
ownerNode.Rgt = 2;
ownerNode.Depth = 0;
await _db.Set<TEntity>().AddAsync(ownerNode);
await _db.SaveChangesAsync();
return ownerNode;
}
There is a base class NestedSetEntity:
public class NestedSetEntity
{
public Guid Id { get; set; }
public int Lft { get; set; }
public int Rgt { get; set; }
public int Depth { get; set; }
public Guid? Tree { get; set; }
}
There is a child class Category:
[Table("categories")]
public class Category: NestedSetEntity
{
public Category()
{
Visible = true;
CreatedAt = DateTime.Now;
UpdatedAt = DateTime.Now;
}
[Column("id")]
public Guid Id { get; set; }
[Required]
[StringLength(256)]
[Column("title")]
public string Title { get; set; }
[Column("lft")]
public int Lft { get; set; }
[Column("rgt")]
public int Rgt { get; set; }
[Column("depth")]
public int Depth { get; set; }
[Column("tree")]
public Guid? Tree { get; set; }
[Column("visible")]
public bool Visible { get; set; }
[Required]
[Column("created_at")]
public DateTime CreatedAt { get; set; }
[Column("updated_at")]
public DateTime UpdatedAt { get; set; }
}
There is a method where makeRootAsync is called
[HttpGet]
public async Task<IActionResult> Categories()
{
//var res = await _dnsParserService.ParseCategoriesAsync();
var res = await _categoryParserService.ParseCategoryListAsync();
var categoryIds = new Dictionary<string, string>();
foreach (var categoryListResItem in res)
{
if (categoryIds.TryGetValue(categoryListResItem.CategoryFirstTitle, out var parentCategory))
continue;
var node = Map(categoryListResItem, "CategoryFirstTitle");
var addedCategory = await _nestedSetBuilder.MakeRootAsync(node); // this call
categoryIds[categoryListResItem.CategoryFirstTitle] = addedCategory.Id.ToString();
}
return Ok(res);
}
In the database, columns Lft, Rgt, Depth have values of zero:
In the debugger, you can see that the fields are duplicated (separately, the fields of the base class and child class):
Tell me how to fix it? I use the base class to work with LINQ
This is likely due to the fact that the properties in your derived class hide the properties in your base class. Based on what you need, I'd recommend setting the Column attributes on your base class, and remove the derived class properties:
public class NestedSetEntity
{
[Column("id")]
public Guid Id { get; set; }
[Column("lft")]
public int Lft { get; set; }
[Column("rgt")]
public int Rgt { get; set; }
[Column("depth")]
public int Depth { get; set; }
[Column("tree")]
public Guid? Tree { get; set; }
}
[Table("categories")]
public class Category: NestedSetEntity
{
public Category()
{
Visible = true;
CreatedAt = DateTime.Now;
UpdatedAt = DateTime.Now;
}
[Required]
[StringLength(256)]
[Column("title")]
public string Title { get; set; }
[Column("visible")]
public bool Visible { get; set; }
[Required]
[Column("created_at")]
public DateTime CreatedAt { get; set; }
[Column("updated_at")]
public DateTime UpdatedAt { get; set; }
}
If you need different column names for the different tables that inherit from NestedSetEntity, you can use an interface rather than a base class:
public interface NestedSetEntity
{
Guid Id { get; set; }
int Lft { get; set; }
}
[Table("categories")]
public class Category : NestedSetEntity
{
[Required]
[Column("title")]
public string Title { get; set; }
[Column("id")]
public Guid Id { get; set; }
[Column("lft")]
public int Lft { get; set; }
}
[Table("mytable")]
public class MyTable : NestedSetEntity
{
[Column("my_id")]
public Guid Id { get; set; }
[Column("left_column")]
public int Lft { get; set; }
}
I have the following code:
internal static bool SaveUOSChangeLog(List<Contracts.DataContracts.UOSChangeLog> values, string user)
{
try
{
using(var ctx = new StradaDataReviewContext2())
{
values.ForEach(u => { u.Username = user; u.Changed = DateTime.Now; });
var test = ctx.UOSChangeLog.Add(values);
ctx.SaveChanges();
return true;
}
}
The thing I want to do Is to save values to the database. However, I get a the following error message:
Here is my Contracts.DataContracts.UOSChangeLog:
public int? Id { get; set; }
public int Accident_nr { get; set; }
public int Refnr { get; set; }
public int Action { get; set; }
public string Old_data { get; set; }
public string New_data { get; set; }
public DateTime SearchedFromDate { get; set; }
public DateTime SearchedToDate { get; set; }
public DateTime Changed { get; set; }
public string Username { get; set; }
public string Comment { get; set; }
And here Is my Services.StradaDataReview2Model.UOSChangeLog that are used as a DbSet
[Table("UOSChangeLog")]
public partial class UOSChangeLog
{
[Required]
public int? Id { get; set; }
public int Accident_nr { get; set; }
[Required]
public int Refnr { get; set; }
[Required]
public int Action { get; set; }
[Required]
public string Old_data { get; set; }
[Required]
public string New_data { get; set; }
[Required]
public DateTime SearchedFromDate { get; set; }
[Required]
public DateTime SearchedToDate { get; set; }
[Required]
public DateTime Changed { get; set; }
[Required]
public string Username { get; set; }
[Required]
public string Comment { get; set; }
}
You're trying to add a list with the Add method which takes a single object, just keep it simple and use a foreach:
using(var ctx = new StradaDataReviewContext2())
{
foreach(var value in values)
{
value.Username = user;
value.Changed = DateTime.Now;
ctx.UOSChangeLog.Add(value);
}
ctx.SaveChanges();
return true;
}
Just use a simple foreach, linq is a querying language, not a modifying language.
Please use addrange method.
db.TheTable.AddRange(TheList)
db.SaveChanges();
You can use Entity Framework's .AddRange method to add a collection of objects to your Db.
MSDN
It will look like:
using(var ctx = new StradaDataReviewContext2())
{
values.ForEach(u => { u.Username = user; u.Changed = DateTime.Now; });
var test = ctx.UOSChangeLog.AddRange(values);
ctx.SaveChanges();
return true;
}
I am trying to force entity framework 5.0 to create a constructor on each of my generated poco classes. This constructor should instantiate any foreign key navigation properties I have.
e.g.
public partial class Event
{
public System.Guid EventId { get; set; }
public System.DateTime CreatedDate { get; set; }
public string CreatedUser { get; set; }
public int CreatedUserId { get; set; }
public string Title { get; set; }
public string EventDesc { get; set; }
public System.DateTime Start { get; set; }
public System.DateTime End { get; set; }
public string Source { get; set; }
public bool Editable { get; set; }
public string ClassName { get; set; }
public string Url { get; set; }
public bool IsDeleted { get; set; }
public bool IsObsolete { get; set; }
public bool AllDay { get; set; }
public System.DateTime ModifiedDate { get; set; }
public string ModifiedUser { get; set; }
public int RowVer { get; set; }
public virtual UserProfile UserProfile { get; set; }
}
should become:
public partial class Event
{
public Event()
{
this.UserProfile = new UserProfile();
}
public System.Guid EventId { get; set; }
public System.DateTime CreatedDate { get; set; }
public string CreatedUser { get; set; }
public int CreatedUserId { get; set; }
public string Title { get; set; }
public string EventDesc { get; set; }
public System.DateTime Start { get; set; }
public System.DateTime End { get; set; }
public string Source { get; set; }
public bool Editable { get; set; }
public string ClassName { get; set; }
public string Url { get; set; }
public bool IsDeleted { get; set; }
public bool IsObsolete { get; set; }
public bool AllDay { get; set; }
public System.DateTime ModifiedDate { get; set; }
public string ModifiedUser { get; set; }
public int RowVer { get; set; }
public virtual UserProfile UserProfile { get; set; }
}
I know it is possible, but not sure how. Any help would be most appreciated.
Thanks
When I retrieve from db in my repository (see below) I create a list of events, when I pass this list of events back via json I get a parse error due to event.UserProfile being null.
I could set it in code for each event, but that wouldn't be smart.
I need a link or an example if possible to help achieve what I need.
public List<Event> GetEvents(int userId, DateTime start, DateTime end)
{
List<Event> domainList = new List<Event>();
using (BookingModels dbEntities = new BookingModels())
{
var eventQuery = from dboEvents in dbEntities.Events
where dboEvents.Start >= start
&& dboEvents.End <= end
select dboEvents;
domainList = eventQuery.ToList<Event>();
}
return domainList;
}