I have been through the tutorial on how to create mobile apps with azure and was able to deploy my middle ware and connect to a table called "todoitem" (which was from the tutorial) and I was able to write data to it and read from it.
(https://learn.microsoft.com/en-us/azure/developer/mobile-apps/azure-mobile-apps/quickstarts/maui/?pivots=vs2022-windows)
I was also able to connect with SSMS to the db and see the table and the items there.
Now I wanted to create my own table (useritem).
I copied everything from the tutorial, and tried creating data on my server but was returned: 500 internal server error.
I tried everything, but never was my table created, nor could I write to it.
Then I thought:
I can just create a table in SSMS via console. So I wrote a create table statement, that looked identical to the table form the todo items, only that I also added "email" as a property.
This went through fine and I saw my table in the tree in SSMS.
I could also query data from it (all from SSMS).
When I now tried to write data to my table from my app
await _table.InsertItemAsync(item);
I was finally no longer given "internal server error".
And via SSMS I could see that my data was put on the server!
Yeiks,
No where in the tutorial did it mention that I had to set the tables up myself but anyway, things where working..
UNTIL...
I tried quering data FROM the table FROM my app (not via SSMS, that works fine).
SO I did what I already did in the tutorial which was:
return await _table.GetAsyncItems().ToListAsync();
Which when done with the TODO item s(from the tutorial) returned a list with all items on that table.
BUt when I do it now with my own table, the app crashes, I dont even get an error message, it just doesnt continue.
So my guess it, that somehow, I missed setting up the connection to the table write.
With my workaround of creating the table in SSMS I was able to write to it from my app, but still cannot get my data returned.
What part of the tutorial was I missing?
Here is some of the code in question that should be important:
program.cs:
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("C5"); // set the connection string name that you set up (caused issues before)
if (connectionString == null)
{
throw new ApplicationException("DefaultConnection is not set");
}
builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(connectionString));
builder.Services.AddDatasyncControllers();
var app = builder.Build();
// Initialize the database
using (var scope = app.Services.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await context.InitializeDatabaseAsync().ConfigureAwait(false);
}
// Configure and run the web service.
app.MapControllers();
app.Run();
appDbcontext.cs:
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
/// <summary>
/// The dataset for the UserItems.
/// </summary>
public DbSet<UserItem> UserItems => Set<UserItem>();
/// <summary>
/// Do any database initialization required.
/// </summary>
/// <returns>A task that completes when the database is initialized</returns>
public async Task InitializeDatabaseAsync()
{
await this.Database.EnsureCreatedAsync().ConfigureAwait(false);
}
}
my controller:
[Route("tables/useritem")]
public class tblUserController : TableController<UserItem>
{
public tblUserController(AppDbContext context)
: base(new EntityTableRepository<UserItem>(context))
{
}
}
and ofcourse the modeL.
public class UserItem : EntityTableData
{
[Required, MinLength(1)]
public string Email { get; set; } = "";
public string Telephone { get; set; } = "";
public string Password { get; set; } = "";
}
EDIT:
Also I have noticed that when I look at the link for my middleware, instead of getting a list of the items (which was the case with the todo items) I get this:
Related
I've recently moved from MVC5 over to .NET Core 2.1 (MVC). Can anyone help me with this please.
I have my ApplicationUser and I've extended the model/table to store the user's FirstName.
In the View, I want to be able to output the current user firstname value.
User in the view is a ClaimsPrincipal so I need to go off to the DB to grab the value I need or access UserManager to get it.
Now, I know I can get that in the controller but I don't want to have to create a JQuery call to grab it every time I need it.
What I do want is to be able to access it server side, ideally via a static helper class.
In the MVC5 I'd have a helper to do the job no problem. Something like this for example:
public static string GetCurrentUserFirstName()
{
string _usrRef = HttpContext.Current.User.Identity.GetUserId();
var user = HttpContext.Current.GetOwinContext().GetUserManager<ApplicationUserManager>().FindById(_usrRef);
return user.FirstName;
}
However, .NET Core doesn't work that way.
In a controller I could say:
var user = await _userManager.GetUserAsync(User);
string firstName = user.FirstName;
or I could go off to the DB via a call using Dapper w/ my connection string.
I can't inject the UserManager or ConnectionStrings into the helper via the constructor as it is static.
Is there a way to access either of those in this static helper?
It's the little changes that get you the most!
Thanks to #Kirk Larkin - I've found the solution.
I have to admit, it feels a little more convoluted having to pass things around to gain access to them but this is a good, working solution.
The View:
#using Microsoft.AspNetCore.Identity
#using MyApplication.Helpers
#inject UserManager<ApplicationUser> UserManager
<div>
#await MyHelper.GetLoggedInUserFirstName(UserManager, User)
</div>
The MyHelper file:
public static async Task<string> GetLoggedInUserFirstName(UserManager<ApplicationUser> userManager, ClaimsPrincipal user)
{
string output = "";
try
{
var currentUser = await userManager.GetUserAsync(user);
if(currentUser!=null)
{
output = currentUser.FirstName ?? currentUser.Email;
}
}
catch(Exception e) { }
return output;
}
My understanding of the TempData attribute in asp.net core is that it marks a model property as needing to persist in value between posts and gets of same page (and possibly wider lifetime than that, but at least that).
My issue is that any property I have marked as TempData and set successfully in OnGetAsync has been reset to null by the time user posts back the form. Why might that be?
Or have I misunderstood what TempData is supposed to do as an attribute? If I have, what's the best way to acheive what I'm trying to do? Pass the phone number to the view and then post it back to OnPostAsync?!?
public class MyPageModel : PageModel
{
[TempData] public string PhoneNumber { get; set; }
public async Task<IActionResult> OnGetAsync(string phoneNumber)
{
PhoneNumber = phoneNumber; //THIS IS WORKING
return Page();
}
public async Task<IActionResult> OnPostAsync()
{
user.PhoneNumber = PhoneNumber; //BUT BY HERE PHONENUMBER is NULL?
}
}
//In Startup.ConfigureServices // I added the CookieTempDataProvider which I did'nt have before, but I believe that CookieTempDataProvider is enabled by default: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/app-state?view=aspnetcore-2.1#tempdata
services.AddSingleton<ITempDataProvider, CookieTempDataProvider>();
services.AddSession(options =>
{
//// Set a short timeout for easy testing.
//options.IdleTimeout = TimeSpan.FromSeconds(10);
options.Cookie.HttpOnly = true;
});
Thoughts?!
OK I finally see what my problem was. I had the CookiePolicyOptions options.CheckConsentNeeded lambda still in place, even though I had taken out the CookieConsentPartial view. So, I assume that the cookies that provided the backing for TempData were not getting set because effectively, I had not consented to them.
For reference, after sorting out the below, I did not need to have ANYTHING TempData or Cookie related in my Configure or ConfigureServices. Because as per Session and app state in ASP.NET Core article:
In ASP.NET Core 2.0 or later, the cookie-based TempData provider is used by default to store TempData in cookies.
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
// options.CheckConsentNeeded = context => true; // WORKED FINE WHEN I COMMENTED IT OUT. DID NOT WORK WHEN COMMENTED IN.
options.MinimumSameSitePolicy = SameSiteMode.None;
});
If you want to bind a property you should set attribute to it [BindProperty] and that should work for post actions:
e.g.
[BindProperty]
public string PhoneNumber { get; set; }
I have the following dependency chain:
IUserAppService
IUserDomainService
IUserRepository
IUserDataContext - UserDataContextImpl(string conn)
All interfaces above and implementations are registered in a Windsor Castle container. When I use one connection string, everything works fine.
Now we want to support multiple databases, In UserAppServiceImpl.cs, we want to get different IUserRepository (different IUserDatabaseContext) according to userId as below:
// UserAppServiceImpl.cs
public UserInfo GetUserInfo(long userId)
{
var connStr = userId % 2 == 0 ? "conn1" : "conn2";
//var repo = container.Resolve<IUserRepository>(....)
}
How can I pass the argument connStr to UserDataContextImpl?
Since the connection string is runtime data in your case, it should not be injected directly into the constructor of your components, as explained here. Since however the connection string is contextual data, it would be awkward to pass it along all public methods in your object graph.
Instead, you should hide it behind an abstraction that allows you to retrieve the proper value for the current request. For instance:
public interface ISqlConnectionFactory
{
SqlConnection Open();
}
An implementation of the ISqlConnectionFactory itself could depend on a dependency that allows retrieving the current user id:
public interface IUserContext
{
int UserId { get; }
}
Such connection factory might therefore look like this:
public class SqlConnectionFactory : ISqlConnectionFactory
{
private readonly IUserContext userContext;
private readonly string con1;
private readonly string con2;
public SqlConnectionFactory(IUserContext userContext,
string con1, string con2) {
...
}
public SqlConnection Open() {
var connStr = userContext.UserId % 2 == 0 ? "conn1" : "conn2";
var con = new SqlConnection(connStr);
con.Open();
return con;
}
}
This leaves us with an IUserContext implementation. Such implementation will depend on the type of application we are building. For ASP.NET it might look like this:
public class AspNetUserContext : IUserContext
{
public string UserId => int.Parse(HttpContext.Current.Session["UserId"]);
}
You have to start from the beginning of your dependency resolver and resolve all of your derived dependencies to a "named" resolution.
Github code link:https://github.com/castleproject/Windsor/blob/master/docs/inline-dependencies.md
Example:
I have my IDataContext for MSSQL and another for MySQL.
This example is in Unity, but I am sure Windsor can do this.
container.RegisterType(Of IDataContextAsync, dbEntities)("db", New InjectionConstructor())
container.RegisterType(Of IUnitOfWorkAsync, UnitOfWork)("UnitOfWork", New InjectionConstructor(New ResolvedParameter(Of IDataContextAsync)("db")))
'Exceptions example
container.RegisterType(Of IRepositoryAsync(Of Exception), Repository(Of Exception))("iExceptionRepository",
New InjectionConstructor(New ResolvedParameter(Of IDataContextAsync)("db"),
New ResolvedParameter(Of IUnitOfWorkAsync)("UnitOfWork")))
sql container
container.RegisterType(Of IDataContextAsync, DataMart)(New HierarchicalLifetimeManager)
container.RegisterType(Of IUnitOfWorkAsync, UnitOfWork)(New HierarchicalLifetimeManager)
'brands
container.RegisterType(Of IRepositoryAsync(Of Brand), Repository(Of Brand))
controller code:
No changes required at the controller level.
results:
I can now have my MSSQL context do its work and MySQL do its work without any developer having to understand my container configuration. The developer simply consumes the correct service and everything is implemented.
I'm haing a few problems updating a row in my database using Linq2Sql.
Inside of my model I have two methods for updating and saving from my controller, which in turn receives an updated model from my view.
My model methods like like:
public void Update(Activity activity)
{
_db.Activities.InsertOnSubmit(activity);
}
public void Save()
{
_db.SubmitChanges();
}
and the code in my Controller likes like:
[HttpPost]
public ActionResult Edit(Activity activity)
{
if (ModelState.IsValid)
{
UpdateModel<Activity>(activity);
_activitiesModel.Update(activity);
_activitiesModel.Save();
}
return View(activity);
}
The problem I'm having is that this code inserts a new entry into the database, even though the model item i'm inserting-on-submit contains a primary key field.
I've also tried re-attaching the model object back to the data source but this throws an error because the item already exists.
Any pointers in the right direction will be greatly appreciated.
UPDATE:
I'm using dependancy injection to instantiate my datacontext object as follows:
IMyDataContext _db;
public ActivitiesModel(IMyDataContext db)
{
_db = db;
}
There should be an insert in case of the InsertOnSubmit method usage, this is an expected behaviour.
We recommend the Attach() method usage in your Update() method implementation. In case you have IsVersion column in the entity then everything is simple, in the other case you will have to pass the original values also to the Attach call. More information is available here in MSDN.
I fixed this issue by re-obtaining and updating my object in the Update method.
Instead of trying to re-attach or get the data context to realise it was the same object that belonged to it before I basically did as follows:
[HttpPost]
public ActionResult Edit(Activity activity)
{
Activity myActivity = activitiesModel.getActivityById(activity.id);
myActivity.name = activity.name;
myActivity.date = activity.date;
_dbContext.SubmitChanges();
return View(activity);
}
This isn't my exact code and to be more precise, I created another partial class to my datacontext and stored my update code in there.
Right now i'm exploring LINQ-to-SQL using Visual Studio 2010 beta. I'm trying to understood the bascis for now, without magics light code auto generated from schemes and from sqlmetal. I have tried the following code:
public class Database : DataContext
{
public Database( string s ) : base( s ) {}
public Table< DUser > items;
}
[Table( Name = "users" )]
public class Item
{
[Column]
public string s;
}
// Using SQL Compact.
var db = new Database( "Data Source=test.sdf;" );
// Works fine and creates database.
db.CreateDatabase();
But how to ADD data to database created / opened? Tutorials i have read shows something like db.Items.Add(), but Table<> don't have Add() member :(. Any insigths without using scheme / sqlmetal?
You're looking for the Table<T>.InsertOnSubmit method (followed of course by DataContext.SubmitChanges). Similarly DeleteOnSubmit for deletions.