I am trying to make a mobile app, which will use the Azure database system. I am having alot of trouble making my own table, and have been running in coding circles for a couple of weeks. I just can't figure out what and how to change.
I can get the todolist up and running from azure, and i have tried to make my own table in the backend with a dataobject and a controller, but after adding the DbSet om the context, the todolist part breaks when i try to run the app.
How do i add my own stuff to the app, so that i can have a table of persons for example, instead of the todolist?
Thank you so much in advance. this is very confusing to me.
This is what i've done:
In the backend, i made a person class inhereting the EntityData class and have a firstname string property and a lastname string property
Then i added
public DbSet<Person> Persons { get; set; }
and then a Personcontroller through the Add -> Controller -> Azure Mobile
Apps Table Controller in visual studio 2017
Then in the app i downloaded from azure, i made the person class
public class Person
{
[JsonProperty(PropertyName = "firstName")]
public string firstName { get; set; }
[JsonProperty(PropertyName = "lastName")]
public string lastName { get; set; }
[JsonProperty(PropertyName = "id")]
public string id { get; set; }
}
Then made the table
IMobileServiceTable<Person> PersonTable = client.GetTable<Person>();
Then tried to insert into the table
Person peter = new Person();
peter.firstName = "Peter";
peter.lastName = "Friis";
await personTable.InsertAsync(peter);
but that gives the error:
Microsoft.WindowsAzure.MobileServices.MobileServiceInvalidOperationException:
'The request could not be completed. (Internal Server Error)'
According to your description, I assumed that you are using C# backend with SQL database. I would recommend that you could add the following code under the ConfigureMobileApp method of Startup.MobileApp.cs file for collecting the detailed error message.
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
Before inserting the new record to your table via the mobile client SDK, you could leverage the postman or fiddler to simulate the insert operation as follows to narrow this issue:
For more details about http table interface, you could refer to here.
Additionally, since you are adding your custom tables, please make sure you have manually updated your database to support your new database model or configure the Automatic Code First Migrations. For more details, you could refer to adrian hall's book about Implementing Table Controllers.
Related
I have already a model class called User.cs with some properties:
[JsonProperty(PropertyName = "admin")]
public bool Admin { get; set; }
[JsonProperty(PropertyName = "email")]
public string Email { get; set; }
This gives me the columns admin and email in the sqlite database.
Now I am making an update for my app. I want to add a new property and therefore a new column:
[JsonProperty(PropertyName = "owner")]
public bool Owner { get; set; }
When I add this to User.cs, the sqlite file will get a new column called owner. All existing rows in the User table of the sqlite file get the value NULL for this new column.
My question: What can I do give existing rows the value 0 (false).
There are a couple of things you can do, depending on requirements. The most obvious is:
Update the model on the server to add the Owner field,
Update the database with a default value.
On first run of your client (after an update), wipe the SQLite database and re-sync all data
I'm trying to create an API and a website client for it. Lately I've been reading a lot about OAuth2 as a security mechanism and companies that offers authentication as a service such as auth0.com or even Azure active Directory and I can see the advantages in using them
Because I'm used to always having the users in the same database and tables with relationships to the Users table in the form of One to Many such as below
public class User
{
public string subjectId { get; set; }
public virtual List<Invoice> Invoices { get; set; }
/*
More properties in here
*/
}
public class Invoice
{
public int InvoiceId { get; set; }
public string PaymentNumber { get; set; }
public DateTime Date { get; set; }
public double Amount { get; set; }
public string Description { get; set; }
public virtual User User { get; set; }
}
My questions is then.
If the users are stored in an external authentication service such as Auth0.com,
How the Invoice class will handle the relation to the user?
Would it be just adding a new property subjectId in the Invoice table and this will take the value of whatever id the authentication service assigned?
In the latter case, would the class Invoice be something like below?
public class Invoice
{
public int InvoiceId { get; set; }
public string PaymentNumber { get; set; }
public DateTime Date { get; set; }
public double Amount { get; set; }
public string Description { get; set; }
public string SubjectId{get;set;}
}
Also, if the users are stored someplace else, how do you make a query like,
Select * from Users u inner join Invoices i where Users.Name='John Doe' and i.Date>Somedate.
Since you have mentioned Auth0 as your Identity provider there are multiple ways to achieve the user table in your database.
1. Authenticating/ registering the user with Auth0 will send a response with Profile Object which will have all the basic profile information you need. Post this profile object back to your own API to save it to database. This API endpoint should be secured with the access token you received along with the profile object from Auth0.
2. You can create a custom rule in Auth0 that posts the user information back to your api. This rule gets executed on Auth0 server so this is a secure call.
3. Identity providers (Auth0 in our case) are required to expose an API endpoint that gives us user profile data (ex: https://yourdoamin.auth0.com/userinfo). You can make a call to this endpoint from your API to receive the user information.
When user Registers to your application, please use one of these techniques to establish a User profile information table in your database. It is always a good idea to treat the Identity Provider as a service responsible for authenticating the resource owner (the user of your application) and providing an access token for securely accessing your API/ application. If you have the profile of the user in your database, you do not have to depend on the Identity Provider once the user is authenticated.
Please let me know if you have any further questions.
Thank you,
Soma.
We have a similar setup for our website. We use Passport for our user database and our website doesn't have a user table at all. This makes life much simpler than having a a bunch of duplicate data between Passport and our website. I'll use our code as an example of what you are doing and hopefully it makes sense.
Our website has a License object that looks like this (Java not C#, but they are similar):
public class License {
public String companyName;
public List<User> users;
}
The License table looks like this (trimmed down):
CREATE TABLE licenses (
id UUID NOT NULL,
company_name VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
The License identifies the users that are associated with it via a join table like this (Passport uses UUIDs for user ids making life simple again):
CREATE TABLE users_licenses (
users_id UUID NOT NULL,
licenses_id UUID NOT NULL,
PRIMARY KEY (users_id, licenses_id),
CONSTRAINT users_licenses_fk_1 FOREIGN KEY (licenses_id) REFERENCES licenses (id)
);
Then we can select in either direction. If we know the user id, we can ask for all their licenses like this:
select * from licenses where users_id = ?
Or if we know the license id, we can ask for all the users that have access to the license:
select * from users_licenses where licenses_id = ?
Once we have one or more user ids, we can call the Passport /api/user endpoint or the /api/user/search endpoint to retrieve one or more user objects. We are actually using the Passport Java Client (https://github.com/inversoft/passport-java-client) which makes the API call for us and then returns a List<User>. This is what is stored in the License class from above. That code looks like this:
License license = licenseMapper.retrieveById(licenseId);
List<UUID> userIds = licenseMapper.retrieveUserIdsFor(licenseId);
ClientResponse<SearchResponse, Errors> clientResponse = passportClient.searchUsers(userIds);
license.users = clientResponse.successResponse.users;
LicenseMapper is a MyBatis interface that executes the SQL and returns the License objects. C# ORMs use LINQ, but it would be similar.
The nice thing about this setup is that we don't have a user database table in our website database that we have to keep in sync. Everything is loaded from Passport via the API. We aren't ever concerned about performance either. Passport is on-premise and can do thousands of user lookups each second, so we always load the data instead of caching it.
The only piece of your question that requires additional code is handling the joins when you are searching for arbitrary users like name='John Doe'. The only way to handle this is to query your user database first, retrieve all the IDs, then load their invoices. This seems like it could be dangerous if you have a large user database, but still doable.
That could would look like this in our situation:
UserSearchCriteria criteria = new UserSearchCriteria().withName("John Doe");
ClientResponse<SearchResponse, Errors> clientResponse = passportClient.searchUsersByQueryString(criteria);
List<User> users = clientResponse.successResponse.users;
Set<License> licenses = new HashSet<>();
for (User user : users) {
licenses.addAll(licenseMapper.retrieveByUserId(user.id));
}
So I am trying to grasp EF6 and it's use of Identity 2.0 for making a many to many relationship. It is Visual Studio 2013 and the MVC 5 template.
I have a fresh MVC app with the following models:
public class Meeting
{
public Guid MeetingID { get; set; }
public string Title { get; set; }
public virtual ICollection<ApplicationUser> Attendees { get; set; }
}
public class ApplicationUser : IdentityUser
{
public ICollection<Meeting> Meetings { get; set; }
}
Then I scaffold a controller and views for Meetings. Now, for instance, if I just wanted to add every user as an attendee to my meeting, I would imagine that I could modify the Create action to look like the following:
public ActionResult Create(Meeting meeting)
{
if (ModelState.IsValid)
{
meeting.MeetingID = Guid.NewGuid();
db.Users.ForEachAsync(u => meeting.Attendees.Add(u));
db.Meetings.Add(meeting);
db.SaveChanges();
return RedirectToAction("Index");
}
else...
}
However I don't think it's working because I don't see it in my LocalDB and if I add this to the detail view for a meeting I get no results:
#{foreach (var item in Model.Attendees)
{
<li>#item.UserName</li>
}}
As a final note, I have two users in the LocalDB, test and test2.
What tutorial or documentation will allow me to make this work?
* Edit *
So I have tried your suggestion (I'll admit, I am unfamiliar with async and await and how to implement it), and I had to modify the controller to allow me to use await so I'm not sure if I'm doing this correctly now, but I got the following to compile and I get run time error of 'object reference not set to an instance of an object' :
public async Task<ActionResult> Create(Meeting meeting)
{
if (ModelState.IsValid)
{
meeting.MeetingID = Guid.NewGuid();
await db.Users.ForEachAsync(u => meeting.Attendees.Add(u));
db.Meetings.Add(meeting);
db.SaveChanges();
(is it possible I'm missing some setup of my model on Entity Framework? The project is exactly the code shown above plus defaults.)
You're going to kick yourself :)
(Drumroll)
You forgot to add await before your ForEachAsync line:
await db.Users.ForEachAsync(u => meeting.Attendees.Add(u));
Without await the application happily continues on and saves the record, all before that async process has completed.
UPDATE
Most likely you haven't initialized the Attendees collection. Just set it to a new List<ApplicationUser> in your constructor.
I recently updated the nuget package for Microsoft.WindowsAzure.Storage to the 3.0 package which also included updates to WCF Data Services Client and it's dependencies. Since the update I get an error when the query is resolving stating:
"There is a type mismatch between the client and the service. Type
'ShiftDigital.Flow.Data.RouteDiagnostic' is not an entity type, but
the type in the response payload represents an entity type. Please
ensure that types defined on the client match the data model of the
service, or update the service reference on the client."
I've done nothing but update the packages and both my application along with a test script I setup in LinqPad generate this exception.
Here is the definition of the entity I've been returning just fine before the update
public class RouteDiagnostic : TableEntity
{
public long? LeadRecipientRouteId { get; set; }
public bool Successful { get; set; }
public int Duration { get; set; }
public string Request { get; set; }
public string Response { get; set; }
public RouteDiagnostic()
: base()
{
this.Timestamp = DateTimeOffset.Now;
this.PartitionKey = GetPartitionKey(this.Timestamp.Date);
this.RowKey = Guid.NewGuid().ToString();
}
public static string GetPartitionKey(DateTime? keyDateTime = null)
{
return string.Format("{0:yyyyyMM}", keyDateTime ?? DateTime.Now);
}
}
Here is the code performing the query
var storageAccount = Microsoft.WindowsAzure.Storage.CloudStorageAccount.Parse("...");
var tableClient = storageAccount.CreateCloudTableClient();
var tableContext = new Microsoft.WindowsAzure.Storage.Table.DataServices.TableServiceContext(tableClient);
var diagnostics =
tableContext.CreateQuery<RouteDiagnostic>("RouteDiagnostic")
.Where(rd => rd.PartitionKey == "0201401")
.ToList();
Has something changed that in the latest update or a different way to structure the entities when using data service queries?
Turns out with the update to WCF Data Services 5.6 I needed to add the following attribute to my type:
[DataServiceKey("PartitionKey", "RowKey")]
Once I added the DataServiceKey attribute, all was well again.
When using WCF Data Services, please make your class inherit from TableServiceEntity rather than TableEntity, which already has the DataServiceKey attribute defined. TableEntity is used for the new Table Service Layer in the Windows Azure Storage Client Library. For more information on the new Table Service Layer, please see our blog post.
I allowed the WAMS wizard to create the "test" table ("Items") it so congenially offers to create after setting up my WAMS.
Then I wanted to create a table that will actually be useful to me. The instructions in the wizard do say, "You can add and remove tables later by using the "Data" tab above."
So I did that, and I did create a table, but I can't see now where I can change the structure of the table (IOW, add columns). I've tried 2-clicking the service, 2-clicking the table, selecting the "New" button, right-clicking on the sole column name (id) in my table, etc., but all to no avail.
Something I'm confused about, too, is the relationship of the tables I create this way with my existing SQL DB tables - or can I do without that SQL DB now (once I get these WAMSical tables set up)?
Or why can't I associate my existing SQL DB tables to my WAMS? And if I can -- how?
UPDATE
Also, it seems there is a mismatch between what is written and what I'm actually experiencing. This (from http://msdn.microsoft.com/en-us/magazine/jj721590.aspx) is not true/did not happen for me:
"2.Create a relational table to store your data. When you click on the Create TodoItem Table button, the wizard will automatically create a table based on the Windows Azure SQL Database you created (or re-used) previously."
I tried "from scratch," creating a new WAMS. Again, I get when I select my existing SQL DB, "Database and Mobile Service Not in the Same Region - Performance will decrease...Additionally, the data sent from the db to the mobile service will be counted as billable bandwidth usage. We recommend that you choose a database in the same location as the mobile service."
I would like to, but how? Why didn't WAMS adjust this for me automatically - or at least give me the option to put my DB and Mobile Service in the same place?
UPDATE 2
What is interesting is that I CAN see the new tables in LINQPad. I already had two SQL DB tables that display under that connection info, but on the same level as those tables is my WAMS name, under which are the "default" Items table and one of my own I created (both of which, though, only have one column, specifically "Id (Int64)"
IOW, what I see in LINQPad is:
blaBlaBla.database.windows.net,1433.blaBla
BlaBla
BlaBlaSQLDB_Table1
BlaBlaSQLDB_Table2
wamsName
Items
Id (Int64)
BlaBlaWAMSTable
Id (Int64)
...so how do I extend/manage the "BlaBlaWAMSTable" is the problem now...
UPDATE 3
Well, looky here; LINQPad to the rescue again:
select * from <WAMSName>.<TableName>
...shows there is a record after I created the table I wanted via a class in my project (NOT in the Azure/WAMS management area)
...and, of course, LINQPad shows the newly added columns added that way.
All I needed to do was follow the steps provided (Reference the Azure SDK, add a corresponding using clause, etc.) and then added this method to test it out:
private async void InsertTestRecordIntoWAMSSQLDBTable()
{
<WAMS Table class name> invitation = new <WAMS Table class name> { SenderID = "donkeyKongSioux#supermax.gov", ReaderDeviceID = "00-AA-11-BB-01-AB-10-BA", ReaderName = "B. Clay Shannon", SenderUTCOffset = 5, SenderDeviceID = "BA-10-AB-01-BB-11-AA-00" };
await App.MobileService.GetTable<<WAMS Table class name>>().InsertAsync(invitation);
}
...and it worked. Now it would be nice to have some samples/examples for select queries as well as updates.
And my big remaining question (so far): can I decorate/annotate the columns/members of my table class? IOW, can I change this:
public class
{
public int Id { get; set; }
public string SenderID { get; set; }
public string ReaderDeviceID { get; set; }
public string ReaderName { get; set; }
public int SenderUTCOffset { get; set; }
public string SenderDeviceID { get; set; }
}
...to something like this:
public class
{
[Primary, AutoInc]
public int Id { get; set; }
[Indexed]
public string SenderID { get; set; }
[Unique]
public string ReaderDeviceID { get; set; }
[MaxLength(255)]
public string ReaderName { get; set; }
public int SenderUTCOffset { get; set; }
public string SenderDeviceID { get; set; }
}
?
I can't do exactly that, as those are SQLite annotations, but since I am unable to manage my table from the Azure/WAMS portal, how can I designate these attributes?
After altering the design of the table in code, I'm able to see that those columns have been added to my table in the WAMS portal, but it seems the only thing I can do to the columns is add an index...
UPDATE 4
It turns out that creating tables in WAMS is as easy as pie (but not as easy as pi/as hard as pi) - once you know how to do it.
With the WAMS created, select Data, and then Create to create a table. Give it a name, and select the permissions you want. This will give you a deadly dull but "living" database table with one, count 'em, one, column: ID, a BigInt, indexed.
THEN, to actually add more columns to the table, the easiest way I've found (the only way I've found that is, and it is easy) is to:
1) Create a class that corresponds to the database table, a la SQLite, such as:
public class WAMS_DUCKBILL
{
public int Id { get; set; }
public string PlatypusID { get; set; }
public DateTime UpdateTimeUTC { get; set; }
public double Income { get; set; }
public double Outgo { get; set; }
}
2) Write a method that will add a record to this table, such as:
private async void InsertTestRecordIntoWAMSDuckbillTable()
{
WAMS_DUCKBILL duckbill = new WAMS_DUCKBILL { PlatypusID = "42", UpdateTimeUTC =
DateTime.Now, Income = 3.85, Outgo = 8311.79 };
await MobileService.GetTable<WAMS_DUCKBILL>().InsertAsync(duckbill);
}
3) Call that method from App.xaml.cs' OnLaunched event
4) As can then be seen by running the following query in LINQPad (or however you want to pee[k,r] into the database):
SELECT * FROM platypi.WAMS_DUCKBILL
Call me old fashioned, but notice I'm using tired old SQL as opposed to LINQ here; so sue me. At any rate, LINQPad shows that the test record has indeed been inserted into the WAMS table. Voila! as the escargot-eating, beret-at-a-rakish-angle wearing cats say.
The focus for Windows Azure Mobile Services (WAMS) are app developers who does not want to invest a lot of time to develop a backend. Therefor the data model easier than you might think.
Tables in WAMS are always backed by tables in a SQL database. You can create and delete tables via the portal.
Creating columns is a bit different. You create columns by simply using them. As soon as you write data for a column that does not exist, WAMS creates that column automatically. That's called Dynamic Schema. After development you should disable Dynamic Schema. You find it in the Mobile Service, Configure.
Documentation: Data access in Windows Azure Mobile Services