I am trying to create mobile app using Xamarin and Realm. I am trying to pull data from server and load it into objects extended by RealmObjects.
code to fetch data from server
public async void getCompanyMaster() {
var uri = new Uri(string.Format(URL, string.Empty));
try
{
getConnection();
var response = await client.GetAsync(uri);
if (response.IsSuccessStatusCode)
{
var content = await response.Content.ReadAsStringAsync();
Company company = JsonConvert.DeserializeObject<Company>(content);
Debug.WriteLine("Company List " );
}
}
catch (Exception e)
{
Debug.WriteLine("Error {0}", e.Message);
}
}
realm model company
namespace DesignModel.Model.Master
{
public class Company : RealmObject
{
public string errorMessage { get; set; }
public string status { get; set; }
[JsonProperty(PropertyName = "companyMaster")]
IList<CompanyMaster> companyMaster { get; }
}
}
realm model for company master
namespace DesignModel.Model.Master
{
public class CompanyMaster : RealmObject
{
public string companyAddress { get; set; }
public string companyId { get; set; }
public string companyName { get; set; }
public string companyPhoneNumber { get; set; }
}
}
now the issue is at following line in getCompanyMaster function
Company company = JsonConvert.DeserializeObject<Company>(content);
it raises an exception
Error Error setting value to 'errorMessage' on
'DesignModel.Model.Master.Company'.
if i do not extend my class from RealmObject then it works fine but when i add RealObject it doesn't work.
the above approach does work in android native but do not know why it doesn't work in xamarin.
sample json
{
"errorMessage": "",
"status": "Ok",
"companyMaster": [
{
"companyAddress": "123 Coffee Street\nSuite 300\nRedmond, WA 98052\nUSA",
"companyId": "cec",
"companyName": "Contoso Entertainment Systems",
"companyPhoneNumber": "425-555-0156"
}]
}
complete stack trace
[0:] Error Newtonsoft.Json.JsonSerializationException: Error setting
value to 'errorMessage' on 'DesignModel.Model.Master.Company'. --->
System.PlatformNotSupportedException: The PCL build of Realm is being
linked which probably means you need to use NuGet or otherwise link a
platform-specific Realm.dll to your main application.
Adding a note from documentation found here
https://realm.io/docs/xamarin/latest/
under Getting Started - Prerequisites
Important note for PCL users - this works via the NuGet Bait and Switch
Trick. You
have to install the Realm NuGet package into every PCL that uses Realm
as well as each of your platform-specific projects, e.g.: your final
app for iOS and Android. If you are using shared projects, you only
have to install the NuGet into each platform-specific project.
Related
I am creating a custom workflow in Microsoft Dynamics CRM to automatically update a field when a record is saved.
A developer on a forum provided the following source code; but he is not responding to my questions.
public class SalesRepActivity2 : WorkFlowActivityBase
{
[Input("Sales Rep Name")]
public InArgument<string> SalesRepName { get; set; }
[Output("Sales Rep")]
[ReferenceTarget("systemuser")]
public OutArgument<EntityReference> SalesRep { get; set; }
[Output("IsSuccess")]
public OutArgument<bool> IsSuccess { get; set; }
[Output("Message")]
public OutArgument<string> Message { get; set; }
protected override void Execute(
CodeActivityContext activityContext,
IWorkflowContext workflowContext,
IOrganizationService CrmService,
ITracingService trace)
{
try
{
string salesRepName = SalesRepName.Get(activityContext);
if (string.IsNullOrWhiteSpace(salesRepName))
{
IsSuccess.Set(activityContext, false);
Message.Set(activityContext, "Sales Rep Name not provided");
}
var QEsystemuser = new QueryExpression("systemuser");
QEsystemuser.ColumnSet.AddColumns("salesrepname");
QEsystemuser.Criteria.AddCondition("salesrepname", ConditionOperator.Equal, salesRepName);
var results = CrmService.RetrieveMultiple(QEsystemuser);
if (results == null || !results.Entities.Any())
{
IsSuccess.Set(activityContext, false);
Message.Set(activityContext, "User with " + salesRepName + " not found");
return;
}
if (results.Entities.Count > 1)
{
IsSuccess.Set(activityContext, false);
Message.Set(activityContext, "Multiple users found with same name : " + salesRepName);
return;
}
IsSuccess.Set(activityContext, true);
SalesRep.Set(activityContext, results.Entities.Single().ToEntityReference());
}
catch (Exception ex)
{
IsSuccess.Set(activityContext, false);
Message.Set(activityContext, "An error occurred trying to find user : " + ex.Message);
}
}
I am trying to get the code to compile on my machine.
I installed the following NuGet packages, which resolved most of the errors:
Microsoft.Xrm.Sdk.Workflow.2015
Microsoft.Xrm.Sdk.2015
But my project cannot resolve the WorkFlowActivityBase class.
Is there a reference I should set or a NuGet package I should install to resolve this?
Thank you.
WorkFlowActivityBase is a custom base class that implements the CodeActivity (System.Activities) anyone can write, it's not an official Dynamics class. Yon can find dozen of classes lie that in the web.
Basically, you should use the CodeActivity. here as an example:
https://learn.microsoft.com/en-us/powerapps/developer/common-data-service/workflow/sample-create-custom-workflow-activity
The code you posted is not a good place for you to start because it makes heavy use of proprietary objects. Ziv has provided good information about extending the CodeActivity class but I do not recommend starting there. Instead read about how to develop custom workflow activities, and write a workflow using the base Microsoft classes so that you understand how they work:
https://learn.microsoft.com/en-us/powerapps/developer/common-data-service/workflow/workflow-extensions
Once you have some experience with creating custom workflows, and you understand the limitations of the base objects, then go back and implement something more fancy.
Here in an exaple from the documentation:
namespace Microsoft.Crm.Sdk.Samples
{
public sealed class SimpleSdkActivity : CodeActivity
{
protected override void Execute(CodeActivityContext executionContext)
{
//Create the tracing service
ITracingService tracingService = executionContext.GetExtension<ITracingService>();
//Create the context
IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();
IOrganizationServiceFactory serviceFactory = executionContext.GetExtension<IOrganizationServiceFactory>();
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
...
var systemUsers = service.RetrieveMultiple(QEsystemuser)
}
}
}
Context: I am handed this massive enterprise iOS & Android Xamarin Native applications that doesn't use ViewModels or any MVVM framework. It does have a separated Network services layer and it has a lot of pages, so starting over won't make any sense.
Why the change is needed: No MVVM, the services layer is called directly from the UI classes (Fragments & ViewControllers), and there is no good abstraction. So I will start with 1 view and then create TechDebt to transform the rest of the app.
What I know:
Adding MVVM frameworks require creating extensive changes, especially to use Navigation services for Navigating the views, and are best done if added when green-fielding the application.
As seen here, Android has an easy way of using a ViewModel but I won't be able to use that for iOS then.
I also know that I can launch a Xamarin Forms page instead and that will be all ready for MVVM, since I can just assign the BindingContext property to an instance of the ViewModel.
What I need: I need to create one new page for iOS & one for Android. I want to be able to create a ViewModel that's shared between iOS & Android. I want to be able to use it for a single view that I am creating and it should be initialized when the page is loaded.
How can I add 1 viewmodel that's shared by a ViewController & a Fragment? Am I missing something, is it much easier than I am making it?
Ended up being able to use MvvmLight for this. Added the Nuget package to the projects, Created a ViewModelBase in the Core Shared Library Project:
public abstract class ViewModelBase : GalaSoft.MvvmLight.ViewModelBase
{
private PropertyChangedEventHandler propertyChangedEventHandler;
protected bool IsLoading { get; set; }
public bool RegisteredPropertyEventHandler { get; set; }
public const string ErrorMessagePropertyName = "ErrorMessage";
public string ErrorMessage { get; set; }
public string SuccessMessage { get; set; }
public void RegisterPropertyEventHandler(PropertyChangedEventHandler propertyChangedEventHandler)
{
this.propertyChangedEventHandler = propertyChangedEventHandler;
this.PropertyChanged += propertyChangedEventHandler;
this.RegisteredPropertyEventHandler = true;
}
public void UnegisterPropertyEventHandler()
{
if (this.RegisteredPropertyEventHandler)
{
this.PropertyChanged -= propertyChangedEventHandler;
this.RegisteredPropertyEventHandler = false;
this.propertyChangedEventHandler = null;
}
}
public void TearDown()
{
this.UnegisterPropertyEventHandler();
}
protected void NotifyError (string message)
{
this.ErrorMessage = message;
RaisePropertyChanged (() => ErrorMessage);
}
}
and a ViewModelLocator
public class ViewModelLocator
{
public const string ABCPageKey = "ABCPage";
public ABCViewModel ABC
{
get
{
return ServiceLocator.Current.GetInstance<ABCViewModel> ();
}
}
public ViewModelLocator ()
{
ServiceLocator.SetLocatorProvider (() => SimpleIoc.Default);
// Register all of the view models
SimpleIoc.Default.Register<ABCViewModel> ();
}
public static void Cleanup ()
{
}
public T GetViewModel<T> ()
{
return ServiceLocator.Current.GetInstance<T> ();
}
}
On the iOS side, I already had a BaseUIViewController, so I created a BaseViewModelUIViewController on top of it
public abstract partial class BaseViewModelUIViewController<T> : BaseUIViewController where T : ViewModelBase
{
public T ViewModel
{
get
{
return App.Locator.GetViewModel<T> ();
}
}
public BaseViewModelUIViewController (IntPtr handle) : base (handle)
{
}
internal virtual void ViewModelPropertyChangedHandler (object sender, PropertyChangedEventArgs e)
{
Console.WriteLine (string.Format ("****** Property Changed for {0} in {1}", e.PropertyName, this.GetType ().Name));
switch (e.PropertyName)
{
default:
break;
}
}
}
And then Android, similarly I already had a BaseFragment, so I created a BaseViewModelFragment on top of it
public class BaseViewModelFragment<T> : BaseFragment where T : ViewModelBase
{
public T ViewModel
{
get
{
return App.Locator.GetViewModel<T> ();
}
}
public BaseViewModelFragment (string title) : base (title)
{
}
internal virtual void ViewModelPropertyChangedHandler (object sender, PropertyChangedEventArgs e)
{
Console.WriteLine (string.Format ("****** Property Changed for {0} in {1}", e.PropertyName, this.GetType ().Name));
switch (e.PropertyName)
{
default:
break;
}
}
public override void OnDestroyView ()
{
this.ViewModel.TearDown ();
base.OnDestroyView ();
}
}
I hope it makes sense to other people looking for solutions.
Creating ViewModels: So naturally, for every new ViewModel created, I had to register it in the ViewModelLocator.
Using ViewModels: In terms of usage, you can simply use the ViewModel in the UI by inheriting from the ": BaseViewModelUIViewController" for iOS or from ": BaseViewModelFragment" for Android
Unfortunately you don't miss anything, all your claims are proper and you have properly listed various directions that you can take (and that you don't like).
Xamarin.Android and Xamarin.iOS are not made with data binding in mind, but rather with using the native interfaces, only Xamarin.Forms is made for the data binding. The capabilities of native platforms to use the data binding is limited (if it existed it would be incompatible among the platforms and you would have to make separate view models, and there is not data binding for iOS as of now anyway).
So basically there is no data binding in Xamarin.iOS and Xamarin.Android. It is completely up to you to abstract the shared business model and connect it with the user interface.
we have an mvc 5 application with individual user authentication we also have a xamarin forms application. we need to be able to use the same login details thats created on the web application when we log in via the xamarin application we are creating. we have successfully been able to create web api controllers using existing models in the web application and read/write the data in the xamarin application. but the problem is that we are not able to provide the same authentication we have(username and password with role assigned to the user) to the xamarin application. how can we make an api controller that reads from our existing database..please note our application is hosted on azure with a sql database.
basically we want to provide a login to our web application via the mobile app.
You need to take a look at Adrian Halls book - chapter 2 covers custom authentication which is what you need.
https://adrianhall.github.io/develop-mobile-apps-with-csharp-and-azure/chapter2/custom/
The key points are setting the mobile app to use authentication in the Azure portal but don't set any of the authentication providers (this makes it custom)
You then need to implement your own custom authentication controller to handle the authentication call back like this example taken from Adrian's book;
using System;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Security.Claims;
using System.Web.Http;
using Microsoft.Azure.Mobile.Server.Login;
using Newtonsoft.Json;
namespace AWPBackend.Controllers
{
[Route(".auth/login/custom")]
public class CustomAuthController : ApiController
{
private MobileServiceContext db;
private string signingKey, audience, issuer;
public CustomAuthController()
{
db = new MobileServiceContext();
signingKey = Environment.GetEnvironmentVariable("WEBSITE_AUTH_SIGNING_KEY");
var website = Environment.GetEnvironmentVariable("WEBSITE_HOSTNAME");
audience = $"https://{website}/";
issuer = $"https://{website}/";
}
[HttpPost]
public IHttpActionResult Post([FromBody] User body)
{
if (body == null || body.Username == null || body.Password == null ||
body.Username.Length == 0 || body.Password.Length == 0)
{
return BadRequest(); ;
}
if (!IsValidUser(body))
{
return Unauthorized();
}
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, body.Username)
};
JwtSecurityToken token = AppServiceLoginHandler.CreateToken(
claims, signingKey, audience, issuer, TimeSpan.FromDays(30));
return Ok(new LoginResult()
{
AuthenticationToken = token.RawData,
User = new LoginResultUser { UserId = body.Username }
});
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool IsValidUser(User user)
{
return db.Users.Count(u => u.Username.Equals(user.Username) && u.Password.Equals(user.Password)) > 0;
}
}
public class LoginResult
{
[JsonProperty(PropertyName = "authenticationToken")]
public string AuthenticationToken { get; set; }
[JsonProperty(PropertyName = "user")]
public LoginResultUser User { get; set; }
}
public class LoginResultUser
{
[JsonProperty(PropertyName = "userId")]
public string UserId { get; set; }
}
The actual custom authentication takes place in the IsValidUser function and should link to your existing internal authentication method (do not use the example here, this is for demonstration only)
Custom authentication has to use a client side flow which also meets your requirements.
I'm using a class test that checks login entity that is being done correctly, but an error occurs that does not seem to return the query in the database, but the application developed in ASP.NET MVC 3 Code First query returns data, I would to know what is wrong and what can be done to solve it.
Upon return of the query gives the following message in the variable:
"Enumeration yielded no results"
Test Method:
[TestMethod()]
public void efetuarLoginTest()
{
EntidadeRepository target = new EntidadeRepository();
string cnpj = "12345678";
string senha = "lalado";
Entidade expected = null; // TODO: Initialize to an appropriate value
Entidade actual;
actual = target.efetuarLogin(cnpj, senha);
Assert.AreNotEqual(expected, actual);
}
Method repository of the entity with the task of returning to the login query:
public Entidade efetuarLogin(string cnpj, string senha)
{
var consulta = from usu in bd.Entidades
where usu.cnpj == cnpj && usu.senha == senha
select usu;
if (consulta.Count() > 0)
{
Entidade e = new Entidade();
e.id_entidade = consulta.First().id_entidade;
e.razao_social = consulta.First().razao_social;
e.cnpj = consulta.First().cnpj;
e.senha = consulta.First().senha;
return e;
}
else
{
return null;
}
}
Class persistence database using the Entity Framework 4.1:
internal class BancoDados: DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<System.Data.Entity.ModelConfiguration.Conventions.PluralizingEntitySetNameConvention>();
modelBuilder.Conventions.Remove<System.Data.Entity.ModelConfiguration.Conventions.PluralizingTableNameConvention>();
base.OnModelCreating(modelBuilder);
}
public DbSet<Entidade> Entidades { get; set; }
public DbSet<Estado> Estados { get; set; }
public DbSet<Administrador> Administradores { get; set; }
public DbSet<Leilao> Leiloes { get; set; }
public DbSet<Lance> Lances { get; set; }
}
Thank You.
for us, this kind of an error was generated because of the right connection string not passed to the EF. If you are using NUnit, NUnit doesn't use your app.config or web.config, you would have to create your assembly.dll.config or nunit project.config. Please check NUnit documentation for usage of config files.
You can verify the connection string passed to NUnit, by examining, DbContext.Database and its properties in debug mode/
Checking your config values, should fix your problem.
I would imagine
if (consulta.Count() > 0)
is throwing the error?
You could change it to
if (consulta != null && consulta.Count() > 0)
Has anyone been able to make ADAM/Azman work with ASP.Net forms authentication. The default ADAM role provider works only with AD Domain users. And every single article I have read says that you need to write a custom role provider for it.
I have also found out bits and pieces of custom role provider code here and there, but nothing concrete. If someone can share the roleprovider needed for this, that will be great.
I have followed following articles so far :
Custom Role provider (doesn't work) - http://www.codeproject.com/KB/aspnet/active_directory_roles.aspx
Partial Custom Role provider code - http://blogs.msdn.com/b/azman/archive/2006/05/06/591230.aspx
Partial Custom Role provider code again - http://blog.avanadeadvisor.com/blogs/johanr/archive/2009/01/20/12373.aspx
MS Article steps to setup ADAM and use it with ASP.Net (windows auth)
Getting started with ADAM for authentication (no roles) - http://www.alexthissen.nl/blogs/main/archive/2007/07/26/getting-started-with-adam-and-asp-net-2-0.aspx
I have a hacked version, and I seriously mean hacked. I don't need to modify roles in my app, so I only implemented 2 methods. I had to send a username and password to query the directory. Someday I'd like to figure out how to use the ActiveDirectoryMembershipProvider's connection string, but I did not spend a lot of time with it, that would simplify things.
public class ActiveDirectoryFormsRoleProvider : RoleProvider
{
public string DomainController { get; set; }
public string ConnectionLDAPSuffix { get; set; }
public string ConnectionUserName { get; set; }
public string ConnectionPassword { get; set; }
public override string ApplicationName { get; set; }
public override bool IsUserInRole(string username, string roleName)
{
var roles = GetRolesForUser(username);
return roles.Contains(roleName);
}
public override string[] GetRolesForUser(string username)
{
var results = new List<string>();
using (var context = new PrincipalContext(ContextType.Domain, DomainController,ConnectionLDAPSuffix,ConnectionUserName,ConnectionPassword))
{
try
{
var p = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, username);
//looping twice because I was getting AppDomainUnloadedException on 50% of the first attempts
for (var i = 0; i < 2; i++)
{
try
{
var groups = p.GetAuthorizationGroups();
foreach (GroupPrincipal group in groups)
{
var name = group.SamAccountName;
if (!string.IsNullOrWhiteSpace(name))
results.Add(group.SamAccountName);
}
break;
}
catch (AppDomainUnloadedException)
{
}
}
}
catch (Exception ex)
{
throw new ProviderException("Unable to query Active Directory.", ex);
}
}
return results.ToArray();
}
...
For some reason on my production server, I have to make 2 attempts of GetAuthorizationGroups() because 50% of the time the first attempt failed by throwing AppDomainUnloadedException. You might be able to remove that for loop.
And here is my web.config element:
<roleManager enabled="true" defaultProvider="ActiveDirectoryFormsRoleProvider">
<providers>
<clear />
<add name="ActiveDirectoryFormsRoleProvider"
type="myapp.ActiveDirectoryFormsRoleProvider"
applicationName="myapp"
DomainController="domaincontroller.testdomain.corp"
ConnectionLDAPSuffix="DC=testdomain,DC=corp"
ConnectionUsername="username"
ConnectionPassword="password"
/>
</providers>
</roleManager>