How to download or save SQL Server database full backup and restore it using ASP.NET Core with a LINQ query? - asp.net-core-mvc

I have developed an ASP.NET Core web application, I need to write a C# method to allow the end user to download or save the SQL Server database full backup on his local computer. Moreover, I need to upload that database backup to restore a database.
Here is an example C# method that I wrote to download and save only one table of data in Excel format. Now I want to write another method to allow the user to download and save the all table full-backup as a .bak file on his local computer.
Please guide how to achieve this, thank you.
[DisplayName("Export Data To Excel")]
[Authorize(policy: "HasAccess")]
public class ExportDataToExcelController : BaseController
{
public ExportDataToExcelController(HoshmandDBContext context) : base(context)
{
}
// ----Export Patient Data -----------------------------
public IActionResult ExportPatientDataToExcel()
{
DataTable patientTable = new DataTable("Patients_Data");
patientTable.Columns.AddRange(new DataColumn[9]
{
new DataColumn("Patient ID"),
new DataColumn("First Name"),
});
var patientsList = _context.PatientTbs.Include(a => a.Doctor).ToList();
foreach (var patient in patientsList)
{
patientTable.Rows.Add(patient.PatientId, patient.FirstName);
}
using (XLWorkbook wb = new XLWorkbook())
{
wb.Worksheets.Add(patientTable);
using (MemoryStream stream = new MemoryStream())
{
wb.SaveAs(stream);
return File(stream.ToArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "Patients Data " + GetLocalDateTime().Date.ToShortDateString() + "_" + ".xlsx");
}
}
}
}
Here is the user interface screenshot that I can download only one table of data in Excel format:
Here is an example user interface for restoring the database backup:

Related

Implementing file attachments in JHipster

I have a monolithic JHipster (v5.6.1) project, and I need to implement file attachments to an entity.
I don't know where to start with this. DTOs are sent to REST controllers to create or update them, how should I send the file?
I could attach the Base64 to my DTO and send it that way, but I'm not sure how (are there any angular plugins to accomplish this?). This would require extra work for the server and, I think, extra file size.
Another option is to send the entity (DTO) and file separately, again I'm not entirely sure how.
I see no reason to leave this question unanswered since I have implemented attachments successfully in several of my projects now.
If you prefer, you can skip this explanation and check the github repository I created at vicpermir/jhipster-ng-attachments. It's a working project ready to fire up and play.
The general idea is to have an Attachment entity with all the required fields (file size, name, content type, etc...) and set a many-to-many relation to any entity you want to implement file attachments for.
The JDL is something like this:
// Test entity, just to showcase attachments, this should
// be the entity you want to add attachments to
entity Report {
name String required
}
entity Attachment {
filename String required // Generated unique filename on the server
originalFilename String required // Original filename on the users computer
extension String required
sizeInBytes Integer required
sha256 String required // Can be useful for duplication and integrity checks
contentType String required
uploadDate Instant required
}
// ManyToMany instead of OneToMany because it will be easier and cleaner
// to integrate attachments into other entities in case we need to do it
relationship ManyToMany {
Report{attachments} to Attachment{reports}
}
I have both filename and originalFilename because one of my requirements was to keep whatever file name the user uploaded it with. The generated unique name that I use on the server side is transparent to the user.
Once you generate a project with a JDL like that, you will have to add the file payload to your DTO (or entity if you don't use DTOs) so that the server can receive it in base64 and store it.
I have this in my AttachmentDTO:
...
private Instant uploadDate;
// File payload (transient)
private byte[] file;
public Long getId() {
return id;
}
...
Then, you only have to process those byte arrays on the server side, store them and save a reference to the location into the database.
AttachmentService.java
/**
* Process file attachments
*/
public Set<AttachmentDTO> processAttachments(Set<AttachmentDTO> attachments) {
Set<AttachmentDTO> result = new HashSet<>();
if (attachments != null && attachments.size() > 0) {
for (AttachmentDTO a : attachments) {
if (a.getId() == null) {
Optional<AttachmentDTO> existingAttachment = this.findBySha256(a.getSha256());
if(existingAttachment.isPresent()) {
a.setId(existingAttachment.get().getId());
} else {
String fileExtension = FilenameUtils.getExtension(a.getOriginalFilename());
String fileName = UUID.randomUUID() + "." + fileExtension;
if (StringUtils.isBlank(a.getContentType())) {
a.setContentType("application/octet-stream");
}
Boolean saved = this.createBase64File(fileName, a.getFile());
if (saved) {
a.setFilename(fileName);
}
}
}
result.add(a);
}
}
return result;
}
What I do here is check if the attachment already exists (using the SHA256 hash). If it does I use that one, otherwise I store the new file and persist the new attachment data.
What's left now is to manage attachments on the client side. I created two components for this so it is extremely easy to add attachments to new entities.
attachment-download.component.ts
...
#Component({
selector: 'jhi-attachment-download',
template: , // Removed to reduce verbosity
providers: [JhiDataUtils]
})
export class JhiAttachmentDownloadComponent {
#Input()
attachments: IAttachment[] = [];
}
This just calls a mapping that takes the attachment ID, looks for the associated file on the server and returns that file for the browser to download. Use this component in your entity detail view with:
<jhi-attachment-download [attachments]="[your_entity].attachments"></jhi-attachment-download>
attachment-upload.component.ts
...
#Component({
selector: 'jhi-attachment-upload',
template: , // Removed to reduce verbosity
providers: [JhiDataUtils]
})
export class JhiAttachmentUploadComponent {
#Input()
attachments: IAttachment[] = [];
loadingFiles: number;
constructor(private dataUtils: JhiDataUtils) {
this.loadingFiles = 0;
}
addAttachment(e: any): void {
this.loadingFiles = 0;
if (e && e.target.files) {
this.loadingFiles = e.target.files.length;
for (let i = 0; i < this.loadingFiles; i++) {
const file = e.target.files[i];
const fileName = file.name;
const attachment: IAttachment = {
originalFilename: fileName,
contentType: file.type,
sizeInBytes: file.size,
extension: this.getExtension(fileName),
processing: true
};
this.attachments.push(attachment);
this.dataUtils.toBase64(file, (base64Data: any) => {
attachment.file = base64Data;
attachment.sha256 = hash
.sha256()
.update(base64Data)
.digest('hex');
attachment.processing = false;
this.loadingFiles--;
});
}
}
e.target.value = '';
}
getExtension(fileName: string): string {
return fileName.substring(fileName.lastIndexOf('.'));
}
}
Use this component in your entity update view with:
<jhi-attachment-upload [attachments]="editForm.get('attachments')!.value"></jhi-attachment-upload>
Once sent to the server, the files will be stored on the folder you configured in your application-*.yml separated in subdirectories by year and month. This is to avoid storing too many files on the same folder, which can be a big headache.
I'm sure many things could be done better, but this has worked for me.

Web API OData custom query issue

I am new to Web API, Entity Framework and OData. I asked a similar question in another forum but haven't gotten a relevant response.
We have a OData compliant web api service for use in Salesforce. We have a custom complex query in Oracle that we need to expose.
I am not sure how to use a custom query like we want to also allow for odata parameter filtering to occur? ($filter, $top, $skip, etc) For example, when a $filter is used i want to apply that filter to the custom query and then send it back to the database to have it return the result set. How can i do this?
The issue i seem to have is that I can see the parameters as they come in but they are not translating to the query being passed to oracle. It seems that it will fire the query returning the full result set and then apply the parameters. This is very slow as the result set is very large.
I am hoping 2 figure out 2 things
1. How can i use custom sql and apply odata parameters to the underlying query?
2. When using EF or a custom query, how can i apply odata parameters to the query so that when the query is sent to the database that the $filter parameter, for example, is included in the query? I don't want the full result returned then apply the filter.
Can anyone give me some pointers on how to make this happen?
private static ODataValidationSettings _validationSettings = new ODataValidationSettings();
//public IHttpActionResult GetName()
//{ }
// GET: odata/ShareData
[ODataRoute("Orders")]
[EnableQuery(PageSize = 50)]
public IHttpActionResult GetOrders(ODataQueryOptions<Orders> queryOptions)
{
// validate the query.
try
{
queryOptions.Validate(_validationSettings);
}
catch (ODataException ex)
{
return BadRequest(ex.Message);
}
try
{
string connectionString = ConfigurationManager.ConnectionStrings["DNATestConnectionString"].ConnectionString;
var items = GetDataItems(connectionString);
return Ok<IEnumerable<Orders>>(items);
}
catch (Exception ex)
{
return StatusCode(HttpStatusCode.InternalServerError);
}
}
#region Load Data Methods
private static List<Orders> GetDataItems(string connectionString)
{
List<Orders> items = new List<Orders>();
using (OracleConnection con = new OracleConnection(connectionString))
{
con.Open();
using (OracleCommand cmd = con.CreateCommand())
{
cmd.CommandText = "select po_header_id, segment1, vendor_id, vendor_site_id from po_headers_all where vendor_id=4993";
using (OracleDataReader rdr = cmd.ExecuteReader())
{
while (rdr.Read())
items.Add(ToOrders(rdr));
}
}
}
return items;
}
private static Orders ToOrders(OracleDataReader rdr)
{
Orders data = new Orders();
data.VENDOR_ID = ToInt32(rdr, "VENDOR_ID");
data.VENDOR_SITE_ID = ToInt32(rdr, "VENDOR_SITE_ID");
data.PO_HEADER_ID = ToInt32(rdr, "PO_HEADER_ID");
data.SEGMENT1 = Convert.ToString(rdr["SEGMENT1"]);
return data;
}
private static int ToInt32(OracleDataReader rdr, string name)
{
int index = rdr.GetOrdinal(name);
return rdr.IsDBNull(index) ? 0 : Convert.ToInt32(rdr[index]);
}
#endregion
I don't think this is possible.
How can i use custom sql and apply odata parameters to the underlying query?
As far as I'm aware, you can't. The whole point of the OData library is that it needs to work off an IQueryable. By using custom SQL in a string like you have in your example, you can't combine it with the OData parameters that are being passed in.
One approach would be to have your custom SQL in a SQL view, then add the SQL view to your EF model in the same way as you would add a table - it will be represented as a DbSet just like tables are.
You can then get an IQueryable to represent the dataset and then apply the OData parameters as follows:
public IHttpActionResult GetOrders(ODataQueryOptions<OrdersView> queryOptions)
{
IQueryable<OrdersView> allData = // ... get the DbSet from entity framework...
// this will apply the OData query to the data set and only pull the data you want from the database
var filteredResults = queryOptions.ApplyTo(allData) as IQueryable<OrdersView>;
return Ok<IQueryable<OrdersView>>(filteredResults);
}

DeleteDatabase is not supported by the provider, Oracle with Entity Framework

I'm using Model-First approach and Oracle database.
Update2: Fixed Now
On including seed data, I'm getting this error "DeleteDatabase is not supported by the provider"
UPDATE1 If I change seed data type from
public class MySeedData : DropCreateDatabaseAlways<ToolContext> to
public class MySeedData : DropCreateDatabaseIfModelChanges<ToolContext>
this error is replaced by another error:
Exception Model compatibility cannot be checked because the DbContext instance was not created using Code First patterns. DbContext instances created from an ObjectContext or using an EDMX file cannot be checked for compatibility
Seed Data
public class MySeedData : DropCreateDatabaseAlways<ToolContext>
{
protected override void Seed(ToolContext context)
{
base.Seed(context);
var category = new List<CategoryValue>
{
new CategoryValue{Id=1, Name = "Associate"},
new CategoryValue{Id =2, Name = "Professional"},
new CategoryValue{Id=3, Name = "Master"},
new CategoryValue{Id = 4, Name = "Product"},
new CategoryValue{Id = 5, Name = "Portfolio"}
};
category.ForEach(cert => context.CategoryValues.Add(cert));
context.SaveChanges();
}
}
Web.Config
<connectionStrings>
<add name="LMSPriorToolContext"
connectionString="metadata=res://*/Models.LMSPriorToolModel.csdl|res://*/Models.LMSPriorToolModel.ssdl|res://*/Models.LMSPriorToolModel.msl;
provider=Oracle.DataAccess.Client;
provider connection string="DATA SOURCE=DEV;PASSWORD=1234;PERSIST SECURITY INFO=True;USER ID=abc""
providerName="System.Data.EntityClient" />
</connectionStrings>
Application_Start()
Database.SetInitializer<ToolContext>(new SeedData());
Main.cs: EXCEPTION RAISED IN THIS FILE I know when you first try to access database, seed data method or scripts are executed.
using (var dbContext = new ToolContext())
{
var items = dbContext.CategoryValues;
foreach(CategoryValue category in **items**) // **Getting error here**
{
Console.WriteLine(category.Name);
}
}
REFERENCES Seems like I'm missing something here as there is nothing related to Oracle or ODAC
Stack Trace
at System.Data.Common.DbProviderServices.DbDeleteDatabase(DbConnection connection, Nullable`1 commandTimeout, StoreItemCollection storeItemCollection)
at System.Data.Objects.ObjectContext.DeleteDatabase()
at System.Data.Entity.Internal.DatabaseOperations.DeleteIfExists(ObjectContext objectContext)
at System.Data.Entity.Database.Delete()
at System.Data.Entity.DropCreateDatabaseAlways`1.InitializeDatabase(TContext context)
at System.Data.Entity.Database.<>c__DisplayClass2`1.<SetInitializerInternal>b__0(DbContext c)
at System.Data.Entity.Internal.InternalContext.<>c__DisplayClass8.<PerformDatabaseInitialization>b__6()
at System.Data.Entity.Internal.InternalContext.PerformInitializationAction(Action action)
Please suggest what I'm missing here.
Way of seeding data using DropCreateDatabaseAlways or DropCreateDatabaseIfModelChanges is not supported in Model-First approach.
Change seed data class to:
public class ToolSeedData : IDatabaseInitializer<ToolContext>
{
public void InitializeDatabase(ToolContext context)
{
var category = new List<CategoryValue>
{
new CategoryValue{Id=1, Name = "Associate"},
new CategoryValue{Id =2, Name = "Professional"},
new CategoryValue{Id=3, Name = "Master"},
new CategoryValue{Id = 4, Name = "Product"},
new CategoryValue{Id = 5, Name = "Portfolio"}
};
category.ForEach(cert => context.CategoryValues.Add(cert));
context.SaveChanges();
}
Possible error if you don't use it:
DeleteDatabase is not supported by the provider
Exception Model compatibility cannot be checked because the DbContext instance was not created using Code First patterns.
DbContext instances created from an ObjectContext or using an EDMX
file cannot be checked for compatibility
Microsoft link Seeding database does not work
Hope this helps someone else.

NHib 3 Configuration & Mapping returning empty results?

Note: I'm specifically not using Fluent NHibernate but am using 3.x's built-in mapping style. However, I am getting a blank recordset when I think I should be getting records returned.
I'm sure I'm doing something wrong and it's driving me up a wall. :)
Background / Setup
I have an Oracle 11g database for a product by IBM called Maximo
This product has a table called workorder which lists workorders; that table has a field called "wonum" which represents a unique work order number.
I have a "reporting" user which can access the table via the maximo schema
e.g. "select * from maximo.workorder"
I am using Oracle's Managed ODP.NET DLL to accomplish data tasks, and using it for the first time.
Things I've Tried
I created a basic console application to test this
I added the OracleManagedClientDriver.cs from the NHibernate.Driver on the master branch (it is not officially in the release I'm using).
I created a POCO called WorkorderBriefBrief, which only has a WorkorderNumber field.
I created a class map, WorkorderBriefBriefMap, which maps only that value as a read-only value.
I created a console application with console output to attempt to write the lines of work orders.
The session and transaction appear to open correct,
I tested a standard ODP.NET OracleConnection to my connection string
The Code
POCO: WorkorderBriefBrief.cs
namespace PEApps.Model.WorkorderQuery
{
public class WorkorderBriefBrief
{
public virtual string WorkorderNumber { get; set; }
}
}
Mapping: WorkorderBriefBriefMap.cs
using NHibernate.Mapping.ByCode;
using NHibernate.Mapping.ByCode.Conformist;
using PEApps.Model.WorkorderQuery;
namespace ConsoleTests
{
public class WorkorderBriefBriefMap : ClassMapping<WorkorderBriefBrief>
{
public WorkorderBriefBriefMap()
{
Schema("MAXIMO");
Table("WORKORDER");
Property(x=>x.WorkorderNumber, m =>
{
m.Access(Accessor.ReadOnly);
m.Column("WONUM");
});
}
}
}
Putting it Together: Program.cs
namespace ConsoleTests
{
class Program
{
static void Main(string[] args)
{
NHibernateProfiler.Initialize();
try
{
var cfg = new Configuration();
cfg
.DataBaseIntegration(db =>
{
db.ConnectionString = "[Redacted]";
db.Dialect<Oracle10gDialect>();
db.Driver<OracleManagedDataClientDriver>();
db.KeywordsAutoImport = Hbm2DDLKeyWords.AutoQuote;
db.BatchSize = 500;
db.LogSqlInConsole = true;
})
.AddAssembly(typeof(WorkorderBriefBriefMap).Assembly)
.SessionFactory().GenerateStatistics();
var factory = cfg.BuildSessionFactory();
List<WorkorderBriefBrief> query;
using (var session = factory.OpenSession())
{
Console.WriteLine("session opened");
Console.ReadLine();
using (var transaction = session.BeginTransaction())
{
Console.WriteLine("transaction opened");
Console.ReadLine();
query =
(from workorderbriefbrief in session.Query<WorkorderBriefBrief>() select workorderbriefbrief)
.ToList();
transaction.Commit();
Console.WriteLine("Transaction Committed");
}
}
Console.WriteLine("result length is {0}", query.Count);
Console.WriteLine("about to write WOs");
foreach (WorkorderBriefBrief wo in query)
{
Console.WriteLine("{0}", wo.WorkorderNumber);
}
Console.WriteLine("DONE!");
Console.ReadLine();
// Test a standard connection below
string constr = "[Redacted]";
OracleConnection con = new OracleConnection(constr);
con.Open();
Console.WriteLine("Connected to Oracle Database {0}, {1}", con.ServerVersion, con.DatabaseName.ToString());
con.Dispose();
Console.WriteLine("Press RETURN to exit.");
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine("Error : {0}", ex);
Console.ReadLine();
}
}
}
}
Thanks in advance for any help you can give!
Update
The following code (standard ADO.NET with OracleDataReader) works fine, returning the 16 workorder numbers that it should. To me, this points to my use of NHibernate more than the Oracle Managed ODP.NET. So I'm hoping it's just something stupid that I did above in the mapping or configuration.
// Test a standard connection below
string constr = "[Redacted]";
OracleConnection con = new Oracle.ManagedDataAccess.Client.OracleConnection(constr);
con.Open();
Console.WriteLine("Connected to Oracle Database {0}, {1}", con.ServerVersion, con.DatabaseName);
var cmd = new OracleCommand();
cmd.Connection = con;
cmd.CommandText = "select wonum from maximo.workorder where upper(reportedby) = 'MAXADMIN'";
cmd.CommandType = CommandType.Text;
Oracle.ManagedDataAccess.Client.OracleDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
Console.WriteLine(reader.GetString(0));
}
con.Dispose();
When configuring NHibernate, you need to tell it about your mappings.
I found the answer -- thanks to Oskar's initial suggestion, I realized it wasn't just that I hadn't added the assembly, I also needed to create a new mapper.
to do this, I added the following code to the configuration before building my session factory:
var mapper = new ModelMapper();
//define mappingType(s) -- could be an array; in my case it was just 1
var mappingType = typeof (WorkorderBriefBriefMap);
//use AddMappings instead if you're mapping an array
mapper.AddMapping(mappingType);
//add the compiled results of the mapper to the configuration
cfg.AddMapping(mapper.CompileMappingForAllExplicitlyAddedEntities());
var factory = cfg.BuildSessionFactory();

PrepareResponse().AsActionResult() throws unsupported exception DotNetOpenAuth CTP

Currently I'm developing an OAuth2 authorization server using DotNetOpenAuth CTP version. My authorization server is in asp.net MVC3, and it's based on the sample provided by the library. Everything works fine until the app reaches the point where the user authorizes the consumer client.
There's an action inside my OAuth controller which takes care of the authorization process, and is very similar to the equivalent action in the sample:
[Authorize, HttpPost, ValidateAntiForgeryToken]
public ActionResult AuthorizeResponse(bool isApproved)
{
var pendingRequest = this.authorizationServer.ReadAuthorizationRequest();
if (pendingRequest == null)
{
throw new HttpException((int)HttpStatusCode.BadRequest, "Missing authorization request.");
}
IDirectedProtocolMessage response;
if (isApproved)
{
var client = MvcApplication.DataContext.Clients.First(c => c.ClientIdentifier == pendingRequest.ClientIdentifier);
client.ClientAuthorizations.Add(
new ClientAuthorization
{
Scope = OAuthUtilities.JoinScopes(pendingRequest.Scope),
User = MvcApplication.LoggedInUser,
CreatedOn = DateTime.UtcNow,
});
MvcApplication.DataContext.SaveChanges();
response = this.authorizationServer.PrepareApproveAuthorizationRequest(pendingRequest, User.Identity.Name);
}
else
{
response = this.authorizationServer.PrepareRejectAuthorizationRequest(pendingRequest);
}
return this.authorizationServer.Channel.PrepareResponse(response).AsActionResult();
}
Everytime the program reaches this line:
this.authorizationServer.Channel.PrepareResponse(response).AsActionResult();
The system throws an exception which I have researched with no success. The exception is the following:
Only parameterless constructors and initializers are supported in LINQ to Entities.
The stack trace: http://pastebin.com/TibCax2t
The only thing I've done differently from the sample is that I used entity framework's code first approach, an I think the sample was done using a designer which autogenerated the entities.
Thank you in advance.
If you started from the example, the problem Andrew is talking about stays in DatabaseKeyNonceStore.cs. The exception is raised by one on these two methods:
public CryptoKey GetKey(string bucket, string handle) {
// It is critical that this lookup be case-sensitive, which can only be configured at the database.
var matches = from key in MvcApplication.DataContext.SymmetricCryptoKeys
where key.Bucket == bucket && key.Handle == handle
select new CryptoKey(key.Secret, key.ExpiresUtc.AsUtc());
return matches.FirstOrDefault();
}
public IEnumerable<KeyValuePair<string, CryptoKey>> GetKeys(string bucket) {
return from key in MvcApplication.DataContext.SymmetricCryptoKeys
where key.Bucket == bucket
orderby key.ExpiresUtc descending
select new KeyValuePair<string, CryptoKey>(key.Handle, new CryptoKey(key.Secret, key.ExpiresUtc.AsUtc()));
}
I've resolved moving initializations outside of the query:
public CryptoKey GetKey(string bucket, string handle) {
// It is critical that this lookup be case-sensitive, which can only be configured at the database.
var matches = from key in db.SymmetricCryptoKeys
where key.Bucket == bucket && key.Handle == handle
select key;
var match = matches.FirstOrDefault();
CryptoKey ck = new CryptoKey(match.Secret, match.ExpiresUtc.AsUtc());
return ck;
}
public IEnumerable<KeyValuePair<string, CryptoKey>> GetKeys(string bucket) {
var matches = from key in db.SymmetricCryptoKeys
where key.Bucket == bucket
orderby key.ExpiresUtc descending
select key;
List<KeyValuePair<string, CryptoKey>> en = new List<KeyValuePair<string, CryptoKey>>();
foreach (var key in matches)
en.Add(new KeyValuePair<string, CryptoKey>(key.Handle, new CryptoKey(key.Secret, key.ExpiresUtc.AsUtc())));
return en.AsEnumerable<KeyValuePair<string,CryptoKey>>();
}
I'm not sure that this is the best way, but it works!
It looks like your ICryptoKeyStore implementation may be attempting to store CryptoKey directly, but it's not a class that is compatible with the Entity framework (due to not have a public default constructor). Instead, define your own entity class for storing the data in CryptoKey and your ICryptoKeyStore is responsible to transition between the two data types for persistence and retrieval.

Resources