I have an Index Method in my FileController that can return a file that is stored in the Attachments entity. How can I change the method to work with any entity not just the Attachments?
public class FileController : Controller
{
private MainDatabaseContext db = new MainDatabaseContext();
// GET: File
public ActionResult Index(int id)
{
var fileToRetrieve = db.Attachments.Find(id);
var FileObject= File (fileToRetrieve.AttachmentObject, fileToRetrieve.ContentType);
if (FileObject.FileDownloadName.Length == 0)
{
FileObject.FileDownloadName = fileToRetrieve.Filename;
}
return FileObject;
}
}
This is what I have done as a workaround, but it has a lot of repeated code which I wanted to avoid:
public class FileController : Controller
{
private MainDatabaseContext db = new MainDatabaseContext();
enum EntityName
{
Attachment=1,
WAProgramApplicationId,
HouseholdIncome,
HouseholdMember
}
// GET: File
public ActionResult Index(int id=0,int WAProgramApplicationId=0,int householdIncomeID=0,int householdMemberId=0)
{
if (householdIncomeID!=0)
{
return GetFileObject(householdIncomeID, EntityName.HouseholdIncome);
}
if (id!=0)
{
return GetFileObject(id, EntityName.Attachment);
}
if (WAProgramApplicationId != 0)
{
return GetFileObject(WAProgramApplicationId, EntityName.WAProgramApplicationId);
}
if (householdMemberId!=0)
{
return GetFileObject(householdMemberId, EntityName.HouseholdMember);
}
return null;
}
private ActionResult GetFileObject(int id, EntityName entityName)
{
if (entityName==EntityName.Attachment)
{
var fileToRetrieve = db.Attachments.Find(id);
var FileObject = File(fileToRetrieve.AttachmentObject, fileToRetrieve.ContentType);
if (FileObject.FileDownloadName.Length == 0)
{
FileObject.FileDownloadName = fileToRetrieve.Filename;
}
return FileObject;
}
if (entityName == EntityName.HouseholdIncome)
{
var fileToRetrieve = db.HouseholdIncomes.Find(id);
var FileObject = File(fileToRetrieve.AttachmentObject, fileToRetrieve.ContentType);
if (FileObject.FileDownloadName.Length == 0)
{
FileObject.FileDownloadName = fileToRetrieve.Filename;
}
return FileObject;
}
if (entityName==EntityName.WAProgramApplicationId)
{
var fileToRetrieve = db.WAProgramApplications.Find(id);
var FileObject = File(fileToRetrieve.AttachmentObject, fileToRetrieve.ContentType);
if (FileObject.FileDownloadName.Length == 0)
{
FileObject.FileDownloadName = fileToRetrieve.Filename;
}
return FileObject;
}
if (entityName==EntityName.HouseholdMember)
{
var fileToRetrieve = db.HouseholdMembers.Find(id);
var FileObject = File(fileToRetrieve.AttachmentObject, fileToRetrieve.ContentType);
if (FileObject.FileDownloadName.Length == 0)
{
FileObject.FileDownloadName = fileToRetrieve.Filename;
}
return FileObject;
}
return null;
}
}
to make the entity more generic just use the Set method, you can do it like this:
db.Set<YourEntity>().Find(id);
They are some approaches how to implement a generic controller with data.
Check here and here.
If you have a repository that can handle _repository.get<T> and return the right object, you can achieve this.
Related
I'm learning asp.net WebApi and EFCore (CodeFirst) and as an exercise, I'm building Warehouse Api and my update method doesn't work.
This is my repository code:
public void Update(T toUpdate)
{
if(toUpdate == null) throw new ArgumentNullException("Entity is null");
T tmp = GetById(toUpdate.Id);
tmp = toUpdate;
_context.SaveChanges();
}
and this is my Service code:
public void UpdateEmployee(UpdateEmployeeCommand command)
{
UpdateEmployeeCommandValidator validator = new UpdateEmployeeCommandValidator();
var results = validator.Validate(command);
if (!results.IsValid)
{
throw new CommandValidationException(results.Errors.Select(x => new CommandValidationError
{
ErrorCode = x.ErrorCode,
ErrorMessage = x.ErrorMessage,
PropertyName = x.PropertyName
}));
}
_repository.Update(new Employee()
{
Id = command.Id,
FirstName = command.FirstName,
Address = command.Address,
LastName = command.LastName,
Age = command.Age,
Email = command.Email,
PhoneNumber = command.PhoneNumber
});
}
and this is how I use it in Controller:
public ActionResult UpdateEmployee(int Id, UpdateEmployeeCommand command)
{
if(Id != command.Id)
{
return BadRequest();
}
var employeeModelFromRepo = _repository.GetById(Id);
if(employeeModelFromRepo == null)
{
return NotFound();
}
_employeeService.UpdateEmployee(command);
return NoContent();
}
When I call UpdateEmployee, it runs without any error but it doesn't update my database.
I'm new to this, so this might be an easy fix.
I am using this generic update function:
public virtual T Update(T t) where T : class, IBaseEntity // contains Id as primary key
{
if (t == null)
return null;
var exist = Context.Set<T>().Find(t);
// Or you can try
var exist = Context.Set<T>()
.Where(i=>i.Id=t.Id).FirstOrdDefault();
if (exist == null) return exist;
Context.Entry(exist).CurrentValues.SetValues(t);
Context.SaveChanges();
return exist;
}
Don't you forget to call your service method in the controller end-point? UpdateEmployee()
Commented solution works, i just had to add db.Entry(tmp).CurrentValues.SetValues(toUpdate) to the repository code.
I need to add a picture and store the picture Id and product Id in a seperate productPicture table. You can't add a picture without an existing product. How to achieve this?
This is my code for adding an entity to database:
controller:
[HttpPost]
[Route("api/v1/catalog/products/pictures")]
[ProducesResponseType((int)HttpStatusCode.Created)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> UploadPicture([FromForm]PictureViewModel pic)
{
try
{
var result = await _service.Add(pic);
if (result.HasErrors)
{
return BadRequest(result.Errors);
}
return CreatedAtAction(nameof(GetById), result);
}
catch (Exception ex)
{
return BadRequest(ex.ToString());
}
}
service:
public async Task<ServiceResultWithoutBaseEntity<Picture>> Add(PictureViewModel newItem)
{
var result = new ServiceResultWithoutBaseEntity<Picture>();
result.Errors.AddRange(Validate(newItem));
if (result.HasErrors)
return result;
var item = await newItem.MapToEntity(new Picture());
_context.Pictures.Add(item);
await _context.SaveChangesAsync();
result.EntityResult = item;
return result;
}
helper:
public async static Task<Picture> MapToEntity(this PictureViewModel source, Picture entity)
{
if (source == null || entity == null)
return null;
if (source.FileForUpload != null || source.FileForUpload.Length != 0)
{
AzureBlobExtensions abe = new AzureBlobExtensions();
string folderValue = string.Concat(entity.ID.ToString().ToLower(), "/", DateTime.Now.ToString("yyyyMMddHHmmss"), source.FileForUpload.FileName);
var fileUrl = await abe.UploadFile(source.FileForUpload, folderValue, Path.GetExtension(source.FileForUpload.FileName), "Picture");
entity.VirtualPath = fileUrl;
}
else
{
entity.VirtualPath = source.FileUrl;
}
entity.FileName = source.FileName;
entity.SeoFilename = source.SeoFilename;
entity.AltAttribute = source.AltAttribute;
entity.TitleAtrribute = source.TitleAtrribute;
entity.MimeType = source.MimeType;
entity.IsNew = source.IsNew;
return entity;
}
product table
picture table
productPicture table
I am gettin an null reference exception inside the Controller on the line:
return await Store.GetSearchDTO();
The error in the console reads:
POST http://localhost:55471/api/GetSearchDTO 500 (Internal Server Error)
Error: Resolving failed with a reason [object Object], but no resolveFailed provided for segment Search
Any insight on why this may be happening would be great.
Controller
namespace Api.Controllers
{
[Authorize]
[RoutePrefix("api/Search")]
public class Controller : ApiController
{
private Store _store;
public Store Store
{
get
{
return _store ?? Request.GetOwinContext().Get<Store>();
}
private set
{
_store = value;
}
}
public Controller()
{
}
public Controller(Store store)
{
Store = store;
}
[HttpPost]
[Route("GetSearchDTO")]
public async Task<SearchDTO> GetSearchDTO()
{
return await Store.GetSearchDTO();
}
}
}
Store
public async Task<SearchDTO> GetSearchDTO()
{
var toReturn = new SearchDTO();
var assessment = await Db.Definitions.Where(x => x.IsActive == true).ToListAsync();
var Types = await Db.Types.ToListAsync();
int i = 0;
int j = 0;
foreach(var assess in assessment)
{
var courseName = await Db.Courses.Where(x => x.Id == assess.CourseId).FirstOrDefaultAsync();
toReturn.CourseIds[i] = courseName.Id;
toReturn.CourseNames[i] = courseName.Name;
toReturn.Names[i] = assess.Name;
i++;
}
foreach(var type in Types)
{
toReturn.TypeIds[j] = type.Id;
toReturn.Types[j] = type.Name;
}
toReturn.SectionFlag = true;
return toReturn;
}
}
}
I have two methods that use different viewmodels but are the same logic. At the moment I have copied and pasted them into their respective controllers. Any way to share these methods somehow?
Song Controller:
public JsonResult IncrementViews(int id)
{
using (ApplicationDbContext db = new ApplicationDbContext())
{
PublishedSongViewModel song = db.PublishedSongs.Single(x => x.Id == id);
song.UniquePlayCounts++;
db.SaveChanges();
return Json(new { UniquePlayCounts = song.UniquePlayCounts }, JsonRequestBehavior.AllowGet);
}
}
Station Controller:
public JsonResult IncrementViews(int id)
{
using (ApplicationDbContext db = new ApplicationDbContext())
{
RadioStationViewModel station = db.RadioStations.Single(x => x.Id == id);
station.UniquePlayCounts++;
db.SaveChanges();
return Json(new { UniquePlayCounts = station.UniquePlayCounts }, JsonRequestBehavior.AllowGet);
}
}
Edit:
class so far:
public static IEnumerable<Type> GetElements(ApplicationDbContext db, Type type)
{
if (type == typeof(SongsController))
return (IEnumerable<Type>)db.PublishedSongs;
else if (type == typeof(RadioStationsController))
return (IEnumerable<Type>)db.RadioStations;
else
throw new Exception("Controller not found, DBHelper");
}
Create a class called BasicController and add the method to it, like this:
public class BasicController {
public JsonResult IncrementViews(int id)
{
using (ApplicationDbContext db = new ApplicationDbContext())
{
var element = DBHelper.GetElements(db, this.GetType()).Single(x => x.Id == id);
element.UniquePlayCounts++;
db.SaveChanges();
return Json(new { UniquePlayCounts = song.UniquePlayCounts }, JsonRequestBehavior.AllowGet);
}
}
}
and modify your classes to inherit from BasicController. You will also have to create the DBHelper class with the GetElements method, which gathers the IEnumerable elements from db based on type.
EDIT: This is how you can create a helper:
public class DBHelper {
public static IEnumerable GetElements(ApplicationDbContext db, System.Type type) {
if (type == typeof(SongController)) {
return db.PublishedSongs;
} else if (type == typeof(StationController)) {
return db.RadioStations;
}
}
}
We are using entity framework codefirst approach
I am new to entity framework and I am facing error while trying to do "New transaction is not allowed because there are other threads running in the session.
public class DatabaseBackup : IDataBackup
{
private readonly IMonarchDbContext m_db;
public DatabaseBackup(IMonarchDbContext podb)
{
if (podb == null)
throw new ArgumentNullException("podb");
m_db = podb;
}
public DBBackupHistory GetLatestBackupHistory(DBBackupFrequency backupFrequency = DBBackupFrequency.Periodic)
{
DBBackupHistory result = null;
// get the backup history of the given backuptype and populate the objects
var configId = m_db.DBBackupConfigurations.Where(c => c.ScheduleType == (int)backupFrequency && c.BackupStatus == 1).Distinct().Select(c => c.ConfigurationId).DefaultIfEmpty(-1).First();
if (configId > 0)
{
result = m_db.DBBackupHistorys.Where(b => b.Status == 1 && b.ConfigurationId == configId).OrderByDescending(lb => lb.BackupDatetime).FirstOrDefault();
}
return result;
}
public IEnumerable<DBBackupConfiguration> GetAllConfiguration()
{
var result = m_db.DBBackupConfigurations.Where(c => c.BackupStatus == 1).OrderByDescending(c => c.ConfigurationId);
return result;
}
public void Backup(DBBackupConfiguration config, int fileIndex)
{
Console.WriteLine("Running DB Backup type {0} to device {1}", (DBBackupType)config.BackupType, fileIndex);
m_db.StoredProc.SPBackup(config, fileIndex);
}
I am calling the below methods in another class as follows
private readonly IDataBackup m_dataBackup;
public int PerformBackup(int defaultPollIntervalInMinutes = 15)
{
// polling interval in Minutes
int pollInterval = defaultPollIntervalInMinutes;
int fileIndex = getCurrentDumpFileIndex();
// check for the backup configuration
var configurations = m_dataBackup.GetAllConfiguration();
foreach (var config in configurations)
{
var lastBackup = m_dataBackup.GetLatestBackupHistory(DBBackupFrequency.Weekly);
if (lastBackup == null)
{
m_dataBackup.Backup(config, fileIndex + 1);
break;
}
Here is the Db Context class is as below
public class MonarchDbContext:DbContext,IMonarchDbContext
{
private IStoredProcedure m_storedProc;
private static object m_dbIntializerSet;
public MonarchDbContext(string nameOrConnectionString)
: base( nameOrConnectionString )
{
//-- Set the DB initializer only once.
System.Threading.LazyInitializer.EnsureInitialized( ref m_dbIntializerSet,()=>{
Database.SetInitializer<MonarchDbContext>(null);
//-- Give debug builds a chance to overwrite the above.
_SetInitializerForDebugBuilds();
return new object();
});
Configuration.LazyLoadingEnabled = false;
Configuration.ProxyCreationEnabled = false;
var csb = new SqlConnectionStringBuilder( this.Database.Connection.ConnectionString );
csb.MultipleActiveResultSets = true;
this.Database.Connection.ConnectionString = csb.ToString();
var objectContext = ( this as IObjectContextAdapter ).ObjectContext;
objectContext.CommandTimeout = 3600;
}
#region Public "Tables"
public IDbSet<DBBackupConfiguration> DBBackupConfigurations { get; set; }
public IDbSet<DBBackupHistory> DBBackupHistorys { get; set; }
public IStoredProcedure StoredProc
{
get
{
return System.Threading.LazyInitializer.EnsureInitialized(ref m_storedProc, () => new BackupStoredProc(this.Database));
}
}
#endregion
please let me know how can i solve the issue.
I found the issue
I need to add toList() at the end of the Linq code and it just worked for me.
public IEnumerable<DBBackupConfiguration> GetAllConfiguration()
{
var result = m_db.DBBackupConfigurations.Where(c => c.BackupStatus == 1).OrderByDescending(c => c.ConfigurationId).ToList();
return result;
}
Just add the List to Ienumerbale types