I have a database first project with ADO.NET Entity Data model generated classes. All of my classes have same boolean and datetime fields, Ok and Date. I want to create generic method for retrieving T class from DbContext but i'm not sure how to use where in that query since i cant access Ok and Date fields.
note: i can't change generated classes, and i would want to avoid using Linq.Dynamic
ADO.Net generated
public partial class First
{
public int Id { get; set; }
public string NameFirst { get; set; }
public DateTime Date { get; set; }
public bool Ok { get; set; }
}
public partial class Second
{
public int Id { get; set; }
public string NameSecond { get; set; }
public DateTime Date { get; set; }
public bool Ok { get; set; }
}
Retrieve data
public List<SomeModel> LoadFromDatabase<T>(bool ok, DateTime date)
{
var dbData = DbContext.Set(typeof(T)).AsNoTracking().Where(x => x.Ok ??? );
//remap to some model and return it
...
return someModel;
}
Edit 1:
public List<SomeModel> LoadFromDatabase<T>(bool ok, DateTime date) where T : IDateOk
{
var dbData = DbContext.Set(typeof(T)).AsNoTracking().Where(x => x.Ok &&);
//remap to some model and return it
...
return someModel;
}
public interface IDateOk {
DateTime Date { get; set; }
bool Ok { get; set; }
}
Edit 2:
I was missing class in method so it should be like this
public List<SomeModel> LoadFromDatabase<T>(bool ok, DateTime date) where T : class IDateOk
Define an interface for the common properties:
public interface IDateOk {
DateTime Date { get; set; }
bool Ok { get; set; }
}
Here is a tutorial how to add the interface to the generated classes:
Getting the Entity Framework to Generate an Interface for Mocking
Constrain your method to require this interface:
public List<SomeModel> LoadFromDatabase<T>(bool ok, DateTime date) where T: IDateOk
Now you should be able to access OK and Date in the implementation.
Related
So i have two different objects, with different properties:
public class ModelA : IServices
{
public string Id { get; set; }
public ServiceType ServiceType { get; set; } (Enum)
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public int Limit { get; set; }
public int ChargePower { get; set; }
}
public class ModelB : IServices
{
public string Id { get; set; }
public ServiceType ServiceType { get; set; } (Enum)
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
public int Power { get; set; }
}
I have then created an Interface:
public interface IServices
{
public string Id { get; set; }
public ServiceType ServiceType { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
And i then use this interface as a input parameter on my method:
public async Task EditObject(IServices serviceObject)
{
}
My problem now is that if i call the method with e.g ModelA, i can only use the properties that is inside my Interface, and yes that makes sense.
How can i make a method that can take both ModelA and ModelB and access the different properties inside these two objects?
Is generics the way to go? or do i have to make two different methods? or am i on the correct way with an Interface?
For instance i dont want ModelA to have ModelB:s property e.g "public int Power { get; set; }"
Hope you understand my question, cheers!
You can test, which class is it:
if (serviceObject is ModelA)
((ModelA)serviceObject).Limit = 5;
Or you can use Reflection:
PropertyInfo pi;
if ((pi = x.GetType().GetProperty("Limit")) != null)
pi.SetValue(x, 5);
Well, a lot of ways
You can make a method overload
public async Task EditObject(ModelA serviceObject) { }
public async Task EditObject(ModelB serviceObject) { }
or you could access needed type through upcasting it
var iServiceVariable = (IServices)new ModelA();
var modelBVariable = iServiceVariable as ModelB;
Console.WriteLine($"Is iServiceVariable of ModelB type? {modelBVariable != null}");
Or you could implement different behavior in your models using the same interface(doesn't have to be IServices), but accessing them in EditObject through the same way (what's the point of interface otherwise?)
If you want to do different things for multiple types, a fairly clean way to do it is by using a switch on the type:
switch(serviceObject)
{
case ModelA modelA:
modelA.Limit = 5;
break;
case ModelB modelB:
modelB.Power = 2;
break;
}
I have a problem with my date. I do the insert with the postman and no matter what I enter I always get back "0001-01-01T00:00:00".
Would also like to limit the output to just the date.
I am very grateful for any help
thank you
Model:
public class BusinessTripDocument
{
public long Id { get; set; }
public string StartLocation { get; set; }
[JsonPropertyName("startDateTime")]
public DateTime StartDate { get; set; }
public string Destination { get; set; }
public DateTime DestinationDate { get; set; }
public string Transportation { get; set; }
public string SuperiorsApproval { get; set; }
// POST: api/Businesstripdocuments
[HttpPost("new/{eId}")]
public async Task<ActionResult<BusinessTripDocument>> PostBusinesstripdocuments(long eId, BusinessTripDocument businesstripdocuments)
{
Employee employee = await _context.Employees.FindAsync(eId);
// businesstripdocuments.Employee = employee;
// _context.Businesstripdocuments.Add(businesstripdocuments);
employee.addDocument(businesstripdocuments);
await _context.SaveChangesAsync();
return CreatedAtAction("GetBusinesstripdocuments", new { id = businesstripdocuments.Id }, businesstripdocuments);
}
Postman
Just from the code you've shown- to me it looks like you should just need to supply the DataType attribute to your Model?
//using System.ComponentModel.DataAnnotations;
public class BusinessTripDocument
{
...
[DataType(DataType.Date)]
[JsonPropertyName("startDateTime")]
public DateTime StartDate { get; set; }
...
}
I suggest that you could do two ways:
1.- The date could be inside the method PostBusinesstripdocuments
2.- The date could be string in class BusinessTripDocument and after conver to datetime.
her is the example:
I have a stored procedure that gets several parameters and based on them it calls another stored procedure.
The returning columns may vary based on the stored procedure that will be actually called.
So I built a base class that contains all the shared columns and extended it with different classes each with its own additional columns as models for the data to be returned from the stored procedures.
Here are the classes for example:
public class SP_BaseModel
{
public decimal? Sum { get; set; }
public string Name { get; set; }
public DateTime? BirthDate { get; set; }
}
public class SP_Extended1 : SP_BaseModel
{
public int? Age { get; set; }
public string Hobby { get; set; }
}
I'm registering the above models in the DbContext like this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<SP_BaseModel>().HasNoKey();
modelBuilder.Entity<SP_Extended1>();
...
}
I've tried both of the following methods but neither works:
public IQueryable<T> GetData<T>(string param1, int? patam2 = null, DateTime? param3 = null) where T : SP_BaseModel
{
return Context.Set<T>()
.FromSqlInterpolated($"EXEC SP_GetData {param1},{param2 ?? null},{param3 ?? null}");
}
public async Task<List<T>> GetData<T>(string param1, int? patam2 = null, DateTime? param3 = null) where T : SP_BaseModel
{
return Context.Set<T>()
.FromSqlInterpolated($"EXEC SP_GetData {param1},{param2 ?? null},{param3 ?? null}").ToListAsync();
}
I'm creating a Nuget from that and in the client, I'm consuming it like this:
public List<SP_Extended1> GetData(DateTime? today = null)
{
today ??= DateTime.Today;
//This didn't work
var list1 = unitOfWork.MyRepository.GetData<SP_Extended1>(...);
//This didn't work as well
var list2 = unitOfWork.GetData<SP_Extended1>(...).AsEnumerable();
return ...
}
But I'm getting this error message:
'FromSqlRaw or FromSqlInterpolated was called with non-composable SQL and with a query composing over it.
Consider calling `AsEnumerable` after the FromSqlRaw or FromSqlInterpolated method to perform the composition on the client-side.'
Try not to use inheritance here. Merge SP_Extended1 with a base class. EF doesn't like inheritance classes. It usually takes a lot of blood to make it working. Easier to use independent classes. This is a sample:
I wrote the special class to get data from stored procedure
public class AccDef
{
public string AccDefName { get; set; }
public string InvoiceNumber { get; set; }
public DateTime InvoiceDate { get; set; }
public DateTime DeliveryDate { get; set; }
public string ZoneName { get; set; }
public double? Quantity { get; set; }
public double HaulawayAmount { get; set; }
public double FuelAmount { get; set; }
public double SumAmount { get; set; }
[NotMapped]
public string StrQuantity
{
get { return Quantity == null || Quantity == 0 ? "-" : string.Format("{0:F0}", Quantity); }
}
[NotMapped]
public string StrInvoiceDate
{
get { return InvoiceDate.ToString("dd/MM/yyyy"); }
}
[NotMapped]
public string StrDeliveryDate
{
get { return DeliveryDate.ToString("dd/MM/yyyy"); }
}
}
EF context
modelBuilder.Entity<AccDef>(e =>
{
e.HasNoKey();
});
This is my code to get data from the stored procedure using EF:
public async Task<IEnumerable<AccDef>> GetAccDefReportDetailsData(DateTime endDate)
{
var pEndDate = new SqlParameter("#EndDate", endDate);
return await _context.Set<AccDef>()
.FromSqlRaw("Execute sp_GetAccDefReportDetails #EndDate", parameters: new[] { pEndDate })
.ToArrayAsync();
}
After this I converted this array to list of my EF model entities.
I'm using AutoMapper v9, and wish to have a postfix on my DTO property names of Dto.
In my profile I add:
RecognizeDestinationPostfixes("Dto");
I validate my mapping with config.AssertConfigurationIsValid(); and immediately it throws with
Unmapped properties:
AccountConnectionIdDto
CreatedDateDto
ModifiedDateDto
IdentityIdDto
AccountIdDto
This is what my DTO looks like:
public class AccountConnectionDto
{
public int AccountConnectionIdDto { get; set; }
public DateTime CreatedDateDto { get; set; }
public DateTime ModifiedDateDto { get; set; }
public Guid IdentityIdDto { get; set; }
public int AccountIdDto { get; set; }
public AccountDto AccountDto { get; set; }
}
Removing the Dto from my model names, and it all works. I feel like the postifx isn't being recognized or utilized at all, am I doing it the correct way?
I have a simple MVC4 model that adds a DateTime.Now to a List<DateTime>() list.
However when I do an EntityState.Modified, the changes are not being kept.
I've debugged this by modifying another property in the model and that saves fine.
So I really am at a loss as to why this is not saving. If anyone has any ideas as to why its not saving that would be life saver material:
The Model:
public class Page
{
public int Id { get; set; }
public string PageURL { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public List<DateTime> Visits { get; set; }
public Page()
{
Visits = new List<DateTime>();
}
}
Here's my code:
private ApplicationDbContext db = new ApplicationDbContext();
public ActionResult CookiePolicy()
{
var page = db.Pages.FirstOrDefault(c => c.PageURL == "cookiepolicy");
page.Visits.Add(DateTime.Now); // this list of datetime objects does not get updated
page.Title = "test "; //but this property does
ViewBag.Title = page.Title;
db.Entry(page).State = EntityState.Modified;
db.SaveChanges();
return View(page);
}
Edit Fabio Luz mentioned:
"collection of primitive types (like int, DateTime, bool) are not
supported"
So the solution below seems like the right option.
Ok, so after some deliberation. I decided to create a new model called vist and have this as the list instead of datetime:
public class Visit
{
public int Id { get; set; }
public DateTime DateTime { get; set; }
public BrowserType BrowserType { get; set; }
public String Duration { get; set; }
public int PageId { get; set; }
public virtual Page Page { get; set; }
public Visit()
{
DateTime = DateTime.Now;
BrowserType = BrowserType.Other;
}
}
There are benefits to this. Now I can store more information then just the datetime.
So for anyone who had the same problem as me. Consider pushing it out into its own model for greater flexibility.
Like Fabio Luz mentioned in his comment primitive type collections aren't supported. A collection within an class retrieved from a context is generally assumed to represent a One-to-Many / Many-to-Many relationship.
When building models keep in mind how they would be represented in a SQL table, and having a column that has a collection within is not supported in such a structure. Now, if you were referencing another object (table) than the object (table record) would have certain properties, such as a primary key etc.
Hope this helps.
Edit:
Here is a sample model you might want to consider:
public class Page
{
public int Id { get; set; }
public string PageURL { get; set; }
public string Name { get; set; }
public string Title { get; set; }
public virtual IQueriable<Visit> Visits { get; set; }
}
public class Visit
{
// ... properties related to data you wish to retain about the visit
public virtual Page Page { get; set; } // navigation property
}